记录于:2020年12月03日
用了N年的手机在经历N次掉落之后终于扛不住了,后背都张嘴了,估计再摔一次电池都能飞出来。
换了手机,由于之前有听喜马拉雅的习惯,但是手机里自带有播客软件,强迫症逼着我不能下载喜马拉雅app。
找了几天没发现喜马拉雅提供的有RSS订阅(后来想了一下,别人怎么可能提供这个功能,O(∩_∩)O哈哈~),网上也没有相关服务。
苦啊,后来还是下载了喜马拉雅app,但是实在受不了,就索性自己捣鼓一个轮子。
诉求很简单,就是想将喜马拉雅的节目搬到播客软件,用原生的app听第三方的数据,这个需求好恶心啊,还好不是产品经理提的。
好吧,开始吧。
其实写爬虫,重要的不是代码实现,而是刚开始对需要爬取的数据的分析,分析怎么爬取,怎么得到自己的数据,只要这个流程明白了。代码实现就很简单了。
在这之前需要知道什么是RSS
分析
浏览器打开喜马拉雅,找到想听的节目,比如:郭德纲
这样就有了爬取项目啦,对着这个页面开始分析,我需要标题,作者,图片三个元素,打开浏览器F12,找到这三个元素的定位,这样只需要相应的代码就能抓取信息了,这些信息就足够生成RSS中的 元素啦。
重要的是 元素,播客播的就是这个元素中的信息。
其实就是要拿到页面上的 【播放列表】,还是F12找到 【播放列表】的定位,有了定位,就可以抓取出这个列表,并获取这个列表中每个元素的链接,通过此链接就可以进去详情页。
点开详情页,离实现越来越近了。
我需要标题,描述,及播放源这三个元素来构成 元素。
标题和描述很好获取,还是老套路F12定位就可以了,播放源就需要观察了,打开F12,观察详情页有哪些请求,看是否有某些请求得到声音源数据,
通过发现: 这个请求,会响应数据播放数据
这就能拿到播放数据啦。这样一来,第一页的所有播放数据都能拿到了。
由于当前是列表页,所以少不了分页,我们只需要找出当前页面是否存在下一页,且找到下一页的链接,发起请求然后重复步骤,这样就能拿到整个列表页。
有了上面的一通分析,就知道了如何去编写代码实现这个功能啦。
编码
按照上面的流程,进行编码
1.构建Channel对象
2.构建Item对象
3.生成RSS(在同级目录下会生成一个xml文件)
import requests
from bs4 import BeautifulSoup
import datetime
##################################
##### 公用对象,存储/生成 ######
##################################
# rss channel
class channel(object):
def init(self, title, author, image):
self.title = title
self.author = author
self.image = image
# rss item
class item(object):
def init(self, title, pubDate, description,enclosure):
self.title = title
self.pubDate = pubDate
self.description = description
self.enclosure = enclosure
##################################
##### 爬取数据,存储 ######
##################################
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36'
}
# 开始页 - 郭德纲21年相声精选
mainUrl = ""
# 播放地址
playV1 = "/revision/play/v1/audio?id={}&ptype=1"
# gmt时间格式化
GMTFORMAT = '%a, %d %b %Y %H:%M:%S GMT'
# 网址
ximalaya = mainUrl【:mainUrl.index('/',8)】
# 所有播放项
items = 【】
# 构建Channel对象
def getChannel():
r = requests.get(mainUrl,//代码效果参考:http://www.zidongmutanji.com/bxxx/134036.html
headers=headers)soup = BeautifulSoup(r.text, 'html.parser')
title = soup.find('h1', attrs={'class': 'title vA'}).text
author = soup.find('a',attrs={'class':'nick-name gK'}).text
image = "http:" + soup.find('img', attrs={'class': 'img vA'})【'src'】.split('!')【0】
return channel(title, author, image)
# 构建Item对象
def getItem(listPageUrl):
print('======> 正在爬取列表页',listPageUrl)
r = requests.get(listPageUrl, headers=headers)
soup = BeautifulSoup(r.text, 'html.parser')
# 获取所有播放列表项详情
soundList = soup.findall('div', attrs={'class': 'text lF'})
for sound in soundList:
getDetails(ximalaya + sound.a【'href'】)
# 进入下一页
pageNext = soup.find('li', attrs={'class': 'page-next page-item WJ_'})
if pageNext:
getItem(ximalaya + pageNext.a【'href'】)
# 进入详情页
def getDetails(detailPageUrl):
print("======> 正在爬取详情页",detailPageUrl)
r = requests.get(detailPageUrl, headers=headers)
soup = BeautifulSoup(r.text, 'html.parser')
# 标题
title = soup.find('h1', attrs={'class': 'title-wrapper _uv'}).text
# 发布时间
pubDate = soup.find('span', attrs={'class': 'time _uv'}).text
# 声音简介
description = ""
if soup.find('article'):
description = soup.find('article').text
# 播放源
playUrl = ximalaya + playV1.format(detailPageUrl.split('/')【-1】);
r = requests.get(playUrl, headers=headers)
enclosure = r.json()【'data'】【'src'】
items.append( item(title,datetime.datetime.strptime(pubDate, '%Y-%m-%d %H:%M:%S').strftime(GMT_FORMAT),description,enclosure) )
##################################
##### 生成RSS ######
##################################
def createRSS(channel):
rss_text = r' ' \
r' ' \
r' {}' \
r' {}' \
r' ' \
.format(channel.title, channel.author, channel.image)
for item in items:
rss_text += r' ' \
r' {}' \
r' ' \
r' ' \
r' '\
.format(item.title, item.description, item.enclosure)
rss_text += r' '
print('======> 生成RSS')
print(rss_text)
#写入文件
with open(mainUrl.split('/')【-2】+'.xml', 'w' ,encoding='utf-8') as f:
f.write(rss_text)
if name=="main":
channel = getChannel()
getItem(mainUrl)
createRSS(channel)
将生成后的xml放到服务器,就可以尽情享用了。
成果
易中天老师讲的真的好
后续
本文编写于2020年12月3日,后续官方可能会对页面进行更改,请求进行更改等,会导致以上爬虫失效,所以需要知道如何进行分析,才能知道如何爬取。
以上代码只作为学习探讨,请问恶意使用!