如果你最近搜过“使用 Python 抓取 IMDb”,你大概率已经发现一件事:你找到的大多数教程都已经不能用了。不是那种“有点过时”的坏,而是那种“返回 0 个结果,还一堆 NoneType 报错”的坏。
过去几周里,我测试了能找到的几乎所有主流 IMDb 抓取教程——GeeksforGeeks、Medium、freeCodeCamp、Kaggle 笔记本,能找的我都试了。在 214+ 个公开的 GitHub 仓库 里,绝大多数都还在引用 td.titleColumn、td.ratingColumn 这类 CSS 选择器,但自从 IMDb 在 2023 年 6 月重做 Top 250 页面后,这些选择器就已经不存在了。结果就是:论坛里一堆开发者在问“为什么我的代码返回空结果?”,而像 Cinemagoer 这样的热门库维护者也只能承认“除了把每个解析器都修一遍,也没什么别的办法。”这篇指南会介绍两种现在仍然有效的 Python 方法,教你处理分页和常见错误,说明什么时候 Python 根本不是最佳工具,并告诉你如何让爬虫更耐用,避免进“报废爬虫墓地”。
用 Python 抓取 IMDb 到底是什么意思?
网页爬取指的是通过程序从网页中提取数据——不用手动复制粘贴,而是写脚本替你完成。我们说“抓取 IMDb”时,指的是用 Python 从 IMDb 网页中提取结构化电影数据,比如片名、评分、类型、演员表、时长和投票数。
Python 里最常见的技术栈由三个库组成:requests(抓取网页)、BeautifulSoup(解析 HTML 并定位数据)、pandas(整理并导出结果)。有些教程还会用 Selenium 或 Playwright 去处理需要 JavaScript 渲染的页面,不过你会看到,其实还有更快的方法。
还有一个重要提醒:本指南中的所有内容都以 2025 年中期 IMDb 当前的页面结构为准做了验证。IMDb 大约每 6–12 个月就会改一次页面,所以如果你在 2027 年读这篇文章,部分选择器可能已经变了。(我也会教你怎么处理这种情况。)
为什么要用 Python 抓取 IMDb?真实应用场景
在写任何一行代码之前,你真的会拿 IMDb 数据做什么?答案取决于你是谁。
IMDb 评论数据集是全球使用最广泛的 NLP 基准之一——Maas 等人 2011 年的基础论文已经累积了 5,706 次引用,而且这个数据集已经内置到 TensorFlow、Keras 和 PyTorch 里。在 Hugging Face 上,stanfordnlp/imdb 数据集每月下载量达到 213,321 次,已经被用于训练 1,500 多个模型。所以如果你做机器学习,大概率早就对 IMDb 数据很熟悉了。
但它的用途远不止学术研究:
| 用途 | 适合谁 | 需要的数据字段 |
|---|---|---|
| 电影推荐引擎 | 数据科学家、爱好者 | 片名、类型、评分、演员表 |
| 流媒体平台内容策略 | 产品/内容团队 | 评分、投票数、上映年份、类型 |
| 情感分析 / NLP 训练 | 机器学习研究者、学生 | 评论、评分 |
| 竞品内容分析 | 影视行业分析师 | 票房、上映日期、评分趋势 |
| 影视旅游研究 | 旅游局、旅行公司 | 拍摄地点、热度指标 |
| 学术研究 | 高校研究人员 | 任意结构化电影元数据 |
单是影视旅游市场,预计在 2025 年全球规模就达到 660 亿美元。Netflix 在 2024 年内容投入超过 170 亿美元,而 80% 的观看行为都由个性化推荐驱动。说到底:IMDb 数据会影响各行各业的真实决策。
获取 IMDb 数据的几种方案(在写代码之前先想清楚)
这一部分通常会被大多数教程直接跳过。他们会直接让你 pip install beautifulsoup4,却完全不问:对你的场景来说,Python 抓取到底是不是最佳方案。
完整的选择如下:
| 方案 | 最适合 | 优点 | 缺点 |
|---|---|---|---|
| Python + BeautifulSoup | 学习、定制提取 | 完全可控、灵活 | 选择器脆弱,经常失效 |
JSON-LD / __NEXT_DATA__ 提取 | 想要稳定性的开发者 | 能处理 JS 内容,更抗变化 | 需要理解 JSON 结构 |
| IMDb 官方数据集 | 大规模分析、学术用途 | 合法、完整、2600 万+ 条目、每日更新 | TSV 格式,没有评论/图片 |
| Cinemagoer(IMDbPY)库 | 按条目程序化查询 | Python 风格 API、字段丰富 | 88 个未解决问题,最后发布于 2023 年 5 月 |
| TMDb API | 电影元数据 + 图片 | 免费 API key、JSON、文档完善 | 数据来源不同(不是 IMDb 评分) |
| Thunderbit(无代码) | 不写代码的人、快速导出 | 2 步抓取,AI 自动建议字段,可导出到 Excel/Sheets | 大规模抓取按额度计费 |
关于这些选项有几点说明。Cinemagoer 自 2023 年 5 月以来就没在 PyPI 发布过新版本,而且在 IMDb 2025 年 6 月重设计后,它的大多数解析器都坏了——我现在不建议用于生产环境。TMDb 很不错,但它用的是自己的评分系统,不是 IMDb 的。至于 IMDb 官方企业 API,通过 AWS Data Exchange 购买的话一年要 5 万美元以上,这对大多数人来说都不现实。
如果你根本不想写代码,Thunderbit 的 AI 网页爬虫可以读取 IMDb 页面,自动建议提取字段(片名、评分、年份、类型),并且只需两步就能导出到 Excel、Google Sheets、Airtable 或 Notion。IMDb 改版时,AI 会自动适配布局变化,所以你不用维护任何选择器。后面我会再详细说。
现在,如果你确实想写 Python——下面有两种真正能用的方法。
方法 1:使用 BeautifulSoup 抓取 IMDb(传统做法)
这是大多数教程里的经典方法。它能用,但我得先说明白:在我介绍的几种方法里,它是最脆弱的。IMDb 的 CSS 类名是自动生成的,重设计时就会变化。不过,它仍然是学习网页爬取基础知识的最佳方式。
第 1 步:安装并导入 Python 库
你需要四个包:
pip install requests beautifulsoup4 pandas lxml
分别作用如下:
requests—— 发送 HTTP 请求,获取网页内容beautifulsoup4—— 解析 HTML,方便查找特定元素pandas—— 将提取的数据整理成表格,并导出为 CSV/Excellxml—— 快速 HTML 解析器(BeautifulSoup 可以把它当后端)
导入代码如下:
import requests
from bs4 import BeautifulSoup
import pandas as pd
第 2 步:向 IMDb 发送 HTTP 请求
这一步通常就是新手第一次卡住的地方。IMDb 会拦截不带正确 User-Agent 请求头的请求——你会得到 403 Forbidden 错误。Python Requests 默认的 user-agent 字符串(python-requests/2.31.0)会立刻被标记。
url = "https://www.imdb.com/chart/top/"
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",
"Accept-Language": "en-US,en;q=0.9"
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
print(f"抓取页面失败:{response.status_code}")
else:
print("页面抓取成功")
Accept-Language 请求头也很重要——没有它的话,IMDb 可能会根据你 IP 的地理位置返回不同语言的内容。
第 3 步:用 BeautifulSoup 解析 HTML
拿到 HTML 后,创建一个 BeautifulSoup 对象,然后开始寻找正确的元素。你可以在 Chrome 里打开 IMDb Top 250 页面,右键点击电影标题,选择“检查”,查看底层 HTML 结构。
soup = BeautifulSoup(response.text, "lxml")
截至 2025 年中期,Top 250 页面使用的选择器如下:
- 电影容器:
li.ipc-metadata-list-summary-item - 标题:
h3.ipc-title__text - 年份:
span.cli-title-metadata-item(第一个 span) - 评分:
span.ipc-rating-star--rating
提醒一下:这些带 ipc- 前缀的类名是 IMDb 组件系统生成的。自 2023 年 6 月改版以来它们一直比较稳定,但不能保证以后不会再变。
第 4 步:提取电影数据(片名、年份、评分)
这里我和大多数教程不一样:我加入了 try/except 错误处理。我看过的竞品指南里几乎都没有这一步,也正因为如此,它们在选择器变化时会静默报错。
movies = []
movie_items = soup.select("li.ipc-metadata-list-summary-item")
for item in movie_items:
try:
title_tag = item.select_one("h3.ipc-title__text")
title = title_tag.text.strip() if title_tag else "N/A"
year_tag = item.select_one("span.cli-title-metadata-item")
year = year_tag.text.strip() if year_tag else "N/A"
rating_tag = item.select_one("span.ipc-rating-star--rating")
rating = rating_tag.text.strip() if rating_tag else "N/A"
movies.append({
"title": title,
"year": year,
"rating": rating
})
except Exception as e:
print(f"解析电影时出错:{e}")
continue
print(f"已提取 {len(movies)} 部电影")
第 5 步:用 Pandas 保存为 CSV 或 Excel
df = pd.DataFrame(movies)
df.to_csv("imdb_top_250.csv", index=False)
df.to_excel("imdb_top_250.xlsx", index=False)
print(df.head())
示例输出:
title year rating
0 1. The Shawshank Redemption 1994 9.3
1 2. The Godfather 1972 9.2
2 3. The Dark Knight 2008 9.0
3 4. The Godfather Part II 1974 9.0
4 5. 12 Angry Men 1957 9.0
这样是能跑的。但它依赖的是可能随时失效的 CSS 选择器——这就引出了我真正推荐的方法。
方法 2:JSON-LD 技巧——完全跳过 HTML 解析
这是没有任何竞品文章会讲的方法,也是我在任何认真项目里都会用的方法。IMDb 会在每个页面的 <script type="application/ld+json"> 标签里嵌入结构化数据,也就是 JSON-LD(面向链接数据的 JavaScript 对象表示法)。这些数据遵循 Schema.org 标准,Google 也会拿它来生成富搜索结果,而且它比 CSS 类名稳定得多。
生产级工具 Apify IMDb Scraper 的提取优先级是:“JSON-LD > NEXT_DATA > DOM”。这也是我建议你采用的顺序。
为什么 JSON-LD 比 CSS 选择器更可靠
| 方法 | 支持 JS 内容吗? | 能抗 UI 变化吗? | 速度 | 复杂度 |
|---|---|---|---|---|
| BeautifulSoup + CSS 选择器 | ❌ 否 | ⚠️ 脆弱(类名会变) | 快 | 低 |
| JSON-LD 提取 | ✅ 是 | ✅ 遵循 Schema.org 标准 | 快 | 低-中 |
__NEXT_DATA__ JSON 提取 | ✅ 是 | ✅ 相对稳定 | 快 | 低-中 |
| Selenium / Playwright | ✅ 是 | ⚠️ 脆弱 | 慢 | 中-高 |
| Thunderbit(无代码,2 步) | ✅ 是(AI 直接读取页面) | ✅ AI 自动适配 | 快 | 无 |
像 ipc-metadata-list-summary-item 这样的 CSS 类名,是 IMDb React 组件系统自动生成的,改版时就会变化。JSON-LD 模式表达的是实际数据模型,而不是展示层。它们的区别就像:一个是看书的目录,另一个是试图根据字号大小来猜章节。

逐步操作:从 JSON-LD 提取 IMDb 数据
第 1 步:抓取页面
和前面一样——使用带正确 User-Agent 请求头的 requests。
import requests
from bs4 import BeautifulSoup
import json
import pandas as pd
url = "https://www.imdb.com/chart/top/"
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",
"Accept-Language": "en-US,en;q=0.9"
}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, "lxml")
第 2 步:找到 JSON-LD 脚本标签
script_tag = soup.find("script", {"type": "application/ld+json"})
if not script_tag:
print("这个页面没有找到 JSON-LD")
else:
data = json.loads(script_tag.string)
print(f"找到 JSON-LD,类型为:{data.get('@type', 'unknown')}")
第 3 步:解析结构化数据
在 Top 250 页面上,JSON-LD 包含一个 itemListElement 数组,里面有全部 250 部电影。每个条目都包括排名、名称、URL、aggregateRating、发布日期、类型、描述、导演和演员数组。
movies = []
for item in data.get("itemListElement", []):
movie = item.get("item", {})
rating_info = movie.get("aggregateRating", {})
movies.append({
"rank": item.get("position"),
"title": movie.get("name"),
"url": movie.get("url"),
"rating": rating_info.get("ratingValue"),
"vote_count": rating_info.get("ratingCount"),
"date_published": movie.get("datePublished"),
"genre": ", ".join(movie.get("genre", [])),
"description": movie.get("description"),
})
第 4 步:导出为 CSV
df = pd.DataFrame(movies)
df.to_csv("imdb_top_250_json_ld.csv", index=False)
print(df.head())
示例输出:
rank title url rating vote_count date_published genre
0 1 The Shawshank Redemption https://www.imdb.com/title/tt0111161/ 9.3 2900000 1994-10-14 Drama
1 2 The Godfather https://www.imdb.com/title/tt0068646/ 9.2 2000000 1972-03-24 Crime, Drama
2 3 The Dark Knight https://www.imdb.com/title/tt0468569/ 9.0 2800000 2008-07-18 Action, Crime, Drama
250 部电影全部到手。干净、结构化、没有任何 CSS 选择器的花活。并且因为这些数据遵循 Schema.org 标准(Google 做搜索结果也依赖它),所以比可视化布局更不容易变化。
额外技巧:用 __NEXT_DATA__ 获取单个电影页信息
如果你想拿到单个电影页更丰富的数据(时长、完整演员表、剧情简介、海报图片),IMDb 还会嵌入一个 __NEXT_DATA__ JSON 对象。这就是 React 用来“水合”页面的数据——如果删掉它,网站就会坏。
# 在类似 /title/tt0111161/ 这样的单个电影页面上
next_data_tag = soup.find("script", {"id": "__NEXT_DATA__"})
if next_data_tag:
next_data = json.loads(next_data_tag.string)
above_fold = next_data["props"]["pageProps"]["aboveTheFoldData"]
title = above_fold["titleText"]["text"]
year = above_fold["releaseYear"]["year"]
rating = above_fold["ratingsSummary"]["aggregateRating"]
runtime_seconds = above_fold.get("runtime", {}).get("seconds", 0)
genres = [g["text"] for g in above_fold["genres"]["genres"]]
plot = above_fold["plot"]["plotText"]["plainText"]
列表页用 JSON-LD,单个电影页用 __NEXT_DATA__。这就是生产级做法。
为什么你的 IMDb 爬虫老是坏掉(以及怎么修)
这是我查过的每一个 IMDb 抓取论坛里,被提到最多的痛点。用户会写:“因为 UI 变化,部分代码坏了”、“2024 年还不能用!”——而回复通常要么是沉默,要么就是“试试 Selenium”。
根本原因是 IMDb 正在持续向 React/Next.js 前端迁移。下面是几个导致大规模失效的重要时间点:
| 日期 | 变化内容 | 受影响内容 |
|---|---|---|
| 2022 年 11 月 | 姓名页重设计 | 旧的姓名页爬虫 |
| 2023 年 6 月 | Top 250 页面重设计 | 所有 td.titleColumn / td.ratingColumn 选择器 |
| 2023 年 4 月 | 作品子页面重设计 | 简介、奖项、新闻爬虫 |
| 2023 年 10 月 | 高级搜索重设计 | 基于搜索的爬虫 |
| 2025 年 6 月 | /reference 页面重设计 | Cinemagoer 库(大多数解析器) |
差不多每 6–12 个月就会有一次重大破坏性变化。如果你的爬虫依赖 CSS 类名,那你基本是在跑步机上追页面。
常见错误以及修复方法
结果为空 / NoneType 报错
这是最常见的错误。你会看到 AttributeError: 'NoneType' object has no attribute 'text'。这表示 BeautifulSoup 没找到你要找的元素——通常是因为 CSS 类名变了,或者内容是由 JavaScript 渲染的。
修复方法:切换到 JSON-LD 提取(上面的方法 2)。数据就在初始 HTML 响应里,不需要 JavaScript。
403 Forbidden
IMDb 使用 AWS WAF 来检测并拦截机器人。最常见的触发原因是缺少或明显伪造的 User-Agent 请求头。这个问题在 多个开源项目以及 IMDb 自己的社区论坛里都有记录,IMDb 员工也承认过这个问题。
修复方法:始终带上真实浏览器的 User-Agent 字符串和 Accept-Language: en-US 请求头。使用 requests.Session() 做连接复用。
只返回 25 条结果
IMDb 搜索页和“最热门”列表使用的是懒加载——初始只渲染大约 25 条结果,随着你滚动再通过 AJAX 加载更多。
修复方法:使用 URL 参数分页(下一节会讲),或者直接切换到 Top 250 页面,因为它会在一次响应中加载完 250 部电影。
选择器突然失效
已经失效的旧选择器包括:td.titleColumn、td.ratingColumn、.lister-item-header、.inline-block.ratings-imdb-rating。如果你的代码还在用这些,说明已经坏了。
修复方法:优先使用 data-testid 属性(比如 h1[data-testid="hero-title-block__title"]),不要用自动生成的类名。更好的办法是直接用 JSON-LD。
决策框架:短期修复 vs 长期修复
- 短期修复:给每个选择器外层都包上
try/except,校验 HTTP 状态码,记录错误而不是直接崩溃 - 中期修复:从 CSS 选择器切换到 JSON-LD 提取(方法 2)
- 长期修复:如果要大规模分析,直接使用 IMDb 官方批量数据集,或者用像 Thunderbit 这样每次都会用 AI 重新读取页面结构的工具——不用维护选择器,AI 会自动适配布局变化
超越 25 条结果限制:IMDb 分页与大数据集抓取
我看过的每一个竞品教程都只抓一页。没有人讲分页。但如果你要的不只是一份单页列表,很快就会撞墙。
不需要分页的页面
好消息是:Top 250 页面会在一次服务器渲染响应里加载全部 250 部电影。JSON-LD 和 __NEXT_DATA__ 都包含完整数据集。不需要分页。
IMDb 搜索分页是怎么工作的
IMDb 搜索页通过 start= URL 参数分页,每次增加 50:
https://www.imdb.com/search/title/?groups=top_1000&start=1
https://www.imdb.com/search/title/?groups=top_1000&start=51
https://www.imdb.com/search/title/?groups=top_1000&start=101
下面是一段遍历结果的 Python 循环:
import time
all_movies = []
for start in range(1, 1001, 50): # 遍历 top 1000
url = f"https://www.imdb.com/search/title/?groups=top_1000&start={start}"
response = requests.get(url, headers=headers)
if response.status_code != 200:
print(f"在 start={start} 处失败:{response.status_code}")
break
soup = BeautifulSoup(response.text, "lxml")
# 用你喜欢的方法提取电影
# ...
print(f"已抓取起始于 {start} 的页面")
time.sleep(3) # 注意礼貌——IMDb 大约在 50 次快速请求后会开始封禁
这里的 time.sleep(3) 很重要。社区反馈显示,IMDb 在大约 50 次快速请求后就会开始封 IP。建议使用 2–5 秒之间的随机延迟。
什么时候可以完全不抓:IMDb 官方批量数据集
如果你真的需要大规模数据,IMDb 提供了 7 个免费的 TSV 文件,可在 datasets.imdbws.com 获取,并且每天更新:
| 文件 | 内容 | 大小 |
|---|---|---|
| title.basics.tsv.gz | 片名、类型、类型标签、时长、年份 | ~800 MB |
| title.ratings.tsv.gz | 平均评分、投票数 | ~25 MB |
| title.crew.tsv.gz | 导演、编剧 | ~300 MB |
| title.principals.tsv.gz | 主演/主创 | ~2 GB |
| title.akas.tsv.gz | 各地区别名 | ~1.5 GB |
| title.episode.tsv.gz | 电视剧集信息 | ~200 MB |
| name.basics.tsv.gz | 人物:姓名、出生年份、代表作 | ~700 MB |
把它们载入 Pandas 很简单:
ratings = pd.read_csv("title.ratings.tsv.gz", sep="\t", compression="gzip")
basics = pd.read_csv("title.basics.tsv.gz", sep="\t", compression="gzip", low_memory=False)
# 按 tconst(IMDb 条目 ID)合并
merged = basics.merge(ratings, on="tconst")
top_movies = merged[merged["titleType"] == "movie"].nlargest(250, "averageRating")
这些数据集覆盖 2,600 多万条目。没有分页、没有选择器、没有 403 错误。许可证仅限个人和非商业用途——你不能重新发布或转售这些数据。
无代码捷径:Thunderbit 帮你处理分页
如果你需要带分页的 IMDb 数据,但又不想自己写分页逻辑,Thunderbit 原生支持点击分页和无限滚动。你只要告诉它抓取什么,剩下的它会自己处理——包括滚动加载内容。
使用 Python 抓取 IMDb:完整可运行代码(可直接复制)
下面是两段可直接运行的独立脚本。
脚本 A:BeautifulSoup 方法(CSS 选择器)
import requests
from bs4 import BeautifulSoup
import pandas as pd
url = "https://www.imdb.com/chart/top/"
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",
"Accept-Language": "en-US,en;q=0.9"
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
print(f"错误:{response.status_code}")
exit()
soup = BeautifulSoup(response.text, "lxml")
movie_items = soup.select("li.ipc-metadata-list-summary-item")
movies = []
for item in movie_items:
try:
title = item.select_one("h3.ipc-title__text")
year = item.select_one("span.cli-title-metadata-item")
rating = item.select_one("span.ipc-rating-star--rating")
movies.append({
"title": title.text.strip() if title else "N/A",
"year": year.text.strip() if year else "N/A",
"rating": rating.text.strip() if rating else "N/A",
})
except Exception as e:
print(f"因错误跳过电影:{e}")
df = pd.DataFrame(movies)
df.to_csv("imdb_top250_bs4.csv", index=False)
print(f"已保存 {len(df)} 部电影")
print(df.head())
脚本 B:JSON-LD 方法(推荐)
import requests
from bs4 import BeautifulSoup
import json
import pandas as pd
url = "https://www.imdb.com/chart/top/"
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",
"Accept-Language": "en-US,en;q=0.9"
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
print(f"错误:{response.status_code}")
exit()
soup = BeautifulSoup(response.text, "lxml")
script_tag = soup.find("script", {"type": "application/ld+json"})
if not script_tag:
print("没有找到 JSON-LD 数据")
exit()
data = json.loads(script_tag.string)
movies = []
for item in data.get("itemListElement", []):
movie = item.get("item", {})
rating_info = movie.get("aggregateRating", {})
directors = movie.get("director", [])
director_names = ", ".join(
d.get("name", "") for d in (directors if isinstance(directors, list) else [directors])
)
movies.append({
"rank": item.get("position"),
"title": movie.get("name"),
"url": movie.get("url"),
"rating": rating_info.get("ratingValue"),
"votes": rating_info.get("ratingCount"),
"year": movie.get("datePublished", "")[:4],
"genre": ", ".join(movie.get("genre", [])),
"director": director_names,
"description": movie.get("description"),
})
df = pd.DataFrame(movies)
df.to_csv("imdb_top250_jsonld.csv", index=False)
print(f"已保存 {len(df)} 部电影")
print(df.head())
这两段脚本都包含错误处理,并能输出干净的 CSV。脚本 B 能给你更丰富的数据——导演、描述、URL——而且对布局变化更有韧性。
不写代码也能抓 IMDb(用 Thunderbit)
不是每个人都需要或想写 Python。也许你只是一个运营分析师,只想把本周评分最高的电影导到表格里。也许你是内容策略人员,想比较不同年份的类型趋势。在这些场景下,自己写爬虫就太大材小用了。
下面是用 Thunderbit 获取同样数据的方法:
开始前:
- 难度:初学者
- 所需时间:约 2 分钟
- 你需要准备:Chrome 浏览器、Thunderbit Chrome 扩展(免费版可用)
第 1 步: 打开你想抓取的 IMDb 页面。在 Chrome 中打开 IMDb Top 250(或任何其他 IMDb 列表/搜索页面)。
第 2 步: 在 Thunderbit 侧边栏点击“AI 建议字段”。AI 会扫描页面并推荐列——通常会包括片名、年份、评分、类型,以及根据页面不同的其他字段。你会看到一个预览表,里面显示建议字段。
第 3 步: 按需调整字段。删除你不需要的列,或者点击“+ 添加列”,用自然语言描述你想要的内容(例如“导演名字”或“投票数”)。
第 4 步: 点击“抓取”。Thunderbit 会提取数据。对于无限滚动或分页页面,它会自动处理滚动。
第 5 步: 导出。点击导出按钮,选择你的格式——Excel、Google Sheets、CSV、Airtable 或 Notion。数据会在几秒内到达你的目标位置。
这里的关键优势不仅仅是省事——更重要的是 Thunderbit 的 AI 每次都会重新读取页面结构。IMDb 改版时(而且它一定会改),AI 会自动适配。没有要更新的选择器,没有要修的代码。对于任何曾经在截止日前夜 2 点被坏掉的爬虫折磨过的人来说,这价值很高。
Thunderbit 还支持子页面抓取——你可以点进每部电影的详情页,把演员、导演、时长以及列表页看不到的其他字段补充进表格里。如果你想看实际效果,可以查看 Thunderbit YouTube 频道。
抓取 IMDb 合法吗?你需要知道这些
用户在论坛里经常直接问:“这样做合法吗?IMDb 不希望别人抓他们的网站。” 这是个很合理的问题,而且没有竞品文章真正讲清楚。
IMDb 的 robots.txt:Top 250 页面(/chart/top/)、单个影片页面(/title/ttXXXXXXX/)和姓名页(/name/nmXXXXXXX/)都没有被 robots.txt 屏蔽。被屏蔽的路径包括 /find、/_json/*、/search/name-text、/user/ur*/ratings 以及各种 AJAX 接口。文件里没有指定 Crawl-delay。
IMDb 的使用条款:相关条款写的是:“除非获得我们明确书面同意,否则你不得在本网站上使用数据挖掘、机器人、屏幕抓取或类似的数据收集和提取工具。” 另外还有一条禁止转售或商业使用抓取数据。
实际意味着什么:最近 2024 年的法院裁决(Meta v. Bright Data、X Corp v. Bright Data)认为,如果用户从未同意过服务条款,那么条款未必能约束用户——如果你是在未登录的情况下抓取公开可见数据,服务条款的可执行性就比较有争议。但这仍然是一个在变化中的法律领域。
更稳妥的替代方案:IMDb 的 官方可下载数据集明确允许个人和非商业用途。TMDb 的 API 也很宽松,免费 key 就能用。如果你想确保没有明显风险,这两个方案都很好。
实用建议:如果你确实要抓取,请保持礼貌的抓取频率(请求间隔 time.sleep(3)),设置正确的请求头,不要访问 robots.txt 明确屏蔽的路径。如果是商业项目,请咨询法律专业人士,或者直接使用官方数据集/API。
我们在 Thunderbit 博客里已经深入讨论过 网页爬取的法律影响。
结论:选择最适合你的 IMDb 抓取方式
简短总结如下:
- BeautifulSoup + CSS 选择器:适合学习基础知识。预计它每 6–12 个月就会坏一次。一定要加错误处理。
- JSON-LD 提取:我最推荐给任何持续进行的 Python 项目。它遵循 Schema.org 标准,比 CSS 类名稳定得多,而且不用 JavaScript 渲染就能拿到干净的结构化数据。
__NEXT_DATA__JSON:作为补充,用来获取单个影片页更丰富的数据(时长、完整演员表、剧情、海报图片)。- IMDb 官方数据集:适合大规模分析的最佳选择。2600 万+ 条目,每日更新,无需抓取。仅限个人/非商业用途。
- Thunderbit:适合不写代码的人,或者想快速拿到数据、又不想维护代码的人。AI 会自动适配布局变化,支持分页,并可导出到 Excel/Sheets/Airtable/Notion。
把这篇指南收藏起来——等 IMDb 下次改结构时我会更新它。如果你想完全跳过代码,试试 Thunderbit 免费版,看看从一个 IMDb 页面到一份干净表格能有多快。如果你还会处理其他网站,我们关于把网站数据抓到 Excel 里的指南会覆盖更完整的工作流。
试试适用于 IMDb 及更多场景的 AI 网页爬虫 Get Started Free
常见问题
抓取 IMDb 合法吗?
IMDb 的使用条款禁止未经许可的抓取,但在最近 2024 年的法院裁决后,对于公开可访问数据上的服务条款是否具有约束力,法律上仍有争议。最稳妥的选择是使用 IMDb 的官方免费数据集(仅限个人/非商业用途)或 TMDb API(免费 key)。如果你确实要抓取,请尊重 robots.txt,设置合理的请求间隔,并避免访问被屏蔽的路径。商业用途请咨询法律专业人士。
为什么我的 IMDb 爬虫返回空结果?
几乎总是因为过时的 CSS 选择器——像 td.titleColumn 和 td.ratingColumn 这样的类名自 2023 年 6 月起就已经不存在了。解决办法是改用 JSON-LD 提取(解析 <script type="application/ld+json"> 标签),或者把选择器更新为当前带 ipc- 前缀的类名。你还要确认自己加了正确的 User-Agent 请求头,因为缺少它会触发 403 错误,而这种错误看起来就像空结果。
如何抓取 IMDb 超过 25 条结果?
Top 250 页面会在一次响应里加载全部 250 部电影——不需要分页。对于搜索结果,请使用 start= URL 参数(每次增加 50)来翻页。例如:start=1、start=51、start=101。每次请求之间加上 time.sleep(3),避免被封。或者直接使用 datasets.imdbws.com 上的 IMDb 官方数据集,里面有 2600 万+ 条目,不需要分页。
什么是 __NEXT_DATA__,为什么要用它抓取 IMDb?
__NEXT_DATA__ 是一个嵌在 IMDb React/Next.js 页面 <script id="__NEXT_DATA__"> 标签里的 JSON 对象。它包含 React 用来渲染页面的完整结构化数据——片名、评分、演员表、类型、时长等。因为它代表的是底层数据模型,而不是视觉布局,所以比 CSS 选择器更能抵抗 UI 重设计。把它和 JSON-LD 一起使用,是最稳健的提取方式。
不写代码也能抓 IMDb 吗?
可以。主要有两种方式:(1)下载 IMDb 的官方批量数据集——7 个 TSV 文件,覆盖 2600 万+ 条目,每日更新,免费供非商业用途使用。(2)使用 Thunderbit 的 AI 网页爬虫,它会读取 IMDb 页面,自动建议提取字段,并且只需两步就能导出到 Excel、Google Sheets 或 CSV——不需要代码,也不需要维护选择器。
了解更多


