爬虫练习:爬取网易云音乐热歌榜全部歌曲的热门评论
目标:获取网易云音乐热歌榜单中的完整曲目列表(共计200首曲目),并从每首都抽取其15条精选评论
分析:
需要采取分阶段的方式进行处理。第一阶段是首先定位热歌榜单所在的资源库,并确定这些音乐作品的具体分类与标签;随后通过系统查询功能获取这些音乐作品的具体ID号码。在第一阶段的基础上第二阶段则是针对每个音乐作品ID号码对应的音乐作品所在的具体位置或渠道来源进行深入分析与研究,并通过相应的爬虫技术手段获取这些特定位置或渠道中的相关数据信息。具体来说:首先利用搜索引擎获取目标用户的搜索记录数据集;其次对搜索结果数据集进行清洗整理并去除重复项与噪音数据;最后通过机器学习算法对清洗后的数据集进行分类分析以识别出目标用户的个性化偏好特征
为了获取热歌排行榜的数据存储位置, 我们可以通过浏览器开发者工具分析网页代码, 进而确定了热歌排行榜的数据请求路径为:
http://music.163.com/discover/toplist?id=3778678
该URL采用GET方法进行请求,并返回一个包含HTML格式的doc文件。在该HTML文件中,第610行记录了每首歌曲的信息包括标题、ID以及对应的播放资源链接。
因此我们首要任务就是解析该 HTML 文档以获取其歌曲标题和歌单编号。在获得这些信息后我们就可以基于这些歌单编号进一步查询每首歌曲的具体内容。
在网页上点击某首歌曲的链接后,在其跳出页面分别研究每个单独新页面的网络行为特征时,则能够推断出包含该歌曲评论的信息。
http://music.163.com/weapi/v1/resource/comments/R_SO_4_489998494?csrf_token=,
采用的是POST请求方式(由于评论内容需要传输至服务端进行处理),返回的形式是JSON格式的数据文件,在该JSON文件中包含了我们所需的热门评论信息。最棘手的问题在于每次发送POST请求都需要指定两个必要的参数(然而,在实际应用中发现不同歌曲对应的这两个参数并不一致(尽管程序设计时我们假设它们相同)),至于如何配置这两个参数的具体方法,请参见下面提供的链接。
下一步任务就是要通过POST请求从该JSON文件中获取数据,并解析其中的热门评论内容。
对于下面代码的具体实现细节,请参考<>
#获取网易云热歌榜所有歌曲的名称和id
def get_all_hotSong():
url = "http://music.163.com/discover/toplist?id=3778678" #网易云音乐热歌榜url
ua_headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) \
AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/60.0.3112.101 \
Safari/537.36'} #请求头部
request = urllib.request.Request(url=url, headers=ua_headers) #构造请求对象
html = urllib.request.urlopen(request).read().decode('utf8') #打开url
html = str(html) #转换为str
#我们要筛选的html字段例如:<ul class="f-hide"><li><a href="/song?id=574566207">说散就散</a></li></ul>
part1 = r'<ul class="f-hide"><li><a href="/song\?id=\d*?">.*</a></li></ul>'#进行第一次筛选的正则表达式,注意在这里有问号(本身是正则表达式的元字符)前要使用转义符\
result = re.compile(part1).findall(html) #用正则表达式进行筛选,返回的结果存在列表中
result = result[0] #获取list的第一个元素
part2 = r'<li><a href="/song\?id=\d*?">(.*?)</a></li>' #进行歌名筛选的正则表达式
part3 = r'<li><a href="/song\?id=(\d*?)">.*?</a></li>' #进行歌id筛选的正则表达式
hot_song_name = re.compile(part2).findall(result) #获取所有热门歌曲名称
hot_song_id = re.compile(part3).findall(result) #获取所有热门歌曲id
return hot_song_name, hot_song_id #返回的是两个list
#参数是歌曲名和id(由上一个函数返回),返回的是每首歌下的15条热评
def get_hotComments(hot_song_name, hot_song_id):
#每首歌使用单独的url请求
url = 'https://music.163.com/weapi/v1/resource/comments/R_SO_4_' + hot_song_id + '?csrf_token='
ua_headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) \
AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/60.0.3112.101 \
Safari/537.36'} #请求头部
#post请求表单数据,以字典形式组织参数
data = {'params':'Ms46IEnUeE4P/yfU6xsyEtEcjeRfhckhg/sBdqIG2tLBXGPoZ7MGVOZsu2AdyLQ+ZTPDvjf1OjalqcSSJLfBmf1vvbVx8cvv8MVoHYOQ6wCRnd2/6+GMfu1M13fbNXVb9aKiduqxPZjZrMsTgX5jecxsXs/rzP9KLRWDDMkvxusGini/WGaWDKGrBQj4cm4k', \
'encSecKey':'b049e6104264186dfaceba5f9a60dc39e8cf6900dac42a58223074346b758a4ca0fc66006a39a8d54e43cc95c271b415b69efa8a81742f06230a105e32a6f11128e8d526450a81f3245ce0fb5f5974de18207e6e3cdaf87b63572fcb7ad720124b19049457fe19c8f7f3424c3eae45d530b743ee7dec5ef78d15c2dbbc258975'}
postdata = urllib.parse.urlencode(data).encode('utf8') #将参数进行编码
request = urllib.request.Request(url, headers=ua_headers, data=postdata) #构造请求对象
response = urllib.request.urlopen(request).read().decode('utf8') #打开url,返回类文件对象并读其内容
json_dict = json.loads(response) #获取json
hot_comments = json_dict['hotComments'] #获取json中的热门评论
num = 0
fhandle = open('./song_comments.txt','a', encoding='utf-8') #按appendix的方式打开文件
fhandle.write(hot_song_name+':'+'\n')
for item in hot_comments: #按循环依次写入歌的每条评论
num += 1
fhandle.write(str(num)+'.'+item['content']+'\n')
fhandle.write('\n==============================\n\n')
fhandle.close()
#==============================主程序入口=======================================
#返回网易云热歌榜所有歌曲的名称和id
hot_song_name, hot_song_id = get_all_hotSong()
num = 0
while num < len(hot_song_name):
print('正在抓取第%d首歌曲热评...'%(num+1))
get_hotComments(hot_song_name[num], hot_song_id[num])
print('第%d首歌曲热评抓取成功'%(num+1))
num += 1
