前阵子帮团队搭国际电影节年度数据链路,发现很多人都卡在同一个位置:入围名单是公开的,但散在官网公告、媒体通稿和资料站里,格式五花八门;就算好不容易抓到一张Excel表,距离“用数据说话”也还差着十万八千里。正好这个项目完整走了一遍从采集、清洗到分析预测的流程,所以我按落地的真实顺序整理一版,标题里的“CSV导出”不是凑数,后面每一步的数据流转都离不开它。
这个项目适合三类人:刚学完Python基础、想找个不无聊的requests练手项目的爬虫新手;已经在写爬虫、但没接触过“数据抓完以后怎么用”的开发者;以及真正在做影视数据、奖项分析,想把获奖预测从玄学变成可解释模型的运营和分析同学。整条链路不用Selenium,也不上深度学习,依赖的就是requests、BeautifulSoup、pandas和scikit-learn这一套熟脸。
1. 先想清楚再动手:这个系统到底要做什么
1.1 为什么拿“电影节入围名单”当实战样本
电影节预测和普通影评人凭感觉押片不一样,它其实是一个非常标准的结构化决策问题。每年入围名单公布后,每部影片就是一行数据,包含单元、奖项、导演、地区、制片方这些字段;等颁奖礼结束,官方又会公布一份获奖名单,这份名单就可以作为历史标签存下来。
相比豆瓣电影Top250那种静态榜单,电影节入围名单有几个很明显的优点。
数据更新节奏稳定。每年同一个时间窗口,电影节组委会会集中公布入围片单,这种周期性非常适合做增量抓取。字段半结构化但需要处理。不同页面给的信息密度差别很大,有的只给片名和导演,有的会连带制片地区、国际发行商、首映性质一起列出,这种“半脏、半规整”的数据最能锻炼清洗能力。结果有清晰的标签可用。一部影片最后获奖还是陪跑,是明确的二分类标签,这让后面的预测模型有监督信号,而不是自说自话。
所以我一直觉得,想做爬虫项目却不知道抓什么的人,与其盯着电商价格、招聘岗位这种变化极快又容易触发风控的目标,不如先拿电影节公开公告练手。服务器压力小,数据结构够复杂,分析环节又有得写,指数级提升经验值。
1.2 模块拆解:把“抓—存—析—出”串成一条流水线
项目标题看着长,实际做下来就四个模块:数据采集、数据存储、智能分析、结果导出。我在正式写代码前会先在纸上画数据流,确定每一步的输入输出都是什么,不然很容易写着写着就把逻辑绕成一团。
第一层是采集调度。给定电影节ID和年份,抓取当年的入围名单页,解析成统一的record列表。每一条record至少包含festival、year、section、category、film、director这些核心字段。第二层是标准化存储。所有record合并后转成pandas DataFrame,做字段对齐、缺失值标记,最终导出为CSV。这一步是整个项目的地基,后面所有分析都基于这份CSV。第三层是特征工程和模型训练。读取历年CSV,把“导演入围次数”“前哨获奖数”“首映性质”“题材类型”等转成数值特征,再训练一个能输出概率的二分类模型。第四层是预测输出。把新一年的入围名单喂给模型,生成每部影片的获奖概率排序表,再次落成CSV,方便别人拿去继续做透视表或画图。
这套流水线的好处是每一层都可以单独替换。今天用requests,明天想换httpx,不需要动解析代码;今天想用CSV,过两个月数据量大了想换SQLite,只需要改存储层。很多初学者喜欢把爬虫、清洗、建模全塞在一个脚本里,单次跑通没问题,但一旦页面结构改变或者要增加分析维度,整个人就会陷入改一处崩三处的泥潭。
1.3 技术选型,以及为什么刻意绕开重型工具
这次项目技术栈很克制:requests负责HTTP请求,BeautifulSoup加lxml解析HTML,pandas处理表格,scikit-learn做建模,CSV作为存储交换格式。之所以不选Scrapy,是这个项目的数据源规模和页面数量还没到需要分布式抓取的程度;Scrapy的中间件、Item Pipeline、Twisted异步机制本身是很好的东西,但对一个每日几十次请求量级的学习型项目来说,学习成本高于实际收益。
我也没上Selenium或Playwright。电影节官网的入围名单公布页,绝大多数是发布会当天生成静态HTML,不需要执行复杂JavaScript。用Selenium去渲染只会消耗更多内存,还会让反爬判断变得敏感,更麻烦的是调试起来要同时维护浏览器驱动版本。先检查页面源码里有没有数据,比直接开浏览器靠谱得多。这个道理放在新闻站、公告站、政府公开数据页都成立。
模型方面同样只用了逻辑回归和随机森林。原因很简单,电影节入围样本通常一届主竞赛单元就三四十部片子,能积累的历史有效记录可能只有两三百条,这种量级的数据上深度学习等于让小学生做博士论文,过拟合会让你产生虚假的预测成就感。在样本量小、特征维度不高的情况下,可解释的线性模型和带约束的树模型反而更稳。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据抓取:从页面到结构化的第一公里
2.1 数据源选型:优先找结构化程度高的公开页面
许多新人一上来就盯着电影节官网首页,但首页通常是动态轮播图,对爬虫并不友好。我的习惯是优先找两类页面:一类是官网自己的“入围名单/官方评选”栏目,它们有时会发布成列表页或PDF公告;另一类是公开资料站按年份整理的奖项页面,这类页面表格结构规整,字段相对完整。
目标站点很明确,第一件事不是写代码,而是看目标站点的robots.txt,同时确认要抓的页面本身有没有“禁止采集”的声明。电影节入围名单是公开宣传材料,个人学习与研究用途通常问题不大,但仍要遵守最基本的爬虫礼仪:控制请求频率、标明可追溯的User-Agent、不抓登录后才能看的内容。关于网络访问,这里只提一句:你的运行环境只要能正常打开目标页面就行,打不开就换一个公开可用的页面做练习,网络连通性不是这个项目要解决的技术重点,不用在无谓的环境问题上消耗时间。
2.2 写爬虫前的准备:请求头、超时和重试
在具体抓数据之前,推荐先手动把目标页面保存成一份HTML文件放在本地,拿这份本地文件调试解析逻辑。不要每次都在线调试,页面结构即使没变,频繁请求也容易触发限流。本地文件调试稳定以后,再切换成在线抓取。
在线请求模块我习惯保持简洁,但以下几个参数一定不会省:请求头里面的User-Agent、Referer;连接超时和读取超时;出错后的指数退避重试。示例代码如下:
python复制import time
import random
import requests
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"Accept-Language": "zh-CN
