Rotten Tomatoes 是公共网络上最常被引用的电影评分来源之一。其电影页面向任何人公开显示腐烂番茄指数(Tomatometer)影评人评分、观众评分、类型以及完整标题页面的链接,无需账户即可查看。对于娱乐研究、趋势分析或个人电影数据库而言,以结构化形式收集这些公开评分数据确实很有价值。
本指南将向你展示如何使用 Python 抓取 Rotten Tomatoes 电影评分。整个教程仅限于公开的非个人数据:网站公开显示的标题、评分、类型和页面链接。它不涉及评论者身份、完整影评文本或任何登录后才能访问的内容。由于 Rotten Tomatoes 使用 JavaScript 在客户端渲染其评分,我们通过 Crawling API 路由请求,确保在解析之前页面已完全加载。
你将构建什么
一个小型 Python 爬虫,接受一个或多个 Rotten Tomatoes 电影页面的公开 URL,通过 Crawling API 获取每个渲染后的页面,解析若干公开字段,并将结果导出为 JSON 和 CSV:
- Movie title 页面上显示的电影名称。
- Tomatometer score 由认证影评人汇总的影评人评分。
- Audience score 该电影的综合公众评分。
- Genre 电影所属的类型分类,例如喜剧或剧情。
- Link Rotten Tomatoes 上该电影页面的规范 URL。
这些都是关于电影本身的公开聚合事实。爬虫可在单次运行中处理多部电影,并生成可加载到笔记本或电子表格中的整洁数据集。
为什么普通请求在 Rotten Tomatoes 上会失败
用基础 HTTP 客户端请求 Rotten Tomatoes 的电影 URL,评分将不会出现。Tomatometer 和观众评分以及大部分评分元数据,都是在初始 HTML 到达后通过 JavaScript 动态加载的。像 requests 这样的库只能看到第一层标记外壳,因此你关心的字段会返回空值。此外,来自单个数据中心 IP 的重复自动化流量往往在内容渲染之前就会受到挑战。
因此,一个能正常工作的爬虫需要在同一次请求中具备两点:一个运行页面 JavaScript 的浏览器,以及一个网站读取为普通访客的 IP 地址。你可以自行搭建无头浏览器和住宅代理池,但维护该栈才是主要工作。Crawling API 将两者整合为一次调用。你向它发送带有 JavaScript token 的 URL,它在受信任住宅 IP 后面渲染页面,并返回可直接交给 BeautifulSoup 的渲染完成 HTML。更多背景知识,请参阅我们的如何抓取 JavaScript 网站指南。
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript (JS) token 首先在真实浏览器中渲染页面。Rotten Tomatoes 的评分是在客户端注入的,因此这里需要 JS token。普通 token 返回的与普通请求相同的不完整外壳。
前提条件
首先需要准备几样东西,都不会花太长时间。
基础 Python 知识。 你应该熟悉运行脚本和使用 pip 安装包。如果你对解析 HTML 比较陌生,我们关于如何在 Python 中使用 BeautifulSoup 的入门指南涵盖了提取部分,而使用 Python 抓取网站则全程讲解了完整流程。
Python 3.8 或更高版本。 使用 python --version 确认。如果尚未安装,请从 python.org 安装。
Crawlbase 账户和 JS token。 注册后,打开控制台并复制你的 JavaScript (JS) token。Crawlbase 提供最多 20,000 次免费请求起步,且只对成功请求收费。请像对待密码一样保管 token,不要将其提交到版本控制系统中。
设置项目
创建独立的虚拟环境,然后安装爬虫所需的两个库。
python --version python -m venv rt_env source rt_env/bin/activate pip install crawlbase beautifulsoup4
在 Windows 上,使用 rt_env\Scripts\activate 替代 source 命令激活环境。两个依赖完成主要工作:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 解析返回的 HTML,让你可以通过选择器提取各个字段。
步骤 1:获取渲染后的电影页面
首先获取渲染完成的页面。导入 CrawlingAPI,使用你的 JS token 初始化它,并请求公开的电影 URL。针对客户端渲染目标,两个等待选项非常重要:ajax_wait 告知 API 等待异步内容加载完成,page_wait 在加载后固定等待若干毫秒,确保评分在页面捕获前已出现。在解析之前检查状态,让失败情况清晰可见。
from crawlbase import CrawlingAPI crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def fetch_html(url): options = {"ajax_wait": "true", "page_wait": "5000"} response = crawling_api.get(url, options) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Failed to fetch the page. Status code: {response['headers']['cb_status']}") return None if __name__ == "__main__": url = "https://www.rottentomatoes.com/m/beetlejuice_beetlejuice" html = fetch_html(url) print(html[:500] if html else "No HTML returned")
五秒是 page_wait 的合理起始值;如果评分返回空,可适当增加。示例使用一个公开电影页面。运行脚本,你应该能看到来自标题页面的真实标记,这证明在你编写任何选择器之前渲染功能已经正常工作。
Rotten Tomatoes 在客户端注入其 Tomatometer 和观众评分,因此你需要在一次调用中获得受信任 IP 后面的渲染页面。Crawling API 接受 JS token,在真实浏览器中运行页面,在服务器端轮换住宅 IP,并向你返回渲染完成的 HTML,省去了自行运行无头浏览器集群和代理池的麻烦。先在免费层级指向一个电影页面试用。
步骤 2:检查页面并解析公开字段
在编写选择器之前,在浏览器中打开一个电影页面,使用开发者工具找到每个字段的位置。Rotten Tomatoes 电影页面的结构足够稳定,可以直接定位:
-
Title 位于带有
slot="titleIntro"属性的<h1>元素中。 -
Tomatometer(影评人)评分 位于带有
slot="criticsScore"的rt-text元素内。 -
观众评分 也位于
rt-text元素中,带有slot="audienceScore"。 -
Genre 出现在电影详情列表中,位于标有 Genre 的
<dt>元素之后的匹配<dd>中。
拿到渲染后的 HTML,将其加载到 BeautifulSoup 中并提取每个字段。以下辅助函数通过 slot 选择器读取标题和两个评分,然后遍历详情列表查找类型。每次查找都有防护,确保缺失字段返回空字符串而非抛出异常。
from bs4 import BeautifulSoup def text_or_blank(node): return node.text.strip() if node else "" def find_genre(soup): for dt in soup.select("dt.key rt-text"): if dt.text.strip() == "Genre": dd = dt.find_parent("dt").find_next_sibling("dd") if dd: values = [v.text.strip() for v in dd.find_all(["rt-link", "rt-text"]) if v.text.strip()] return ", ".join(values) return "" def parse_movie(html, url): soup = BeautifulSoup(html, "html.parser") title = text_or_blank(soup.select_one('h1[slot="titleIntro"]')) critics_score = text_or_blank(soup.select_one('rt-text[slot="criticsScore"]')) audience_score = text_or_blank(soup.select_one('rt-text[slot="audienceScore"]')) genre = find_genre(soup) return { "title": title, "tomatometer_score": critics_score, "audience_score": audience_score, "genre": genre, "link": url, }
两个评分直接来自 criticsScore 和 audienceScore 槽。类型来自详情列表,其中每个标签位于 dt.key 中,值位于匹配的 dd 中。连接 rt-link 和 rt-text 值可处理标有多个类型的电影,例如喜剧和奇幻同时标注的情况。
Rotten Tomatoes 会不时更改其标记。这里使用的 slot 属性比深层嵌套的类名更稳定,但如果某个字段返回空值,请在浏览器开发者工具中重新检查实时页面并更新选择器。对于任何生产级爬虫,定期维护都是正常操作。
步骤 3:处理多部电影并导出
大多数研究从电影列表而非单个页面开始。将获取和解析步骤整合到一个循环中,遍历电影 URL 列表,控制请求速率,并将收集的行写入 JSON 和 CSV 两种格式。JSON 保留结构供笔记本使用;CSV 可直接导入电子表格。如果你打算之后将数据用于分析,我们关于为 AI 和 ML 整理和清洗抓取数据的指南涵盖了下一步操作。
import csv import json import time from crawlbase import CrawlingAPI from bs4 import BeautifulSoup crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) MOVIE_URLS = [ "https://www.rottentomatoes.com/m/beetlejuice_beetlejuice", "https://www.rottentomatoes.com/m/deadpool_and_wolverine", "https://www.rottentomatoes.com/m/twisters", ] def save_to_json(rows, filename="movies.json"): with open(filename, "w") as f: json.dump(rows, f, indent=4) print(f"Saved {len(rows)} movies to {filename}") def save_to_csv(rows, filename="movies.csv"): fields = ["title", "tomatometer_score", "audience_score", "genre", "link"] with open(filename, "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(rows) print(f"Saved {len(rows)} movies to {filename}") def main(): movies = [] for url in MOVIE_URLS: html = fetch_html(url) if html: movies.append(parse_movie(html, url)) time.sleep(3) save_to_json(movies) save_to_csv(movies) if __name__ == "__main__": main()
此脚本复用了前几个步骤中的 fetch_html 和 parse_movie 辅助函数,因此请将所有三个代码块粘贴到同一个文件中。请求之间的 time.sleep(3) 不是装饰:控制速率是运行能否保持健康的最关键因素。将你自己的电影 URL 加入 MOVIE_URLS,脚本将依次采集每一部。
输出结果示例
运行完整脚本,你将得到每部电影的整洁公开字段记录,可加载到笔记本或电子表格中。
[ { "title": "Beetlejuice Beetlejuice", "tomatometer_score": "77%", "audience_score": "81%", "genre": "Comedy, Fantasy", "link": "https://www.rottentomatoes.com/m/beetlejuice_beetlejuice" }, { "title": "Deadpool & Wolverine", "tomatometer_score": "79%", "audience_score": "95%", "genre": "Action, Comedy", "link": "https://www.rottentomatoes.com/m/deadpool_and_wolverine" } ]
CSV 格式携带相同的列,每部电影一行,并带有表头行。从这里开始,你可以按 Tomatometer 排序、比较影评人与观众评分,或按类型筛选,用于你的娱乐研究。
扩展至更多电影并保持畅通运行
上述模式可简洁地扩展:更长的 MOVIE_URLS 列表,或先从公开浏览页面(如票房排行榜)收集电影链接的发现步骤,然后再访问每个标题页面。一些良好习惯可以让较大规模的运行保持健康,这些习惯适用于任何有防护的目标。
- 控制请求速率。 保持调用间的延迟,克制大量并行的冲动。控制速率是保持运行顺畅的最快途径。
- 善用轮换。 Crawling API 为你在住宅 IP 间分散请求,确保没有单个地址触发速率限制。如果你自建栈,这正是需要做好的部分。
- 关注状态码。 当运行开始返回非 200 状态时,应退后而非加大力度。
- 保持合理的采集量。 公开评分研究很少需要整个目录。对你关心的电影进行采样然后停止。
更广泛的操作手册,请参阅我们的如何在不被封锁的情况下抓取网站指南。
抓取 Rotten Tomatoes 合法吗?
这是你在编写生产代码之前需要阅读的章节。此处的方法仅限于公开的非个人评分数据:电影标题、Tomatometer 和观众评分、类型以及页面链接。这些是关于一部电影的聚合事实,而非关于某个人的个人数据,因此这项工作处于教育性、公开数据的一侧。即便如此,负责任地采集这些数据意味着遵守 Rotten Tomatoes 的服务条款及其 robots.txt,并控制你的请求速率以避免对网站造成负担。
有几条清晰的界限不能逾越。不得重新发布受版权保护的内容:影评人个人评论的完整文字、编辑文章、图片和视频都是受保护内容,聚合他人的评论文章或将其与具名影评人关联,与记录公开评分是完全不同的行为。坚守数字、类型和链接。不要尝试访问任何登录后才能访问的内容,也不要收集影评人或用户的个人数据。当涉及个人数据时,GDPR 和 CCPA 等隐私法规适用,包括合法的采集依据和响应删除请求的义务。
如果你需要更丰富或大规模的电影数据用于实际项目,官方渠道才是正规途径。Rotten Tomatoes 数据可通过官方合作伙伴关系和 Fandango 旗下服务获取,也有专为程序化访问构建的授权电影数据库。对于任何持续性或商业性项目,官方协议能为你提供有保障的结构,并确保你遵守条款,这是爬虫无法承诺的。请将本教程视为读取公开评分的技术练习,而非授权镜像整个网站的许可。
核心要点
- Rotten Tomatoes 使用 JavaScript 渲染。 评分在客户端加载,普通请求返回不完整的外壳;解析之前必须先渲染页面。
-
渲染和受信任 IP 属于同一次调用。 带有 JS token 的 Crawling API 两者兼顾,
ajax_wait和page_wait控制等待评分出现的时长。 -
定位稳定的槽。
titleIntro、criticsScore和audienceScore槽以及详情列表,比嵌套类名更耐用。 - 导出为 JSON 和 CSV。 JSON 保留结构供分析使用;CSV 可导入电子表格,两者都包含每部电影的相同公开字段。
- 仅限公开评分。 采集标题、评分、类型和链接;绝不重新发布受版权保护的影评文字,并遵守服务条款和 robots.txt。
常见问题
为什么普通请求不能从 Rotten Tomatoes 返回评分?
因为 Tomatometer 和观众评分在初始 HTML 到达后通过 JavaScript 在客户端加载。使用 requests 等库发出的原始 HTTP 请求只能看到第一层标记外壳,因此这些字段返回空值。首先渲染页面(Crawling API 的 JS token 负责处理这一点)才能使评分可供解析。
我需要普通 token 还是 JS token?
需要 JS token。普通 token 获取静态 HTML,而 Rotten Tomatoes 的静态 HTML 与普通请求返回的不完整外壳相同。JS token 在将 HTML 返回之前先在真实浏览器中渲染页面,因此当 BeautifulSoup 解析时,评分和类型已就位。
哪些 Rotten Tomatoes 数据可以安全抓取?
关于电影的公开非个人事实:标题、Tomatometer 评分、观众评分、类型和页面链接。避免重新发布受版权保护的内容(如影评全文),也不要收集评论者或用户的个人数据。坚守聚合评分并遵守网站的服务条款和 robots.txt。
如何同时抓取多部电影的评分?
将电影 URL 放入列表并逐一遍历,为每个 URL 调用获取和解析辅助函数,并在请求之间添加短暂延迟。你也可以添加一个发现步骤,先从公开浏览页面收集链接,然后再访问每个标题页面。保持合理的采集量,并随时将结果写入 JSON 或 CSV。
如果 Rotten Tomatoes 更改了布局会怎样?
你的选择器可能停止匹配,字段将返回空值。在浏览器开发者工具中重新检查实时页面,找到该字段的新属性或元素,并更新选择器。依赖 slot 属性而非深层嵌套的类名可以减少这种情况的发生频率,但对任何爬虫来说,定期维护都是正常操作。
我应该使用官方数据源而非抓取吗?
对于任何持续性或商业性项目,是的。Rotten Tomatoes 数据可通过官方合作伙伴关系和 Fandango 旗下服务获取,也有专为程序化访问构建的授权电影数据库。官方协议为你提供有保障的结构,并确保你遵守条款。此处的抓取方式适用于轻量级、无授权访问的公开数据研究场景。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
