【爬虫逆向】Python逆向采集猫眼电影票房数据

'# 【爬虫逆向】Python逆向采集猫眼电影票房数据

一、背景与问题

在电影行业数据分析领域,猫眼电影作为国内领先的影院售票平台,其发布的票房数据具有重要参考价值。但该平台对数据采集设置了多重反爬虫机制,常规的requests库无法直接获取。本文将深入解析猫眼电影数据接口的逆向分析过程,重点探讨如何通过代码实现数据采集。

二、基本原理

猫眼电影的数据接口通常采用以下特征:

  1. 接口地址常包含动态参数(如/movie/后接电影ID)
  2. 请求参数包含加密字段(如token)
  3. 使用Cookie维持会话
  4. 可能存在IP封禁机制

通过Chrome开发者工具分析发现,其核心接口为https://m.maoyan.com/ajax/movieList,该接口返回包含电影票房、评分等关键信息的JSON数据。但直接访问该接口时会发现响应内容包含错误提示,表明需要正确构造请求参数。

三、环境准备

# 安装必要依赖
pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
import time
import hashlib

四、核心实现

1. 爬虫流程分析

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Referer': 'https://m.maoyan.com/'
}

2. 加密参数生成

通过逆向分析发现,token参数采用md5(时间戳+随机字符串)算法生成:

def get_token():
    timestamp = str(int(time.time()))
    random_str = 'maoyan'  # 逆向分析得到的固定字符串
    return hashlib.md5((timestamp + random_str).encode()).hexdigest()

3. 请求参数构造

def get_params():
    token = get_token()
    return {
        'token': token,
        'cityId': '1',  # 城市ID,1代表北京
        'type': '1',    # 电影类型,1代表正在上映
        'page': '1',    # 页码
        'city': '北京'   # 城市名称
    }

4. 请求发送与响应处理

def fetch_movie_data():
    url = 'https://m.maoyan.com/ajax/movieList'
    params = get_params()
    response = requests.get(url, params=params, headers=headers)
    
    if response.status_code == 200:
        return response.json()
    else:
        print(f"请求失败,状态码:{response.status_code}")
        return None

五、完整案例

1. 完整爬虫流程

def main():
    data = fetch_movie_data()
    if data and data.get('success'):
        movies = data['data']['movies']
        for movie in movies:
            print(f"电影名称:{movie['name']}")
            print(f"票房:{movie['totalBox} 万元")
            print(f"评分:{movie['score']}")
            print("-" * 50)

2. 数据存储

import json

def save_to_file(data, filename='movies.json'):
    with open(filename, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)

3. 完整运行示例

if __name__ == '__main__':
    data = fetch_movie_data()
    if data and data.get('success'):
        save_to_file(data)
        main()

六、源码解析

1. 加密算法分析

逆向分析发现,token参数生成过程如下:

def get_token():
    timestamp = str(int(time.time()))
    random_str = 'maoyan'
    return hashlib.md5((timestamp + random_str).encode()).hexdigest()

关键点:

  • 时间戳使用当前秒级时间
  • 固定字符串maoyan是逆向分析得到的
  • 使用MD5算法进行加密

2. 请求参数构造

def get_params():
    token = get_token()
    return {
        'token': token,
        'cityId': '1',
        'type': '1',
        'page': '1',
        'city': '北京'
    }

参数说明:

  • cityId对应城市ID,1为北京
  • type为1表示正在上映的电影
  • page为分页参数,1表示第一页
  • city为城市名称,用于校验

七、进阶使用

1. 多城市数据采集

def get_city_id(city_name):
    # 逆向分析得到的城市映射关系
    city_map = {
        '北京': '1',
        '上海': '2',
        '广州': '3',
        # ...其他城市
    }
    return city_map.get(city_name, '1')

2. 分页处理

def get_all_pages(city='北京'):
    city_id = get_city_id(city)
    all_data = []
    for page in range(1, 11):  # 获取前10页数据
        params = {
            'token': get_token(),
            'cityId': city_id,
            'type': '1',
            'page': str(page),
            'city': city
        }
        response = requests.get(url, params=params, headers=headers)
        if response.status_code == 200:
            all_data.extend(response.json().get('data', {}).get('movies', []))
    return all_data

八、性能与工程实践

1. 并发请求优化

from concurrent.futures import ThreadPoolExecutor

def get_all_pages_concurrent(city='北京'):
    city_id = get_city_id(city)
    results = []
    with ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(fetch_page, city_id, page) for page in range(1, 11)]
        for future in futures:
            results.extend(future.result())
    return results

def fetch_page(city_id, page):
    params = {
        'token': get_token(),
        'cityId': city_id,
        'type': '1',
        'page': str(page),
        'city': '北京'
    }
    response = requests.get(url, params=params, headers=headers)
    if response.status_code == 200:
        return response.json().get('data', {}).get('movies', [])
    return []

2. 缓存机制

import redis

def get_cached_token():
    r = redis.Redis(host='localhost', port=6379, db=0)
    token = r.get('maoyan_token')
    if not token:
        token = get_token()
        r.setex('maoyan_token', 3600, token)  # 缓存1小时
    return token

九、常见问题与踩坑

1. 常见错误

错误示例:

def get_token():
    return 'fixed_token'  # 固定值

问题分析:

  • 时间戳未更新导致token失效
  • 未使用MD5算法
  • 未包含固定字符串maoyan

解决方案:

  • 使用动态时间戳
  • 正确实现MD5加密
  • 添加固定字符串

2. 请求头缺失

错误示例:

response = requests.get(url, params=params)

问题分析:

  • 缺少User-Agent和Referer头
  • 导致服务器拒绝请求

解决方案:

  • 补充必要请求头
  • 可模拟浏览器访问

3. IP封禁问题

解决方案:

  • 使用代理IP池
  • 设置请求间隔
  • 使用Selenium模拟浏览器

十、最佳实践

  1. 参数生成:确保时间戳和固定字符串的正确性
  2. 异常处理:添加重试机制和超时控制
  3. 数据存储:使用JSON或数据库存储原始数据
  4. 日志记录:记录请求参数和响应结果
  5. 法律合规:遵守《计算机软件保护条例》等法律法规

十一、总结

本文通过逆向分析猫眼电影数据接口,深入探讨了Python爬虫的实现方法。重点分析了加密参数生成机制、请求头配置、分页处理等关键技术点,提供了完整的代码示例和实践方案。在实际应用中,需要根据具体需求选择合适的技术方案,同时注意法律风险和安全防护。对于需要大量数据采集的场景,建议结合代理IP池、并发处理等技术提升效率,但也要注意避免对目标服务器造成过大负担。

最后修改于:2026年09月27日 08:34

评论已关闭

推荐阅读

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日