环境:{
系统:win10
python:python-3.6.4-amd64.exe # 官网下的,自己可以下
urllib:urllib3 # pip install urllib3 安装。
idea: pycharm-community-2017.3.4.exe # 这个官网也有,不用破解,因为是社区版(够用),不是专业版。
时间:大家注意博客时间,因为他api改了也说不定
pycrytodome(AES): pip install pycrytodome # pycryto&cryto直接pip不行,网上也有解决办法,但感觉这个简单
ps: 想节约时间看干货可以直接去最后,我感觉主要是参数问题。
}
想做一个网易云音乐的音频下载爬虫,苦难重重,本人小白,不喜勿喷。主要是记录我在编程时的坑,遇到同样问题不至于浪费时间。
我只做了一个页面爬真实地址,剩下的就简单了。就决定是你了:http://music.163.com/#/song?id=29567100 眉间雪
大家都懂打开网页F12,再F5刷新:
对于b这个GET请求我想大家都知道吧,直接可以打开,通过另存为mp3实现。
主要说a这个POST请求,除了该有的请求头外,他是有参数的:
关于为什么是这个xhr请求和参数解析,大家请移步:https://www.zhihu.com/question/36081767
感谢这个知乎大神提供的方法。
为了测试我自己的代码,对于params和encSecKey我是直接传进去的:
data = {
'params': '/MHyb9Ekp9GO4dUqbNJUDaJ4JruAX+R7ZodEeiAdlSsPNXZOpEmBY5InP574jBLKHYzExLm0q3SPfuIrWdNatehjgX1Zxoo4nhip45ycQUu2/MIK3KcbfD8B7Mh8IXhr',
'encSecKey': '96f3df985b1912785e15b4c3ba7bb070a978693c016514d6ee866c70a54a1baa520d5b8076b1384c7098fc9d2b52816bef418854d17704679d723d53671992ad680a1b4488959e8cc28fd6762368a1840364dbf62c60e4ef6663015a35e8fdc6eabb52d1ccca642b4ad827826140b1248758f83b04dcd9661f4b203c03735237'
}
请求头的话是这个:
headers = {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Cache-Control': 'max-age=0, no-cache',
'Connection': 'keep-alive',
'Content-Length': '408',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'music.163.com',
'Referer': 'http://music.163.com/',
'Pragma': 'no-cache',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0',
}
但实际这个才是最关键的,必须有。当然其他的最好有(我强迫症)
'Content-Type': 'application/x-www-form-urlencoded'
于是开始用urllib3做请求:(urllib3的用法官网有的就不说了,https://urllib3.readthedocs.io/en/latest/user-guide.html)
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) #这个里面两个参数是防止产生Warning的
# 方式:post, 地址:http://music.163.com/weapi/song/enhance/player/url(也可以在后面加上"?csrf_token")
# fields用于传入表单数据(data就是前面的代码段data), headers(也是前面的代码段headers)
html = http.request('POST', 'http://music.163.com/weapi/song/enhance/player/url',fields=data, headers=headers)
result = html.data # 返回的二进制数据。可以通过json.loads(data.decode('utf-8'))编程json格式数据
code = html.status # 返回的请求状态码
结果,code=200,result=b''。居然是空。
这个问题困扰了我两天。我不断的检查:是post方式啊,关键请求头有啊(我一直以为是请求头的问题,无聊到一条一条的试),表单数据是fields={}传的啊,不可能是body={}(其实我也试了),data数据是对的啊(不会因为时间产生错误)。吃枣药丸。。。。。。。
灵光一闪,我擦,祖传手艺----编译啊怎么能忘呢,于是开始一条一条的看,还真的有发现,这条官网上没说,是关于content-type请求的:
这里面提到了encode_multipart参数,当为False时才会以content-type:application/x-www-form-urlencoded 传递数据data。并且默认是True,而我就是因为没设置为False才会导致传递方式错误,而且这个content-type这个不能使用aplication/json或者multipart_boundary(默认是这种)方式。
http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where())
html = http.request('POST', 'http://music.163.com/weapi/song/enhance/player/url',fields=data, headers=headers,encode_multipart=False)
添加encode_multipart=False参数后,终于返回了数据。
但是我这个地方params和encSecKey是指定的,肯定可以返回数据的。当我用上文知乎大神和另一篇博客给的计算方法计算的params和encSecKey做请求时却还是返回空数据,现在还不知道原因,也许是因为跨时间太久,加密方式发生改变导致。我还没解决出来,等弄出来会更新的。源代码我这挺乱的,等我整理好就传上来。
Good Luck To You All.