python爬虫爬取音乐-JS逆向爬虫

'# Python爬虫爬取音乐-JS逆向爬虫

一、背景与问题

在音乐网站爬取数据时,常遇到以下挑战:

  • 动态生成的加密参数(如token)
  • 前端JavaScript动态计算的请求参数
  • 后端接口的动态签名机制
  • 防止请求被识别的反爬虫机制

以网易云音乐为例,其音乐评论接口的请求参数包含动态生成的token和signature字段。传统爬虫无法直接获取这些参数,需要逆向分析前端JavaScript代码,找到生成规则后进行模拟计算。

二、基本原理

JS逆向爬虫的核心原理是:

  1. 通过浏览器开发者工具分析前端代码
  2. 找到关键的加密算法逻辑
  3. 用Python模拟执行JavaScript代码
  4. 构造合法的请求参数
  5. 发送请求获取数据

关键点在于理解前端JavaScript的运行时环境,包括:

  • 全局变量作用域
  • 异步函数执行顺序
  • 模块化加载机制
  • 依赖的第三方库(如crypto-js)

三、环境准备

pip install pyexecjs requests

准备工具:

  • Chrome浏览器开发者工具(F12)
  • Python 3.8+
  • 代码编辑器(VSCode/PyCharm)

四、核心实现

1. 分析JS代码

以网易云音乐为例,分析https://music.163.com/api/comment/接口的请求参数生成逻辑:

// 伪代码示例(简化版)
function getSignature() {
    const crypto = require('crypto');
    const token = 'xxxxx';
    const key = '123456';
    const data = token + key;
    return crypto.createHash('md5').update(data).digest('hex');
}

2. 逆向算法实现

import execjs

# 加载JavaScript代码
with open('signature.js', 'r') as f:
    js_code = f.read()

# 构造参数
token = 'xxxxx'  # 从页面获取的token
key = '123456'   # 密钥

# 执行JS代码
ctx = execjs.compile(js_code)
signature = ctx.call('getSignature', token, key)
print(signature)

3. 请求构造

import requests

headers = {
    'User-Agent': 'Mozilla/5.0',
    'Referer': 'https://music.163.com'
}

params = {
    'type': '1',
    'id': '190264',
    'offset': '0',
    'limit': '20',
    'csrf_token': 'xxxxx'
}

response = requests.get(
    'https://music.163.com/api/comment',
    params=params,
    headers=headers
)
print(response.json())

五、完整案例

1. 网易云音乐评论爬取案例

步骤1:获取token

def get_token():
    response = requests.get('https://music.163.com/api/v1/user/190264')
    return response.cookies.get('__csrf_token')

步骤2:构造请求参数

def get_signature(token):
    js_code = """
    const crypto = require('crypto');
    function getSignature(token, key) {
        const data = token + key;
        return crypto.createHash('md5').update(data).digest('hex');
    }
    return getSignature;
    """
    ctx = execjs.compile(js_code)
    return ctx.call('getSignature', token, '123456')

步骤3:发送请求

def get_comments():
    token = get_token()
    signature = get_signature(token)
    
    params = {
        'type': '1',
        'id': '190264',
        'offset': '0',
        'limit': '20',
        'csrf_token': token,
        'signature': signature
    }
    
    response = requests.get(
        'https://music.163.com/api/comment',
        params=params,
        headers={'User-Agent': 'Mozilla/5.0'}
    )
    return response.json()

完整运行示例:

if __name__ == '__main__':
    comments = get_comments()
    for comment in comments['comments']:
        print(f"{comment['user']['nickname']}: {comment['content']}")

六、源码解析

1. JS逆向核心部分

// signature.js
const crypto = require('crypto');

function getSignature(token, key) {
    const data = token + key;
    return crypto.createHash('md5').update(data).digest('hex');
}

关键点:

  • 使用Node.js的crypto模块
  • 通过MD5算法生成签名
  • 需要完整的依赖环境(如crypto模块)

2. Python执行部分

import execjs

def execute_js(js_code, *args):
    ctx = execjs.compile(js_code)
    return ctx.call('getSignature', *args)

注意:

  • 需要完整的JS运行时环境
  • 需要处理模块依赖
  • 需要处理异步函数

七、进阶使用

1. 处理复杂加密

# 处理多层加密
js_code = """
const crypto = require('crypto');
function getComplexSignature(token, key) {
    const first = crypto.createHash('md5').update(token).digest('hex');
    const second = crypto.createHash('sha1').update(first + key).digest('hex');
    return second;
}
"""

signature = execute_js(js_code, 'xxxxx', '123456')

2. 处理动态加载

# 使用Selenium处理动态加载内容
from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://music.163.com')
token = driver.get_cookie('__csrf_token')['value']
driver.quit()

八、性能与工程实践

1. 性能优化

  • 使用缓存机制:缓存token和签名
  • 使用多线程:并发处理多个请求
  • 使用代理池:避免IP被封禁
from concurrent.futures import ThreadPoolExecutor

def batch_request(urls):
    results = []
    with ThreadPoolExecutor(max_workers=5) as executor:
        results = executor.map(fetch, urls)
    return results

2. 安全风险

  • 遭遇反爬机制:网站可能更新加密算法
  • IP封禁:频繁请求可能导致被封
  • 法律风险:音乐数据版权问题

3. 异常处理

try:
    response = requests.get(url, timeout=5)
    response.raise_for_status()
except requests.exceptions.RequestException as e:
    print(f"请求失败: {e}")

九、常见问题与踩坑

1. 常见错误

错误示例:

ctx.call('getSignature', token)  # 缺少参数

错误原因: JavaScript函数需要两个参数

解决方法:

ctx.call('getSignature', token, '123456')

2. 参数处理错误

错误示例:

token = 'xxxxx'  # 未正确获取

解决方法:

token = get_token()  # 使用正确的获取方式

3. 依赖缺失

错误示例:

# 未加载crypto模块
ctx = execjs.compile("function getSignature() { ... }")

解决方法:

ctx = execjs.compile("""
const crypto = require('crypto');
function getSignature() { ... }
""")

十、最佳实践

1. 推荐方案

  • 使用pyexecjs执行JavaScript代码
  • 使用requests发送HTTP请求
  • 使用concurrent.futures进行并发处理
  • 使用fake_useragent生成随机User-Agent

2. 方案比较

方案优点缺点
JS逆向可处理复杂加密需要维护JS运行时
Selenium支持动态加载资源消耗大
使用API无需逆向需要API权限

3. 实际应用建议

适合使用JS逆向的场景:

  • 数据是动态生成的
  • 接口有加密参数
  • 需要处理复杂算法
  • 非常规的反爬机制

不适合使用的场景:

  • 数据量极大
  • 需要频繁请求
  • 网站有强反爬机制
  • 需要处理复杂前端框架(如React/Vue)

十一、总结

JS逆向爬虫是应对动态内容反爬的重要手段,但需要深入理解前端代码和算法逻辑。本文详细讲解了:

  • JS逆向的基本原理
  • 代码实现的完整流程
  • 常见错误及解决方法
  • 性能优化技巧
  • 安全风险和法律注意事项

在实际开发中,应根据具体情况选择合适的方案。对于音乐网站这类存在动态加密的场景,JS逆向是有效的解决方案,但需注意法律风险和技术更新。通过合理的设计和优化,可以实现高效稳定的爬虫系统。

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日