Python爬取网易云音乐热评(附源码)

Python爬取网易云音乐热评(附源码)


一、获取歌曲信息

爬取热歌榜的200首歌曲的热评
网易云热歌榜
通过分析不难发现这些歌曲并不在网页源代码中,这些歌曲信息都是动态加载的

点击检查,Network,F5刷新一下网页
在这里插入图片描述
粗略的预览浏览器返回的信息看出这个toplist?id=3778678中就有我们想要的歌曲信息
请求的URL
在这里插入图片描述
歌曲名和歌曲的ID
在这里插入图片描述

二、获取热评信息

选择一首歌,点击进入,查看下面的评论信息
在这里插入图片描述
同样查看网页源代码,这些信息也不再源代码中
点击检查,Network,F5刷新一下网页
在这里插入图片描述
通过查看浏览器返回的信息可以看出我们想要的热评在**get?csrf_token=**中
在这里插入图片描述
接下来就是获取热评信息了

三、破解JS加密

当我们向这个网页发送请求后根本得不到热评信息
查看POST请求提交的data信息因该是加密的
在这里插入图片描述
因此只要知道params和encSecKey这两个值是如何加密的就能爬取评论信息

点击Initiator查看执行的js程序有哪些
在这里插入图片描述

点击第一个是执行的最后一下JS程序,查看到了data的信息,设置断点,F5刷新,调试跟踪data的信息变化
在这里插入图片描述
通过调试可以发现下图中的data还没有被加密,通过分析这个i3x中的数据就是要提交的信息
在这里插入图片描述
那么这个程序的下一步因该就是data的加密过程,通过分析我们找到了params和encSecKey的值
在这里插入图片描述
其中执行的window.asrsea就是params和encSecKey的加密过程
把这段代码单独拿出分析

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
    }
    window.asrsea = d,

分析可得window.asrsea就是d,d中的h.encText,h.encSecKey就是params和encSecKey的值
d函数中有四个参数分别是d,e,f,g 查看window.asrsea的调用可知参数d就是提交的data,e,f,g是定值
在这里插入图片描述

先分析函数c可知函数c是加密encSecKey,c的参数中e,f是定值,i是函数a生成的,分析函数a可知这是一个产生16位随机数的函数,如果i的值是定值则encSecKey的值也一定是定值
通过调试浏览器确定一个i的值之后得到encSecKey的值

#1808492017是歌曲的ID,pageNo是页数
data = {
        'cursor': '-1',
        'offset': '0',
        'orderType': '1',
        'pageNo': '1',
        'pageSize': '20',
        'rid': 'R_SO_4_1808492017',
        'threadId': 'R_SO_4_1808492017'
    }
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

g = '0CoJUm6Qyw8W8jud'
# 随机值
i = 'vDIsXMJJZqADRVBP'
def get_encSecKey():
    encSecKey = "516070c7404b42f34c24ef20b659add657c39e9c52125e9e9f7f5441b4381833a407e5ed302cac5d24beea1c1629b17ccb86e0d9d57f6508db5fb7a6df660089ac57b093d19421d386101676a1c8d1e312e099a3463f81fbe91f28211f9eccccfbfc64148fdd65e2b9f5fcf439a865b95fb656e36f75091957f0a1d39ca8ddd3"

    return encSecKey

接下来就是确定encText的值
可以看出encText是执行了两次b函数后得到的,分析b函数可以得出这是一个AES加密,密钥是‘0102030405060708’,加密的数据就是data,加密模式是CBC
以此用Python实现AES的加密过程就可以得到encText的值了,也就是params的值

python 在 Windows下使用AES时要安装的是pycryptodome 模块

pip install pycryptodome

AES加密过程

def get_params(data):
	#两次AES加密
    first = enconda_params(data,g)
    second = enconda_params(first,i)

    return second
# 加密params
def enconda_params(data,key):

    d = 16 - len(data) % 16
    data += chr(d) * d
    data = data.encode('utf-8')
    aes = AES.new(key=key.encode('utf-8'),IV='0102030405060708'.encode('utf-8'),mode=AES.MODE_CBC)
    bs = aes.encrypt(data)
    #b64解码
    params = b2a_base64(bs).decode('utf-8')
    return params

四、保存热评

这里我使用了MySQL数据库保存
Python连接MySQL数据库需要安转pymysql模块

pip install pymysql

建表

CREATE TABLE `music_163` (
  `song_id` int DEFAULT NULL,
  `song_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `comments` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `nickname` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

连接数据库

#这里注意:charset选择utf8mb4,因为评论中存在表情符号
#数据库建表的字符集也要选择utf8mb4
connect = pymysql.Connect(
        host='localhost',
        port=3306,
        user='root',
        passwd='root',
        db='wangyi',
        charset='utf8mb4'
    )

保存信息
在这里插入图片描述

五、程序代码

import requests
from Crypto.Cipher import AES
from lxml import etree
from binascii import b2a_base64
import json
import time
import pymysql
from pymysql.converters import escape_string

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'
}
e = '010001'
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'

g = '0CoJUm6Qyw8W8jud'
# 随机值
i = 'vDIsXMJJZqADRVBP'

def get_163():
	#热歌榜URL
    toplist_url = 'https://music.163.com/discover/toplist?id=3778678'

    response = requests.get(toplist_url,headers=headers)
    html = response.content.decode()
    html = etree.HTML(html)
    namelist = html.xpath("//div[@id='song-list-pre-cache']/ul[@class='f-hide']/li")
    #可选择保存到文件
    # f = open('./wangyi_hotcomments.txt',mode='a',encoding='utf-8')
    for name in namelist:
        song_name = name.xpath('./a/text()')[0]
        song_id = name.xpath('./a/@href')[0].split('=')[1]
        content = get_hotConmments(song_id)
        print(song_name,song_id)
        save_mysql(song_id,song_name,content)
        # f.writelines(song_id+song_name)
        # f.write('\n')
        # f.write(str(content))
    # f.close()


def get_encSecKey():
    encSecKey = "516070c7404b42f34c24ef20b659add657c39e9c52125e9e9f7f5441b4381833a407e5ed302cac5d24beea1c1629b17ccb86e0d9d57f6508db5fb7a6df660089ac57b093d19421d386101676a1c8d1e312e099a3463f81fbe91f28211f9eccccfbfc64148fdd65e2b9f5fcf439a865b95fb656e36f75091957f0a1d39ca8ddd3"

    return encSecKey

def get_params(data):
    first = enconda_params(data,g)
    second = enconda_params(first,i)

    return second
# 加密params
def enconda_params(data,key):

    d = 16 - len(data) % 16
    data += chr(d) * d
    data = data.encode('utf-8')
    aes = AES.new(key=key.encode('utf-8'),IV='0102030405060708'.encode('utf-8'),mode=AES.MODE_CBC)
    bs = aes.encrypt(data)
    #b64解码
    params = b2a_base64(bs).decode('utf-8')
    # params = b64decode(bs)
    return params

def get_hotConmments(id):
    # print(id)
    #提交的信息
    data = {
        'cursor': '-1',
        'offset': '0',
        'orderType': '1',
        'pageNo': '1',
        'pageSize': '20',
        'rid': f'R_SO_4_{id}',
        'threadId': f'R_SO_4_{id}'
    }
    post_data = {
        'params': get_params(json.dumps(data)),
        'encSecKey': get_encSecKey()
    }
    #获取评论的URL
    song_url = 'https://music.163.com/weapi/comment/resource/comments/get?csrf_token=ce10dc34c626dc6aef3e07c86be16d70'

    response = requests.post(url=song_url,data=post_data,headers=headers)
    # time.sleep(1)
    json_dict = json.loads(response.content)
    # print(json_dict)
    hotcontent = {}
    for content in json_dict['data']['hotComments']:
        content_text = content['content']
        content_id = content['user']['nickname']
        hotcontent[content_id] = content_text

    return hotcontent
#保存到MySQL数据库
def save_mysql(song_id,song_name,content):
    connect = pymysql.Connect(
        host='localhost',
        port=3306,
        user='root',
        passwd='root',
        db='wangyi',
        charset='utf8mb4'
    )
    cursor = connect.cursor()
    # sql = "inster into music_163 velues(%d,'%s','%s','%s')"
    sql = """
        INSERT INTO music_163(song_id, song_name, comments,nickname)
        VALUES(%d, '%s', '%s', '%s')
    """
    for nikename in content:
        data = (int(song_id),escape_string(song_name),escape_string(content[nikename]),escape_string(nikename))
        print(data)
        cursor.execute(sql % data)
        connect.commit()

if __name__ == '__main__':
    get_163()

感谢观看!!!

  • 23
    点赞
  • 150
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值