鱼C论坛

 找回密码
 立即注册
查看: 2801|回复: 4

网易云热评爬虫

[复制链接]
发表于 2021-2-23 22:49:25 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
新人求助
小甲鱼老师的网易云爬虫代码爬取的是网易云音乐的热评,且网易云的API已经改变,参数不是固定,需要经过加密算法后产生参数(在搜索歌曲ID时),在爬取热门评论时的传递的参数是相同的,可以视为常量
但是现在想要爬取最新评论,但是问题在于最新评论不止一页
经过网上的搜索拼凑出来的代码如下
参考资料:https://blog.csdn.net/loveliveoil/article/details/107437265
  1. import requests
  2. import json
  3. import os
  4. import pprint
  5. from urllib import parse
  6. import base64
  7. from Crypto.Cipher import AES

  8. second_param = "010001"
  9. third_param = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
  10. forth_param = "0CoJUm6Qyw8W8jud"

  11. #======知乎大佬给出的搜索歌曲加密函数======#
  12. def pkcs7padding(text):
  13.     """
  14.     明文使用PKCS7填充
  15.     最终调用AES加密方法时,传入的是一个byte数组,要求是16的整数倍,因此需要对明文进行处理
  16.     :param text: 待加密内容(明文)
  17.     :return:
  18.     """
  19.     bs = AES.block_size  # 16
  20.     length = len(text)
  21.     bytes_length = len(bytes(text, encoding='utf-8'))
  22.     # tips:utf-8编码时,英文占1个byte,而中文占3个byte
  23.     padding_size = length if(bytes_length == length) else bytes_length
  24.     padding = bs - padding_size % bs
  25.     # tips:chr(padding)看与其它语言的约定,有的会使用'\0'
  26.     padding_text = chr(padding) * padding
  27.     return text + padding_text

  28. def aes_en(text,key,iv):
  29.     #print('pkcs7padding处理之前:',text)
  30.     text =pkcs7padding(text)
  31.     #print('pkcs7padding处理之后:',text)
  32.     #entext = text + ('\0' * add)
  33.     # 初始化加密器
  34.     aes = AES.new(key.encode(encoding='utf-8'), AES.MODE_CBC, iv)
  35.     enaes_text = str(base64.b64encode(aes.encrypt(str.encode(text))),encoding='utf-8')
  36.     return enaes_text

  37. def get_params(first_param):
  38.     iv = b"0102030405060708"
  39.     first_key = forth_param
  40.     second_key = 16 * 'F'
  41.    
  42.     h_encText = aes_en(first_param, first_key, iv)
  43.     h_encText = aes_en(h_encText, second_key, iv)
  44.     return h_encText
  45. #========================================#

  46. #=====最简单的bug级api,该api未被加密======#
  47. def get_comments_easiestAPI(music_name,music_place):
  48.     music_id = get_id(music_name,music_place)
  49.     base_url = 'http://music.163.com/api/v1/resource/comments/R_SO_4_' + str(music_id) + '?limit=20&offset='
  50.     # http://music.163.com/api/v1/resource/comments/R_SO_4_516997458?limit=20&offset=40
  51.     # limit和offset就是改变页数的参数,我在代码注释里面写的有,limit是每页获取的(最大)评论数,offset就是(评论页数-1) * 20
  52.     try:
  53.         os.remove('网易云' + music_name + '评论.txt')
  54.     except FileNotFoundError:
  55.         pass
  56.     headers = {'User-Agent':'firefox'}
  57.     res = requests.get(base_url,headers = headers)
  58.     comments_json = json.loads(res.text)     
  59.     save_comments(comments_json,music_name,comment_key = 'hotComments')
  60.     for i in range(1,100):
  61.         url = base_url + str(20*(i-1))
  62.         res = requests.get(url,headers = headers)
  63.         comments_json = json.loads(res.text)
  64.         save_comments(comments_json,music_name,comment_key = 'comments')
  65.     print('爬取完毕!')
  66. #========================================#

  67. #==============抓取歌曲id函数=============#
  68. def get_id(music_name,music_place):
  69.     url = 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token='
  70.     headers = {'User-agent':'firefox'}
  71.     search_name = music_name
  72.     page = "0"     #必须是30的整数倍,因为下面first_param参数中的limit值为30,意思是每页最多显示30个评论
  73.     first_param = "{"hlpretag":"<span class=\\"s-fc7\\">","hlposttag":"</span>","s":"%s","type":"1","offset":"%s","total":"%s","limit":"30","csrf_token":""}" %(search_name,page,True)
  74.     params = get_params(first_param)
  75.     data = {'params':params,
  76.             'encSecKey':'257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c'
  77.         }
  78.     res = requests.post(url,headers = headers,data = data)
  79.     music = json.loads(res.text)
  80.     music_id = music['result']['songs'][music_place-1]['id']
  81.     print('歌曲id:', music_id)
  82.     return music_id
  83. #=========================================#

  84. #=============保存评论函数================#
  85. def save_comments(comment_json,music_name,comment_key):
  86.     with open('网易云' + music_name + '评论.txt','a',encoding = 'utf-8') as f:
  87.         if(comment_key == 'hotComments'):
  88.             f.write('热门评论\n')
  89.         if(comment_key == 'comments'):
  90.             f.write('\n\n\n最新评论\n')
  91.             state = 0
  92.         for each in comment_json[comment_key]:
  93.             f.write(each['user']['nickname'] + ' :')
  94.             f.write(each['content'])
  95.             f.write('\n')
  96. #========================================#   

  97. #====使用还可以用的旧api,同样被加密过======#
  98. #问题:无法爬取全部最新评论
  99. def get_comments_oldAPI(music_name,music_place):
  100.     music_id = get_id(music_name, music_place)
  101.     try:
  102.         os.remove('网易云' + music_name + '评论.txt')
  103.     except FileNotFoundError:
  104.         pass
  105.     first_param = '{rid:"", offset:"0", total:"true", limit:"20", csrf_token:""}'
  106.     #offset就是(评论页数-1) * 20,total在第一页是true,其余是false
  107.     headers = {'user-agent':'Firefox'}
  108.     params = get_params(first_param)
  109.     encSecKey = '257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c'
  110.     data = {'params' : params,'encSecKey' : encSecKey}
  111.     url = 'http://music.163.com/weapi/v1/resource/comments/R_SO_4_' + str(music_id) + '/?csrf_token='
  112.     res = requests.post(url,headers = headers,data = data)
  113.     comments_json = json.loads(res.text)     
  114.     save_comments(comments_json,music_name,comment_key = 'hotComments')
  115.     for i in range(1,10):
  116.         offset = (i-1)*20
  117.         first_param = "{rid:"", offset:"%s", total:"false", limit:"20", csrf_token:""}" % offset
  118.         print(first_param)
  119.         params = get_params(first_param)
  120.         res = requests.post(url,headers = headers,data = data)
  121.         comments_json = json.loads(res.text)
  122.         save_comments(comments_json,music_name,comment_key = 'comments')
  123.     print('评论下载成功')

  124. #===========最新版API============#
  125. #url = 'https://music.163.com/weapi/comment/resource/comments/get?csrf_token='
  126. #暂时没有人研究,好像非常不好用
  127. #===============================#
  128. #==========下载网页用的函数==============#
  129. def get_webpage(url):
  130.     headers = {'user-agent':'firefox'}
  131.     res = requests.get(url,
  132.                        headers = headers)
  133.     with open('Page.txt','w',encoding = 'utf-8') as f:
  134.         f.write(res.text)
  135. #=======================================#

  136. def main():
  137.     # url = 'https://music.163.com/#/song?id=4466775'
  138.     # get_webpage(url)
  139.     #music_name = 'lostrivers'
  140.     # get_id(music_name)
  141.     music_name = input('请输入搜索名称:')
  142.     music_place = int(input('请输入第几首歌:'))
  143.     # get_comments_easiestAPI(music_name,music_place) #最容易的API
  144.     get_comments_oldAPI(music_name,music_place) #旧版API
  145.    
  146.    
  147. if __name__ == '__main__':
  148.     main()
复制代码

使用该api(小甲鱼老师的API)进行爬取最新评论时出现了结果,但只有第一页的新评,然而按照代码注释给出的方法无法翻页,应该怎样才能改变参数,进行翻页?
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-2-24 15:04:24 | 显示全部楼层
网易云的翻页是跟params和encSecKey这两个参数关联的。
要翻页,这两个参数要变化,也就是说params和encSecKey都不是固定参数,是动态的,需要生成,而你代码中参数是固定的。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-24 21:16:04 | 显示全部楼层
YunGuo 发表于 2021-2-24 15:04
网易云的翻页是跟params和encSecKey这两个参数关联的。
要翻页,这两个参数要变化,也就是说params和encSe ...

不是的,代码中参考了网上的参数解密方法,是根据给定信息的字符串生成的,每一页都不同,但是按照代码作者注释中改变字符串参数,结果却没有发生变化
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-24 21:16:35 | 显示全部楼层
不知正确的方法应该是如何
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-2-25 14:59:26 | 显示全部楼层
2079501562 发表于 2021-2-24 21:16
不知正确的方法应该是如何

你在网上找的其他的代码我不知道是怎么做的,但我当时是分析过网易云的加密的,也成功了。
我说简单点吧。params参数是翻页的关键参数,这个参数加密的原数据中就包含翻页的参数,你的params加密前的数据并不包含翻页参数(我看你代码是想用offset参数进行翻页,其实翻页是有一个pageNo参数的)。

这个是我去年写的代码,放在github上,刚刚试了还可以运行,你可以参考看看。
https://github.com/YungGuo08/WebSpider/tree/master/Music_Download/Music_163
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-27 09:40

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表