天天看点

关于爬网易云音乐的一些坑和一些方法

最近,心血来潮的我准备去爬爬网易云音乐(https://music.163.com/)的评论,对于这个网站,我在开始学习爬虫的时候也试过,但是当时没有坚持下去,最后放弃了,现在为了工作项目的准备,准备将这些网站慢慢的进行爬取,好了,废话不多说。

我用了3天,将评论爬取下来,暂时只是将其下载到我的磁盘上,没有上传数据库

1第一步我准备先将热门的歌手爬取下来,然后进入歌手的主页将歌曲爬取,在接着爬取歌曲对应的评论,

https://music.163.com/#/discover/artist这个是歌手的主页

刚开始我就用一般爬虫最基础的方法,requests请求,结果请求下载的不是在网页上看到的东西,我尝试着加上headers,结果还是一如既往的糟糕,返回的一直一堆没有用的数据,(有一个小问题,我掉进了这个坑,就是网页链接中的#,参考这篇博客https://www.cnblogs.com/kaituorensheng/p/3776527.html)

然后我就更换了url,https://music.163.com/discover/artist去掉了#号后,果然爬取出来的数据不一样了,数据中出现了几个歌手的名字,但是我想要的热门歌手的名字却没有,我之后打开了控制台,在里面进行找寻,最后发现了几个post请求的url,通过抓包工具,fiddler,我获取到了想要的url,但是这个请求中的两个参数却是加密的,看下图

关于爬网易云音乐的一些坑和一些方法

之后我通过百度,知道了这个是一种js的一种加密方式,然后我准备按照js的加密方式,找到数据的加密参数,模拟浏览器的post请求来实现我的目的。

2.第二步:

   分析网页js,打开控制台(F12),找到Sources,按ctrl+f,搜索encSecKey,找到后发现在某个函数当中,

关于爬网易云音乐的一些坑和一些方法

然后准备进行单步调试,设置断点,刷新网页,进入单步调试模式

关于爬网易云音乐的一些坑和一些方法

在调试的过程中,我发现,i参数是一个随机的16位的字符,efg三个参数不变,分别为:

e:"010001"
f:"00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
g:"0CoJUm6Qyw8W8jud"
           

只有d参数在变化。

!function() {
    function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)
            e = Math.random() * b.length,
            e = Math.floor(e),
            c += b.charAt(e);
        return c
    }
    function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b)
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)
          , f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }
    function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d, a)
    }
    function d(d, e, f, g) {
        var h = {}
          , i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }
    function e(a, b, d, e) {
        var f = {};
        return f.encText = c(a + e, b, d),
        f
    }
    window.asrsea = d,
    window.ecnonasr = e
}();
           

这个是js进行的加密方式,具体分析:如下图:

关于爬网易云音乐的一些坑和一些方法

废话不多说,看代码:

from Crypto.Cipher import AES
import base64
from binascii import hexlify
import json,os,requests,headers

class Encrypyed():
    def __init__(self):
        self.pub_key = '010001'
        self.modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
        self.nonce = '0CoJUm6Qyw8W8jud'

    def create_secret_key(self, size):
        return hexlify(os.urandom(size))[:16].decode('utf-8')

    def aes_encrypt(self,text, key):
        iv = b'0102030405060708'
        pad = 16 - len(text) % 16
        text = text + pad * chr(pad)
        text=text.encode('utf-8')
        encryptor = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv)

        result = encryptor.encrypt(text)
        result_str = base64.b64encode(result).decode('utf-8')
        return result_str

    def rsa_encrpt(self,text, pubKey, modulus):
        text = text[::-1]
        rs = pow(int(hexlify(text.encode('utf-8')), 16), int(pubKey, 16), int(modulus, 16))
        return format(rs, 'x').zfill(256)

    def work(self,text):
        text = json.dumps(text)
        i=self.create_secret_key(16)

        encText =self.aes_encrypt(text, self.nonce)
        encText=self.aes_encrypt(encText,i)
        encSecKey=self.rsa_encrpt(i,self.pub_key,self.modulus)
        data = {'params': encText, 'encSecKey': encSecKey}
        return data
           

我将其封装为一个类class Encrypyed()如下图分析:

关于爬网易云音乐的一些坑和一些方法

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

上述的Crypto要进行安装,安装命令:

pip install pyCryptodome

这个我在2018.12.26日亲测有效,导入的时候如果导入失败,进入python安装目录下的Lib\site-packages找到crypto目录,将名字改为Crypto然后进行导入,因为我是首字母大写的,默认安装时小写的

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

生成参数后,看下面代码:

def parse_data(self,song_id,offset=0,limit=20):
        data={"rid":"R_SO_4_"+str(song_id),"offset":str(offset),"total":"true","limit":str(limit),"csrf_token":""}
        return self.work(data)

    def parse(self,song_id,offset,limit):
        url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_"+str(song_id)+"?csrf_token="
        data=self.parse_data(song_id,offset,limit)
        res = requests.post(url=url, data=data, headers=headers.headers_discuss, verify=False)
        return res
           

在对需要参数的网页解析时,要先找到post请求的url和对应的d参数,才能生成对应的data参数

关于爬网易云音乐的一些坑和一些方法

通过上述的操作,我们就完成了对于某一个歌曲的评论的爬取。

完整代码在上面,只需要添加headers,然后对类进行实例化,调用parse传递相应参数即可得到json格式的评论

如上就讲清楚了一点,对于网易云音乐的评论的加密的方法

之后我在说说如何找到对应参数,我将这个写到另一篇博客中《爬取网易云音乐评论的d参数的分析方式》

然后我们就得到了相应的url和data了

看代码:

url = "https://music.163.com/weapi/v1/resource/comments/R_SO_4_"+str(song_id)+"?csrf_token="
        data=self.parse_data(song_id,offset,limit)
        res = requests.post(url=url, data=data, headers=headers.headers_discuss, verify=False)
           

将其整合进行请求

在对歌手的主页进行爬取时,发现并没有歌曲的数据post请求,然后就可以进行普通的方法,对网页url进行拼接,发送resquests请求,用xpath进行解析,或者美丽汤。

以上的就是我这次爬取网易云遇到的坑,如果还有其他问题可以给我留言,我一定尽心解答。

源代码的github地址:https://catherineangel.github.io/music_163/

继续阅读