博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python3爬虫-下载网易云音乐,评论
阅读量:5966 次
发布时间:2019-06-19

本文共 6801 字,大约阅读时间需要 22 分钟。

# -*- coding: utf-8 -*-'''16位随机字符的字符串参数一获取歌曲下载地址      "{"ids":"[1361348080]","level":"standard","encodeType":"aac","csrf_token":""}"获取歌曲评论信息      "{"rid":"R_SO_4_1361348080","offset":"0","total":"true","limit":"20","csrf_token":""}"第二三四为参数是固定的"010001""00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7""0CoJUm6Qyw8W8jud"encSecKey 通过方法c,传入参数  参数一为16为随机字符串,参数二、三为上面的参数二三encText 进行了两次加密  通过方法b    第一次是通过上面从参数一,参数四拿到返回值   第二次是返回值和16随机字符串加密# 关于我遇到的问题1、首先就是参数一的获取,参数一很容易看得出来就是json格式的字典,通过json.dumps(dict)就能得到,对,当时我也是这样做的,因为分析网易云进行js加密的代码,它也是将对象(也就是python的字典,类似)进行Json.stringify(obj)。所以我就跟着转为json格式的数据。代码书写完毕之后,就进行测试阶段了,启动脚本,他给我返回的是json格式的字符串,400的错误,提示信息为参数错误,最终我排除了请求头是否不合法等原因,那就是我进行加密的时候,得出的加密结果不对。为了检测是否我加密错误,我继续在谷歌浏览器的开发者工具调试js代码,因为进行加密的时候有一串16位的随机字符串,我先在浏览器中获取到这个随机字符串然后在我写的python代码中,将那个随机的字符串固定为浏览器获取到的16位随机字符串,并且执行,比较二者参数哪里对不上,encSecKey的值是能对上的,encText(也就是formdata中的params)是对不上的,所以我获取encText的加密内容出错了。当时我完全没有考虑到是参数一的错误,因为我认为参数一是对的,就是json格式的数据呀,我认为我加密的逻辑写错了。我把网易云加密的那段js代码,copy一份到本地的html文件中执行,参数也是一致,得到的结果也是和浏览器加密后的数据是一致的,我把加密后的数据直接用在python代码中,执行,数据成功返回了,这是我更加肯定是我加密代码写错了,经过一段的测试,我在本地的html文件中,把参数一的值写成一个很简单的字符串 "aaaa",我也把python代码中的参数一也改为一样。分别执行,卧槽,加密后参数完全一样,找出了原因,竟然是我认为不会出错的参数一的原因。找到原因了,我就去看看参数一print出来到底是啥,js中参数一console.log几次,它的值是不变的我再看python中,测试几次,终于知道了原因,我竟然忽略了字典是无序了,dic = {"name":"zhuyu","age":22}print(json.dumps(dic))# 多执行几次,下面是输出结果,你会发现:{"name": "zhuyu", "age": 22}{"age": 22, "name": "zhuyu"}解决问题一:字典是无序的,执行json.dumps得到的数据不是固定的,所以必须要弄一个固定的json格式数据,json数据它也是一个字符串,我自己弄一个和js中的json数据一样的字符串就好了也就是这个>>>:  self.arg1 = '{"ids":"[%s]","level":"standard","encodeType":"aac","csrf_token":""}' % songId2、对加密方法不清楚对这一块确实不擅长,你看到js中的加密代码,却不知道它传递的参数,到底有啥用,是用来做什么的,你先要懂js代码加密的逻辑,你猜能写python代码来实现一样的加密逻辑。解决问题二:这个只能多百度,Google了,了解到加密方法,传递的参数是什么形式,参数作用是啥,返回值又是什么3、了解网易云js加密的流程只有知道流程了,只能写python加密的流程,这个需要你会chrome的开发者工具的使用,对js进行调试,知道重点的变量代表什么解决问题三:刚开始确实不会,需要查看一些博客,知道每个按钮对应的功能是什么。还有就是要多看,多看,多看,指的是调试js代码4、知道js加密代码的位置我的方法:先看请求的所携带的参数,还有就是url,请求方法等等。。。然后就是ctrl+shift+f搜索你认为的关键字,然后就是慢慢找吧如果请求那里找不到,也搜索不到加密的js位置,试试搜索 encrypt,还不行只能百度,Google了,看看别人的'''from Crypto.Cipher import AESimport base64import randomimport codecsimport requestsfrom fake_useragent import UserAgentimport timefrom multiprocessing import Processclass DownLoadWYY:    ua = UserAgent()    def __init__(self):        self.arg2 = "010001"        self.arg3 = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"        self.arg4 = "0CoJUm6Qyw8W8jud"        self.session = requests.Session()        self.session.headers = {            "Referer": "https://music.163.com/",            "User-Agent": self.ua.random        }        self.__get_random_str()        # self.__init_session()    def __init_session(self):        '''拿到后面需要的cookies'''        resopnse = self.session.get("https://music.163.com/#/discover/playlist", headers=self.session.headers)        print(resopnse.headers)    def __AES_encrypt(self, text, key):        '''        获取到加密后的数据        :param text: 首先CBC加密方法,text必须位16位数据        :param key: 加密的key        :return: 加密后的字符串        '''        iv = "0102030405060708"        pad = 16 - len(text) % 16        if isinstance(text, str):            text = text + pad * chr(pad)        else:            text = text.deocde("utf-8") + pad * chr(pad)        aes = AES.new(key=bytes(key, encoding="utf-8"), mode=2, iv=bytes(iv, encoding="utf-8"))        res = aes.encrypt(bytes(text, encoding="utf-8"))        res = base64.b64encode(res).decode("utf-8")        return res    def __get_encText(self):        encText = self.__AES_encrypt(self.arg1, self.arg4)        encText = self.__AES_encrypt(encText, self.random_16_str)        return encText    def __get_encSecKey(self):        '''通过查看js代码,获取encSecKey'''        text = self.random_16_str[::-1]        rs = int(codecs.encode(text.encode('utf-8'), 'hex_codec'), 16) ** int(self.arg2, 16) % int(self.arg3, 16)        return format(rs, 'x').zfill(256)    def __get_random_str(self):        '''这是16位的随机字符串'''        str_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"        random_str = ""        for i in range(16):            index = random.randint(0, len(str_set) - 1)            random_str += str_set[index]        self.random_16_str = random_str    def __getFormData(self):        '''获取到提交的数据'''        data = {
"params": self.__get_encText(), "encSecKey": self.__get_encSecKey()} return data def downloadSong(self, songId): '''获取到歌曲的下载的地址就好了。如果想要下载可以单独再写一个方法去下载音乐''' print("开始爬取歌曲mp3地址....") self.arg1 = '{"ids":"[%s]","level":"standard","encodeType":"aac","csrf_token":""}' % songId api = "https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=" headers = self.session.headers.copy() formdata = self.__getFormData() response = self.session.post(url=api, data=formdata, headers=headers) print("歌曲的下载地址为>>:", response.json()["data"][0]["url"]) def song_comment(self, songId): '''获取到歌曲评论信息,我只是将结果print出来,如果保存的话,可以单独写一个保存的方法''' print("开始爬取歌曲评论信息....") # self.arg1的格式为:"{"rid":"R_SO_4_歌曲id","offset":"0","total":"true","limit":"20","csrf_token":""}" # 第一页为0,第二页为20,第三页为40 第四页为60 第五页为80 offset = 0 n = 1 api = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_{}?csrf_token=".format(songId) headers = self.session.headers.copy() while True: self.arg1 = '{"rid":"R_SO_4_%s","offset":"%s","total":"true","limit":"20","csrf_token":""}' % ( songId, offset) formdata = self.__getFormData() response = self.session.post(url=api, headers=headers, data=formdata) # print("*"*100) # print("第{}页评论".format(n)) comment_list = response.json().get("comments") for dic in comment_list: try: print("用户: {}".format(dic["user"]["nickname"])) print("评论内容: {} ".format(dic.get("content"))) print() except UnicodeEncodeError: pass offset += 20 n += 1 time.sleep(1)if __name__ == '__main__': song = DownLoadWYY() comment = DownLoadWYY() p1 = Process(target=song.downloadSong, args=(1361348080,)) p2 = Process(target=comment.song_comment, args=(1361348080,)) p1.start() p2.start() p1.join() p2.join() print("爬取完毕...")

 

转载于:https://www.cnblogs.com/zhuchunyu/p/10778427.html

你可能感兴趣的文章
ajax上传图片的本质
查看>>
转]最长递增子序列问题的求解
查看>>
SilverLight:基础控件使用(6)-Slider控件
查看>>
Android写的一个设置图片查看器,可以调整透明度
查看>>
第 5 章 File Share
查看>>
判断字符串解析是JsonObject或者JsonArray
查看>>
[LeetCode] Implement strStr()
查看>>
多模块Struts应用程序的几个问题(及部分解决方法)
查看>>
1.2. MariaDB
查看>>
SpringSide示例之HelloWorld
查看>>
LINQ-to-SQL那点事~LINQ-to-SQL中的并发冲突与应对
查看>>
日志不说谎--Asp.net的生命周期
查看>>
C#~异步编程续~.net4.5主推的await&async应用
查看>>
C#进行MapX二次开发之图层操作
查看>>
ASP.NET 运行机制详解
查看>>
C++ little errors , Big problem
查看>>
在 ML2 中配置 OVS vlan network - 每天5分钟玩转 OpenStack(136)
查看>>
Selenium2+python自动化34-获取百度输入联想词
查看>>
【★★★★★】提高PHP代码质量的36个技巧
查看>>
如何解决/home/oracle: is a directory报警
查看>>