Intro
前一阵子写了个爬取网易云音乐评论的python程序
但是只是完成了一个开端,最近抽空稍微完善了一下
先看一下整体流程
- 获取要爬取的歌手的ID
- 通过ID获取这个歌手的所有专辑ID
- 通过专辑获取歌手的所有歌曲
- 歌曲间一个一个的通过爬虫进行爬取
Problem
这里面存在几个问题
- 怎么判断一个歌曲或者一张专辑已经爬过了
- 怎么保证爬取的效率
- 怎样防止网易的反爬虫机制
Solution1
目前只解决了第一个问题,我的方法是先生成一个Task表,将任务以及任务的进度存储在Task表中,每次重新运行的时候先从Task表中读取进度,然后再进行爬取操作。
TaskSchedule.json
[{
"id": "35520072",
"isCrawler": 1,
"musicId": "2116",
"name": "\u653e & \u62ab\u98ce",
"songs": [{
"album": "35520072",
"isCrawler": 1,
"id": "478731242",
"name": "\u653e",
"offset": "-1"
}, {
"album": "35520072",
"isCrawler": 0,
"id": "478736172",
"name": "\u62ab\u98ce",
"offset": "530"
}]
}, {...}]
如上形成json格式的一个文件,将专辑ID和是否爬取存入,offset代表这首歌爬取到了第几个评论,每次爬取完通知爬取者取更新这张表(通过调用trigger方法)
def trigger(self,musicId,offset):
isOk = False
albumList = self.load()
for album in albumList:
if isOk:
break
if album["isCrawler"] == 1:
continue
for song in album["songs"]:
if song["id"] == musicId:
if offset == -1:
song["isCrawler"] = 1
for song in album["songs"]:
isCrawler = 1
if song["isCrawler"] == 0:
isCrawler = 0
break
album["isCrawler"] = isCrawler
else:
song["offset"] = offset
isOk = True
logger.critical("%s has fininsh with %s"%(str(musicId),str(offset)))
break
self.store(albumList)
Solution2
第二个问题可以用多线程去解决,虽然Python本质上没有多线程,但是在IO密集型操作中,类似爬虫发送http请求的操作中存在大量IO操作,因此多线程还是可以几何倍增加爬取速度的。
方法就是将要爬取得内容放进队列中,然后多线程利用线程池进行出队操作选择要爬取的对象。这一部分待完工。
Solution3
第三个问题存在于爬取的测试过程中,在爬取中不一会就出现503拒绝访问的错误。这是网易云音乐对API的保护,我们只能利用爬虫代理进行深一步操作,待完工。
最后附一个爬取时候的截图
Github: https://github.com/WJerry0227/MusicComment163
持续更新中。。。