【Python/爬虫】爬取网易云音乐评论区热评(练习)

【Python/爬虫】爬取网易云音乐评论区热评(练习)


前言

之前在b站看网课学习爬虫,正好老师讲了一个案例,爬取PC端网易云音乐一首歌的热评,觉得很有趣,就记录下来。
接下来,将以许嵩的《雅俗共赏》评论区为例。

一、分析网页需求

首先,打开《雅俗共赏》网页,看一眼评论区有啥内容,再右键点开“查看页面源代码”,看看能否直接在源码中找到评论区的内容。

我这边就不放图片了,找一下可以发现,页面源代码中并没有评论区的内容,再右键“检查”,打开开发者工具的“元素”栏,可以找到评论区的内容。
(小技巧,点击开发者工具左上角的小箭头,再点击网页上内容,就可以确定网页内容在代码中的位置)
在这里插入图片描述
这点说明,评论区的内容是通过js等手段和前端静态代码整合在一起的,我们需要通过抓包,来找到评论区的内容。

打开网络栏,选取“Fetch/XHR”限制,刷新网页,可以得到很多包,查看他们的预览,一个包一个包往下找,可在“get?”包里面找到我们想要找的热评。
在这里插入图片描述
点开表头分析,发现是POST请求,就要找它的表单数据,点开Payload,发现,表单数据中的两个参数的值是很奇怪的乱码。
在这里插入图片描述
在这里插入图片描述
这就意味着,网易对表单数据的原值进行了加密,而我们要做的事有两件,找到表单数据未加密数据params和encSecKey的原值,并模拟网易对原值的加密过程去请求网页。

二、找到原值和加密函数

点开“发起程序”可以请求调用堆栈,从下往上是浏览器请求数据的前后顺序。
我们可以知道,一开始的请求是用表单数据的原值,在某个请求后,被加密。

在这里插入图片描述
点开最上面那个请求,通过添加断点刷新网页,就可以得到请求的数据,在右边的“作用域”中,找到request中的data以及url,确定你请求的数据是来自于我们想要的(get?)url。如果url中没有get,通过蓝色的小三角刷新即可。找到(get?)url的请求后,可以通过调用堆栈往下找,对比data的值,以及其他数据,寻找疑似未加密的原值。
在这里插入图片描述
直接快进。
在其中一个请求中,而且我们能看到,data的数据换成了比较易于理解的数据,看起来像rid=参数,threadId=参数等的拼接起来的,且在该请求中发现i6c下面有非常疑似表单数据的原值,且能和data对应上(i6c数据拼接起来就是data的数据)。

上一个查看的请求中的data还是乱码,这个请求中就有,我们怀疑上一个查看的请求中含有加密函数。
在这里插入图片描述
点开上一个请求“u6o”我们能发现以下数据,bUM2x中有两个参数encSecKey和encText,data还是乱码,但是data的开头和encText开头有点像,且i6c的数据也在。我们将分析左边的代码,确定i6c中的数据是否是原值,且找到加密函数。
在这里插入图片描述

在蓝条上面的一段函数中分析,我们可以清晰到这么两条代码。
一个是bUM2x = window.asrsea(),中有调用了i6c的值,下面那条就是data,以及我们要找的两个参数:
params 对应bUM2x.encText 和 encSecKey对应bUM2x.encSecKey
我们就可以断定,window.asrsea()就是我们要找的加密函数,而i6c中的值就是我们要找的原始数据。
在这里插入图片描述

三、分析加密函数的运作方式

现将原始数据贴到pycharm里面等待

data = {
    "csrf_token": "",
    "cursor": "-1",
    "offset": "0",
    "orderType": "1",
    "pageNo": "1",
    "pageSize": "20",
    "rid": "R_SO_4_411214279",
    "threadId": "R_SO_4_411214279"
}
## params: bUM2x.encText,
## encSecKey: bUM2x.encSecKey

接下来就来找,window.asrsea()是如何定义的。
在开发者工具的搜索栏里面搜索window.asrsea。
在这里插入图片描述
可以看到,只有两条,一条是上面画出来,一条就是我们要找的,点击window.asrsea=d那条跳转到详细的信息。

我们可以看到,d也是一个函数,d函数和函数a,c,d都有关系,所以四个函数需要一起分析。
在这里插入图片描述

首先看function dwindow.asrsea
在这里插入图片描述
我们可以看到,window.asrsea也是d函数的执行方式,window.asrsea的四个参数对应d中的d,e,f,g的四个形参。
则,
d = JSON.stringify(i6c),即就是data原数据的json格式,d=data
e = bsG7z(…),一个很奇怪的东西,但是我们可以在控制台内运行这句话,可以发现,是个定值,所以可以确定,e='010001'
在这里插入图片描述

后面的f和g都可以在控制台中运行,且也是定值:
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
g = '0CoJUm6Qyw8W8jud'
在这里插入图片描述

可以在代码中写:

data = {
    "csrf_token": "",
    "cursor": "-1",
    "offset": "0",
    "orderType": "1",
    "pageNo": "1",
    "pageSize": "20",
    "rid": "R_SO_4_411214279",
    "threadId": "R_SO_4_411214279"
}
## params: bUM2x.encText,
## encSecKey: bUM2x.encSecKey

d= data
e ="010001"
f ="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud"
i = "nVMEz65CNmypYp1Q"

(其实这些奇怪的汉字在源代码中是有定义的,有兴趣的可以翻一下)
在这里插入图片描述
继续来看d函数
在这里插入图片描述
定义了一个h字典,最后输出的就是h两个参数,由之前的代码可知encText和encSecKey正好和我们想要的params和encSeckey分别对应。
又定义了一个i,i的值和a函数有关,那我们来看一下a函数:
在这里插入图片描述
函数a很复杂,读一读,又是定义d,e,b的,最后可以由random得出,输出的是一个16位的随机值,这个就很难搞,而且后面的encText和encSecKey的值也和i有关系,看起来好像没有路可以走了。

我们再仔细看一下后面的encText和encSecKey输出代码。
在这里插入图片描述
encText有一层嵌套,和b函数有关,有点麻烦。
encSecKey,和c函数有关,而且c里面的参数,i是随机值,e和f都是定值,且和我们的表单原数据data无关,看起来是个切入点,来分析一下c函数,看看有没有什么发现:
在这里插入图片描述
c函数里面,定义了d和e,d用了RSA加密,e也是一个函数的处理,但是,如果你在搜索栏里面搜索一下,找一下函数的定义,你会发现,都不含有random,输入是定值,那么c函数输出的也是定值。

也就是说,对于encSecKey的输出,i是随机值,e和f都是定值,最后的输出由i的值来定,只要i确定了,encSecKey就可以确定。

1.获取encSecKey的函数

那么,i该怎么办呢。
我们可以通过在浏览器的代码中添加断点,来截断数据。
将i 定死之后,因为encSecKey和我们的表单数据没有关系,并不需要详细的加密过程,只要我们在相应i 下得到的encSecKey,就是我们想要的。

操作如下:

  1. 由于i=a(16)之后就等到了一个16位的i,我们在这句代码之后添加断点,再点击蓝色小三角输出,得到一个i="CMnnifoCstAlW3C2"。尤其要注意的是,因为i 是随机得到的,你每一次刷新得到的i都不同,这个是分析的时候需要注意的。

在这里插入图片描述
2. 然后返回window.asrsea()函数,给两句话都添加断点,然后点击蓝色小三角运行,就能得到i="CMnnifoCstAlW3C2"时的encSecKey

encSecKey="b7c430347f9e9820ac1c4b680edd0587382fedd567e01fa09c85943592bf5e82bfa2b892351b22d9af439bcd8b2e6aff0ef80ffa61d39ed2816627c12099969efa36df7520b3b8ffa5cdd5ff5d3fb275d7a72e4f976503e105b7c973e65870db45ba30e77ccf3321e775c8584269c6eb380e6011aaf2f6c86f7eb4fdd7d1a651"
在这里插入图片描述

补充代码块,并编写获得encSecKey的函数,因为和表单数据data无关,我们只需要简单直接返回即可

data = {
    "csrf_token": "",
    "cursor": "-1",
    "offset": "0",
    "orderType": "1",
    "pageNo": "1",
    "pageSize": "20",
    "rid": "R_SO_4_411214279",
    "threadId": "R_SO_4_411214279"
}
## params: bUM2x.encText,
## encSecKey: bUM2x.encSecKey

d= data
e ="010001"
f ="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud"
i = "CMnnifoCstAlW3C2"

def Get_encSecKey( ):##获得encSecKey参数
	return "b7c430347f9e9820ac1c4b680edd0587382fedd567e01fa09c85943592bf5e82bfa2b892351b22d9af439bcd8b2e6aff0ef80ffa61d39ed2816627c12099969efa36df7520b3b8ffa5cdd5ff5d3fb275d7a72e4f976503e105b7c973e65870db45ba30e77ccf3321e775c8584269c6eb380e6011aaf2f6c86f7eb4fdd7d1a651"
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2.获取encText的函数

相比较另一个参数来说,这个参数的获取就很困难了。
在这里插入图片描述
encText的输出经历了两层b函数。
第一层b函数的参数,d从上面可知,是表单数据data,g是一个定值,我们可以猜测,或许是data作为明文,而g作为密钥加密。
第二层是上一次加密的结果作为明文,而i 这个原本的随机数作为密钥来加密。
现在的好消息是,随机数i 我们找到了一个定值,四个参数都是定值了,我们来分析b函数即可。
在这里插入图片描述
解析b函数:

  1. 首先传进来参数a和b,根据上面的猜测,a是明文,b是密钥
  2. 第一句,b经过utf-8编码赋值给局部变量c
  3. 第二句,一串定值的字符串经过utf-8编码赋值给局部变量d
  4. 第四句,a经过utf-8编码赋值给局部变量e
  5. 第五句,重点来了,通过代码,我们可以知道,这是一个AES的加密方式,也就是说,我们之后需要写一个AES的加密方式来加密我们的明文。函数内定义了一个iv,也就是偏移量,是AES加密方式中必须的一个参数,mode也就模式,加密模式有四种,这里采用CBC的加密模式,这里如果对密码学比较熟悉的人应该会很快理解。
  6. 最后,返回一个字符串类型的值。

解析完毕,动手写我们的代码:

首先是,加密过程,我们需要AES的加密,Python有专门的库Pycrpto,需要提前pip安装一下,有些人安装可能会有问题,这个百度自行解决。

AES的加密网上有很多解释,大概注意的点就是:

  1. 我们要现将变量的值经过“utf-8”编码,这是模拟网易加密的过程,然后AES要将明文补充成16的倍数,所以我们需要对明文a进行处理一下(见to_16函数)
  2. 输出时要用base64编码一下,也是AES的加密的要求。
  3. 最后别忘了转成字符串类型(网易模拟需求)
  4. AES编码很容易出现一些报错,如果出现报错,善用搜索引擎解决。
  5. 再写一个获取encText的函数,需要两层加密。

代码如下:

from Crypto.Cipher import AES
from base64 import b64encode

def Get_encText(data):
    first = enText(data, g) #第一次加密
    second  = enText(first,i) #第二次加密
    return second #返回的值即是我们想要的encText

def to_16(a): #对a进行16位倍数的补充
    size = 16 - len(a) % 16
    return a+size*chr(size)

def enText(a,b):
    iv = "0102030405060708" #定义一个偏移量
    a = to_16(a) 
    #AES编码
    aes  = AES.new(key=b.encode("utf-8"),mode= AES.MODE_CBC, iv=iv.encode("utf-8")) ##这两步就是aes加密过程
    bs  = aes.encrypt(a.encode('utf-8'))
    return  str(b64encode(bs),"utf-8")

你可以打印输出一下,保证代码无误

四、请求页面和结果

接下来就是书写经典的爬虫请求页面代码了。

从开发者工具的页面表头中找到url,并写请求代码,这里面要注意的是,它是“POST”请求,需要对表单数据处理成对应的形式。不知道大家还记不记得,在windou.asrsea()的加密函数,所需要的是表单数据的json格式,所以写的时候,要先将data转为json格式。
在这里插入图片描述
代码:

import requests
import json
url = "https://music.163.com/weapi/comment/resource/comments/get?csrf_token="

resp=requests.request(url=url,method="POST", data={
    "params": Get_encText(json.dumps(data)), 
    "encSecKey": Get_encSecKey()
})

最后,将获取的页面转为json格式,将其中的hotcomments提取出来写入一个文件,最后别忘了关闭请求和文件。


str = resp.content.decode()
dic_json = json.loads(str) 

hotComments= dic_json["data"]["hotComments"]

with open("hotcomments.txt","w",encoding='utf-8') as file:
    for i in hotComments:
        file.writelines(i["content"]+"\n")
    print("over!")

resp.close()
f.close()

最终得到结果:
在这里插入图片描述

代码如下(示例):

五、完整代码

这边将放出完整代码,以供参考。

from Crypto.Cipher import AES
from base64 import b64encode
import requests
import json


data = {
    "csrf_token": "",
    "cursor": "-1",
    "offset": "0",
    "orderType": "1",
    "pageNo": "1",
    "pageSize": "20",
    "rid": "R_SO_4_411214279",
    "threadId": "R_SO_4_411214279"
}
## params: bUM2x.encText,
## encSecKey: bUM2x.encSecKey

d= data
e ="010001"
f ="00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g = "0CoJUm6Qyw8W8jud"
i = "nVMEz65CNmypYp1Q"

def Get_encSecKey( ):##
    return "51458895f9a066a4d75661d66b61598f9f6eff6c29f936696b706869ea7cc22ed8dc37281a521f02849ac6c199ab0473e865b3fa22627485135883277b02aefa5d3d299e888342ba817a0080a5346f9026ea6be46dd4318cb3c46c0f0077f819a66a04954466286878ac34fbd139b5a1a2f0db4ca16bb4b61173c55899e89605"

def Get_encText(data):
    first = enText(data, g)
    second  = enText(first,i)
    return second

def to_16(a):
    size = 16 - len(a) % 16
    return a+size*chr(size)

def enText(a,b):
    iv = "0102030405060708"
    a = to_16(a)
    aes  = AES.new(key=b.encode("utf-8"),mode= AES.MODE_CBC, iv=iv.encode("utf-8")) ##这两步就是aes加密过程
    bs  = aes.encrypt(a.encode('utf-8'))
    return  str(b64encode(bs),"utf-8")


url = "https://music.163.com/weapi/comment/resource/comments/get?csrf_token="

resp=requests.request(url=url,method="POST", data={
    "params": Get_encText(json.dumps(data)),
    "encSecKey": Get_encSecKey()
})


str = resp.content.decode()
dic_json = json.loads(str)

hotComments= dic_json["data"]["hotComments"]

with open("hotcomments.txt","w",encoding='utf-8') as file:
    for i in hotComments:
        file.writelines(i["content"]+"\n")
    print("over!")

resp.close()
file.close()

总结

解析网易的页面代码还是有一定难度的,尤其是和别的网站相比起来,有时还需要一点密码学知识。
顺带一提,这里只获取了一个页面的评论,而网易云评论区采用了局部刷新,可以通过线程池一次性获取多个页面的评论,有兴趣可以研究一下,这里不过多赘述。

  • 29
    点赞
  • 162
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值