地区电影市场分析:用Python爬虫抓取猫眼_灯塔专业版各地区票房

在当今高度数据驱动的影视行业,精准把握地区票房表现是制片方、宣发团队和影院经理做出关键决策的基础。一部电影在北上广深的表现与二三线城市有何差异?哪种类型的电影在特定区域更受欢迎?回答这些问题,不能再依赖"拍脑袋"和经验主义,而需要真实、及时、细粒度的数据支撑。

猫眼专业版(piaofang.maoyan.com)和灯塔专业版(box.taobao.com)作为国内最权威的票房数据平台,每日发布包括全国、省、市乃至单个影院的多维度票房数据。这些数据是进行深度市场分析的宝藏。本文将详细介绍如何利用Python爬虫技术,自动化地从这些平台抓取各地区票房数据,并完成一次小规模的分析实践。

一、技术选型与思路分析

在开始编写代码之前,我们需要对目标和数据获取方式进行一番侦察。

  1. 目标网站分析 :猫眼和灯塔专业版的数据部分为公开数据(如首页榜单)和非公开的详细数据(需登录账号)。本文将以猫眼专业版的日票房排行榜 及其背后的单日影片地区票房明细作为抓取目标。这类数据通常通过XHR(Ajax)请求动态加载,而非直接渲染在HTML中,这决定了我们的技术路线。
  2. 反爬策略考虑:专业数据平台通常没有较强的反爬机制,如猫眼专业版,但我们会遵循道德爬虫的准则。灯塔作为阿里系产品,反爬机制可能更为严格(需要更复杂的请求头模拟、Cookie处理等),本文为简化流程,将以猫眼为例。
  3. 技术栈
    • 请求库**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">requests</font>**,用于发送HTTP请求,简单易用。
    • 解析库**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">json</font>**,因为数据接口返回的是JSON格式,直接解析即可,无需HTML解析器。
    • 数据存储**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pandas</font>****<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">csv</font>**,用于将爬取的数据结构化并存储到CSV文件中,方便后续分析。
    • 可视化**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pyecharts</font>**,一个非常强大的可视化库,可以生成交互式的、美观的图表。

核心思路

  1. 打开浏览器开发者工具(F12),切换到"网络(Network)"面板。
  2. 刷新猫眼专业版页面,筛选XHR请求。
  3. 逐个查看请求,找到返回票房数据的API接口。
  4. 分析该接口的URL、请求头(Headers)和请求参数(Payload)。
  5. 在Python代码中模拟这个请求,获取返回的JSON数据。
  6. 从JSON数据中提取我们需要的信息(日期、影片名、总票房、地区、地区票房等)。
  7. 将数据存储到CSV文件或数据库中。
  8. 利用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pandas</font>**进行数据清洗和初步分析,并用**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pyecharts</font>**进行可视化。

二、代码实现过程

下面我们以抓取猫眼专业版某一天的影片地区票房明细为例。

步骤1:分析API接口

通过浏览器开发者工具分析,我们找到了获取地区票房数据的接口(注:接口地址和参数可能随时间变化,请以实际分析为准)。

一个典型的接口URL可能类似于:
**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">https://piaofang.maoyan.com/movie/1281575/regionbox?date=2023-10-01</font>**

其中:

  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">1281575</font>** 是影片的唯一ID(例如《志愿军:雄兵出击》)。
  • **<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">date=2023-10-01</font>** 指定了要查询的日期。

请求这个URL,服务器会返回一个JSON对象,其中**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">data</font>**字段下的**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">list</font>**包含了各个地区的详细票房数据。

步骤2:编写爬虫代码

首先,安装必要的库(如果尚未安装):

然后,开始编写代码:

plain 复制代码
import requests
import pandas as pd
from pyecharts.charts import Bar, Map
from pyecharts import options as opts
from pyecharts.globals import ThemeType
import json
import time
import csv

# 代理配置信息
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"

# 代理服务器
proxyMeta = f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"

proxies = {
    "http": proxyMeta,
    "https": proxyMeta,
}

# 定义一个请求头,模拟浏览器行为,避免被简单的反爬机制拦截
headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
    'Accept': 'application/json, text/plain, */*',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
    'Connection': 'keep-alive',
    # 'Cookie': '你的Cookie(如果需要的话)', # 猫眼这个接口通常不需要Cookie
    'Referer': 'https://piaofang.maoyan.com/'
}

def fetch_movie_list(date):
    """获取某一天的票房排行榜,从而得到电影ID列表"""
    # 这是一个获取单日大盘数据的接口,返回榜单
    url = f"https://piaofang.maoyan.com/box-office?date={date}&type=1"
    try:
        response = requests.get(url, headers=headers, proxies=proxies, timeout=15)
        response.raise_for_status() # 如果状态码不是200,抛出异常
        data = response.json()
        # 解析JSON,获取电影列表
        # 实际路径需要根据返回的JSON结构调整
        movie_list = data['data']['list']
        print(f"成功获取{date}日票房榜单,共{len(movie_list)}部电影")
        return movie_list
    except requests.exceptions.ProxyError as e:
        print(f"代理连接失败: {e}")
        return []
    except requests.exceptions.RequestException as e:
        print(f"请求电影列表失败: {e}")
        return []
    except json.JSONDecodeError as e:
        print(f"解析电影列表JSON失败: {e}")
        return []

def fetch_movie_region_boxoffice(movie_id, movie_name, date):
    """获取单一电影在指定日期的地区票房明细"""
    url = f"https://piaofang.maoyan.com/movie/{movie_id}/regionbox?date={date}"
    try:
        response = requests.get(url, headers=headers, proxies=proxies, timeout=15)
        response.raise_for_status()
        data = response.json()
        region_list = data['data']['list']
        
        data_to_save = []
        for region in region_list:
            # 提取每个地区的信息
            region_data = {
                'date': date,
                'movie_id': movie_id,
                'movie_name': movie_name,
                'region': region.get('regionName'),
                'box_office': region.get('boxInfo'), # 票房,单位通常是万元
                'box_office_ratio': region.get('boxRate'), # 票房占比
                'avg_price': region.get('avgViewBox'), # 平均票价
                'attendance': region.get('attendance'), # 场均人次
            }
            data_to_save.append(region_data)
        print(f"成功获取电影《{movie_name}》在{date}的地区票房数据,共{len(region_list)}个地区")
        return data_to_save
    except requests.exceptions.ProxyError as e:
        print(f"代理连接失败(电影《{movie_name}》): {e}")
        return []
    except requests.exceptions.RequestException as e:
        print(f"请求电影《{movie_name}》的地区票房失败: {e}")
        return []
    except json.JSONDecodeError as e:
        print(f"解析电影《{movie_name}》的地区票房JSON失败: {e}")
        return []
    except KeyError as e:
        print(f"JSON数据结构异常(电影《{movie_name}》): {e}")
        return []

def main():
    target_date = "2023-10-05" # 指定要抓取的日期
    all_region_data = [] # 存储所有电影的地区数据

    # 测试代理连接
    try:
        test_response = requests.get("http://httpbin.org/ip", proxies=proxies, timeout=10)
        print(f"代理连接测试成功,当前IP: {test_response.json()['origin']}")
    except Exception as e:
        print(f"代理连接测试失败: {e}")
        print("请检查代理配置信息是否正确,网络是否通畅")
        return

    # 1. 获取当天的电影排行榜
    movies = fetch_movie_list(target_date)
    if not movies:
        print("未获取到电影列表,程序退出")
        return

    # 2. 遍历榜单中的每一部电影,获取其地区明细
    for movie in movies:
        movie_id = movie.get('movieId')
        movie_name = movie.get('movieName')
        if not movie_id:
            continue
        
        # 暂停一小段时间,避免请求过于频繁
        time.sleep(1.5)  # 稍微延长等待时间,避免触发反爬
        
        single_movie_data = fetch_movie_region_boxoffice(movie_id, movie_name, target_date)
        if single_movie_data:
            all_region_data.extend(single_movie_data)

    # 3. 将所有数据保存到CSV文件
    if all_region_data:
        df = pd.DataFrame(all_region_data)
        filename = f'maoyan_region_boxoffice_{target_date}.csv'
        df.to_csv(filename, index=False, encoding='utf-8-sig') # utf-8-sig支持Excel直接打开显示中文
        print(f"所有数据已保存到文件: {filename}")
        print(f"共爬取{len(all_region_data)}条地区票房记录")
        
        # 这里可以调用数据分析函数
        # analyze_data(df, target_date)
    else:
        print("未获取到任何地区票房数据")

if __name__ == '__main__':
    main()

步骤3:数据清洗与存储

上述代码已经将数据存储为CSV文件。**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">pandas</font>**库使得数据清洗变得非常简单。例如,票房数据可能是字符串"12.3万",我们需要将其转换为浮点数**<font style="color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">123000.0</font>**以便于计算。

plain 复制代码
# 在analyze_data函数或单独进行数据清洗
def clean_data(df):
    # 示例:清洗票房数据(假设原始数据是'1.2万'的形式)
    def convert_box_office(val):
        if '万' in val:
            return float(val.replace('万', '')) * 10000
        else:
            return float(val)
    
    df['box_office_clean'] = df['box_office'].apply(convert_box_office)
    # 类似地,可以清洗其他列...
    return df

三、数据分析与可视化示例

数据抓取和清洗完成后,我们就可以进行分析了。假设我们想分析《志愿军:雄兵出击》在2023年国庆档期间在全国各省的票房分布。

plain 复制代码
def analyze_data(df, date):
    # 假设我们只分析一部特定的电影
    target_movie = "志愿军:雄兵出击"
    df_movie = df[df['movie_name'] == target_movie].copy()
    
    if df_movie.empty:
        print(f"未找到电影《{target_movie}》的数据")
        return
        
    # 1. 绘制全国票房分布地图
    # 准备地图数据:列表,元素为[省份名称,票房值] 
    map_data = [[row['region'], row['box_office_clean']] for _, row in df_movie.iterrows()]
    
    map_chart = (
        Map(init_opts=opts.InitOpts(theme=ThemeType.ROMA, width="1200px", height="600px"))
        .add(
            series_name="票房",
            data_pair=map_data,
            maptype="china",
            is_map_symbol_show=False,
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title=f"{target_movie} {date} 全国各省票房分布(元)"),
            visualmap_opts=opts.VisualMapOpts(
                max_=max(df_movie['box_office_clean']), # 视觉映射的最大值
                is_piecewise=False, # 是否为分段型
                range_text=['高', '低'],
            ),
        )
        .set_series_opts(label_opts=opts.LabelOpts(is_show=True)) # 显示省份名称
    )
    map_chart.render(f"{target_movie}_{date}_票房地图.html")
    
    # 2. 绘制票房TOP10省份柱状图
    df_sorted = df_movie.sort_values(by='box_office_clean', ascending=False).head(10)
    regions = df_sorted['region'].tolist()
    box_offices = df_sorted['box_office_clean'].tolist()
    
    bar_chart = (
        Bar(init_opts=opts.InitOpts(theme=ThemeType.LIGHT))
        .add_xaxis(regions)
        .add_yaxis("票房(元)", box_offices)
        .set_global_opts(
            title_opts=opts.TitleOpts(title=f"{target_movie} {date} 省份票房TOP10"),
            xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45)), # 旋转x轴标签避免重叠
            yaxis_opts=opts.AxisOpts(name="票房"),
        )
    )
    bar_chart.render(f"{target_movie}_{date}_票房TOP10.html")
    
    print(f"可视化图表已生成完毕。")

运行上述代码后,将会生成两个交互式的HTML图表文件:一个是中国地图,颜色深浅代表票房高低;另一个是柱状图,直观展示票房最高的10个省份。

四、总结与展望

通过本文的技术讲解和代码实现,我们成功地构建了一个可以自动抓取、解析、存储和分析猫眼专业版地区票房数据的Python爬虫。这套方法不仅可以用于单日分析,稍加改造(如循环日期)即可用于分析时间序列数据,研究电影票房在不同地区的生命周期和走势。