如果你最近搜过“使用 Python 抓取 IMDb”,你大概率已经发现一件事:你找到的大多数教程都已经不能用了。不是那种“有点过时”的坏,而是那种“返回 0 个结果,还一堆 NoneType 报错”的坏。
过去几周里,我测试了能找到的几乎所有主流 IMDb 抓取教程——GeeksforGeeks、Medium、freeCodeCamp、Kaggle 笔记本,能找的我都试了。在 里,绝大多数都还在引用 td.titleColumn、td.ratingColumn 这类 CSS 选择器,但自从 IMDb 在 2023 年 6 月重做 Top 250 页面后,这些选择器就已经不存在了。结果就是:论坛里一堆开发者在问“为什么我的代码返回空结果?”,而像 “除了把每个解析器都修一遍,也没什么别的办法。”这篇指南会介绍两种现在仍然有效的 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 年的基础论文已经累积了 ,而且这个数据集已经内置到 TensorFlow、Keras 和 PyTorch 里。在 Hugging Face 上,stanfordnlp/imdb 数据集每月下载量达到 213,321 次,已经被用于训练 1,500 多个模型。所以如果你做机器学习,大概率早就对 IMDb 数据很熟悉了。
但它的用途远不止学术研究:
| 用途 | 适合谁 | 需要的数据字段 |
|---|---|---|
| 电影推荐引擎 | 数据科学家、爱好者 | 片名、类型、评分、演员表 |
| 流媒体平台内容策略 | 产品/内容团队 | 评分、投票数、上映年份、类型 |
| 情感分析 / NLP 训练 | 机器学习研究者、学生 | 评论、评分 |
| 竞品内容分析 | 影视行业分析师 | 票房、上映日期、评分趋势 |
| 影视旅游研究 | 旅游局、旅行公司 | 拍摄地点、热度指标 |
| 学术研究 | 高校研究人员 | 任意结构化电影元数据 |
单是影视旅游市场,预计在 2025 年全球规模就达到 。Netflix 在 2024 年内容投入超过 170 亿美元,而 都由个性化推荐驱动。说到底: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 购买的话一年要 ,这对大多数人来说都不现实。
如果你根本不想写代码,可以读取 IMDb 页面,自动建议提取字段(片名、评分、年份、类型),并且只需两步就能导出到 Excel、Google Sheets、Airtable 或 Notion。IMDb 改版时,AI 会自动适配布局变化,所以你不用维护任何选择器。后面我会再详细说。
现在,如果你确实想写 Python——下面有两种真正能用的方法。
方法 1:使用 BeautifulSoup 抓取 IMDb(传统做法)
这是大多数教程里的经典方法。它能用,但我得先说明白:在我介绍的几种方法里,它是最脆弱的。IMDb 的 CSS 类名是自动生成的,重设计时就会变化。不过,它仍然是学习网页爬取基础知识的最佳方式。
第 1 步:安装并导入 Python 库
你需要四个包:
1pip install requests beautifulsoup4 pandas lxml
分别作用如下:
requests—— 发送 HTTP 请求,获取网页内容beautifulsoup4—— 解析 HTML,方便查找特定元素pandas—— 将提取的数据整理成表格,并导出为 CSV/Excellxml—— 快速 HTML 解析器(BeautifulSoup 可以把它当后端)
导入代码如下:
1import requests
2from bs4 import BeautifulSoup
3import pandas as pd
第 2 步:向 IMDb 发送 HTTP 请求
这一步通常就是新手第一次卡住的地方。IMDb 会拦截不带正确 User-Agent 请求头的请求——你会得到 403 Forbidden 错误。Python Requests 默认的 user-agent 字符串(python-requests/2.31.0)会立刻被标记。
1url = "https://www.imdb.com/chart/top/"
2headers = {
3 "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",
4 "Accept-Language": "en-US,en;q=0.9"
5}
6response = requests.get(url, headers=headers)
7if response.status_code != 200:
8 print(f"抓取页面失败:{response.status_code}")
9else:
10 print("页面抓取成功")
Accept-Language 请求头也很重要——没有它的话,IMDb 可能会根据你 IP 的地理位置返回不同语言的内容。
第 3 步:用 BeautifulSoup 解析 HTML
拿到 HTML 后,创建一个 BeautifulSoup 对象,然后开始寻找正确的元素。你可以在 Chrome 里打开 IMDb Top 250 页面,右键点击电影标题,选择“检查”,查看底层 HTML 结构。
1soup = 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 错误处理。我看过的竞品指南里几乎都没有这一步,也正因为如此,它们在选择器变化时会静默报错。
1movies = []
2movie_items = soup.select("li.ipc-metadata-list-summary-item")
3for item in movie_items:
4 try:
5 title_tag = item.select_one("h3.ipc-title__text")
6 title = title_tag.text.strip() if title_tag else "N/A"
7 year_tag = item.select_one("span.cli-title-metadata-item")
8 year = year_tag.text.strip() if year_tag else "N/A"
9 rating_tag = item.select_one("span.ipc-rating-star--rating")
10 rating = rating_tag.text.strip() if rating_tag else "N/A"
11 movies.append({
12 "title": title,
13 "year": year,
14 "rating": rating
15 })
16 except Exception as e:
17 print(f"解析电影时出错:{e}")
18 continue
19print(f"已提取 {len(movies)} 部电影")
第 5 步:用 Pandas 保存为 CSV 或 Excel
1df = pd.DataFrame(movies)
2df.to_csv("imdb_top_250.csv", index=False)
3df.to_excel("imdb_top_250.xlsx", index=False)
4print(df.head())
示例输出:
1 title year rating
20 1. The Shawshank Redemption 1994 9.3
31 2. The Godfather 1972 9.2
42 3. The Dark Knight 2008 9.0
53 4. The Godfather Part II 1974 9.0
64 5. 12 Angry Men 1957 9.0
这样是能跑的。但它依赖的是可能随时失效的 CSS 选择器——这就引出了我真正推荐的方法。
方法 2:JSON-LD 技巧——完全跳过 HTML 解析
这是没有任何竞品文章会讲的方法,也是我在任何认真项目里都会用的方法。IMDb 会在每个页面的 <script type="application/ld+json"> 标签里嵌入结构化数据,也就是 (面向链接数据的 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。
1import requests
2from bs4 import BeautifulSoup
3import json
4import pandas as pd
5url = "https://www.imdb.com/chart/top/"
6headers = {
7 "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",
8 "Accept-Language": "en-US,en;q=0.9"
9}
10response = requests.get(url, headers=headers)
11soup = BeautifulSoup(response.text, "lxml")
第 2 步:找到 JSON-LD 脚本标签
1script_tag = soup.find("script", {"type": "application/ld+json"})
2if not script_tag:
3 print("这个页面没有找到 JSON-LD")
4else:
5 data = json.loads(script_tag.string)
6 print(f"找到 JSON-LD,类型为:{data.get('@type', 'unknown')}")
第 3 步:解析结构化数据
在 Top 250 页面上,JSON-LD 包含一个 itemListElement 数组,里面有全部 250 部电影。每个条目都包括排名、名称、URL、aggregateRating、发布日期、类型、描述、导演和演员数组。
1movies = []
2for item in data.get("itemListElement", []):
3 movie = item.get("item", {})
4 rating_info = movie.get("aggregateRating", {})
5 movies.append({
6 "rank": item.get("position"),
7 "title": movie.get("name"),
8 "url": movie.get("url"),
9 "rating": rating_info.get("ratingValue"),
10 "vote_count": rating_info.get("ratingCount"),
11 "date_published": movie.get("datePublished"),
12 "genre": ", ".join(movie.get("genre", [])),
13 "description": movie.get("description"),
14 })
第 4 步:导出为 CSV
1df = pd.DataFrame(movies)
2df.to_csv("imdb_top_250_json_ld.csv", index=False)
3print(df.head())
示例输出:
1 rank title url rating vote_count date_published genre
20 1 The Shawshank Redemption https://www.imdb.com/title/tt0111161/ 9.3 2900000 1994-10-14 Drama
31 2 The Godfather https://www.imdb.com/title/tt0068646/ 9.2 2000000 1972-03-24 Crime, Drama
42 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 用来“水合”页面的数据——如果删掉它,网站就会坏。
1# 在类似 /title/tt0111161/ 这样的单个电影页面上
2next_data_tag = soup.find("script", {"id": "__NEXT_DATA__"})
3if next_data_tag:
4 next_data = json.loads(next_data_tag.string)
5 above_fold = next_data["props"]["pageProps"]["aboveTheFoldData"]
6 title = above_fold["titleText"]["text"]
7 year = above_fold["releaseYear"]["year"]
8 rating = above_fold["ratingsSummary"]["aggregateRating"]
9 runtime_seconds = above_fold.get("runtime", {}).get("seconds", 0)
10 genres = [g["text"] for g in above_fold["genres"]["genres"]]
11 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 使用 来检测并拦截机器人。最常见的触发原因是缺少或明显伪造的 User-Agent 请求头。这个问题在 开源项目以及 里都有记录,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)
- 长期修复:如果要大规模分析,直接使用 ,或者用像 这样每次都会用 AI 重新读取页面结构的工具——不用维护选择器,AI 会自动适配布局变化
超越 25 条结果限制:IMDb 分页与大数据集抓取
我看过的每一个竞品教程都只抓一页。没有人讲分页。但如果你要的不只是一份单页列表,很快就会撞墙。
不需要分页的页面
好消息是:Top 250 页面会在一次服务器渲染响应里加载全部 250 部电影。JSON-LD 和 __NEXT_DATA__ 都包含完整数据集。不需要分页。
IMDb 搜索分页是怎么工作的
IMDb 搜索页通过 start= URL 参数分页,每次增加 50:
1https://www.imdb.com/search/title/?groups=top_1000&start=1
2https://www.imdb.com/search/title/?groups=top_1000&start=51
3https://www.imdb.com/search/title/?groups=top_1000&start=101
下面是一段遍历结果的 Python 循环:
1import time
2all_movies = []
3for start in range(1, 1001, 50): # 遍历 top 1000
4 url = f"https://www.imdb.com/search/title/?groups=top_1000&start={start}"
5 response = requests.get(url, headers=headers)
6 if response.status_code != 200:
7 print(f"在 start={start} 处失败:{response.status_code}")
8 break
9 soup = BeautifulSoup(response.text, "lxml")
10 # 用你喜欢的方法提取电影
11 # ...
12 print(f"已抓取起始于 {start} 的页面")
13 time.sleep(3) # 注意礼貌——IMDb 大约在 50 次快速请求后会开始封禁
这里的 time.sleep(3) 很重要。社区反馈显示,IMDb 在大约 50 次快速请求后就会开始封 IP。建议使用 2–5 秒之间的随机延迟。
什么时候可以完全不抓:IMDb 官方批量数据集
如果你真的需要大规模数据,IMDb 提供了 7 个免费的 TSV 文件,可在 获取,并且每天更新:
| 文件 | 内容 | 大小 |
|---|---|---|
| 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 很简单:
1ratings = pd.read_csv("title.ratings.tsv.gz", sep="\t", compression="gzip")
2basics = pd.read_csv("title.basics.tsv.gz", sep="\t", compression="gzip", low_memory=False)
3# 按 tconst(IMDb 条目 ID)合并
4merged = basics.merge(ratings, on="tconst")
5top_movies = merged[merged["titleType"] == "movie"].nlargest(250, "averageRating")
这些数据集覆盖 2,600 多万条目。没有分页、没有选择器、没有 403 错误。许可证仅限个人和非商业用途——你不能重新发布或转售这些数据。
无代码捷径:Thunderbit 帮你处理分页
如果你需要带分页的 IMDb 数据,但又不想自己写分页逻辑, 原生支持点击分页和无限滚动。你只要告诉它抓取什么,剩下的它会自己处理——包括滚动加载内容。
使用 Python 抓取 IMDb:完整可运行代码(可直接复制)
下面是两段可直接运行的独立脚本。
脚本 A:BeautifulSoup 方法(CSS 选择器)
1import requests
2from bs4 import BeautifulSoup
3import pandas as pd
4url = "https://www.imdb.com/chart/top/"
5headers = {
6 "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",
7 "Accept-Language": "en-US,en;q=0.9"
8}
9response = requests.get(url, headers=headers)
10if response.status_code != 200:
11 print(f"错误:{response.status_code}")
12 exit()
13soup = BeautifulSoup(response.text, "lxml")
14movie_items = soup.select("li.ipc-metadata-list-summary-item")
15movies = []
16for item in movie_items:
17 try:
18 title = item.select_one("h3.ipc-title__text")
19 year = item.select_one("span.cli-title-metadata-item")
20 rating = item.select_one("span.ipc-rating-star--rating")
21 movies.append({
22 "title": title.text.strip() if title else "N/A",
23 "year": year.text.strip() if year else "N/A",
24 "rating": rating.text.strip() if rating else "N/A",
25 })
26 except Exception as e:
27 print(f"因错误跳过电影:{e}")
28df = pd.DataFrame(movies)
29df.to_csv("imdb_top250_bs4.csv", index=False)
30print(f"已保存 {len(df)} 部电影")
31print(df.head())
脚本 B:JSON-LD 方法(推荐)
1import requests
2from bs4 import BeautifulSoup
3import json
4import pandas as pd
5url = "https://www.imdb.com/chart/top/"
6headers = {
7 "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",
8 "Accept-Language": "en-US,en;q=0.9"
9}
10response = requests.get(url, headers=headers)
11if response.status_code != 200:
12 print(f"错误:{response.status_code}")
13 exit()
14soup = BeautifulSoup(response.text, "lxml")
15script_tag = soup.find("script", {"type": "application/ld+json"})
16if not script_tag:
17 print("没有找到 JSON-LD 数据")
18 exit()
19data = json.loads(script_tag.string)
20movies = []
21for item in data.get("itemListElement", []):
22 movie = item.get("item", {})
23 rating_info = movie.get("aggregateRating", {})
24 directors = movie.get("director", [])
25 director_names = ", ".join(
26 d.get("name", "") for d in (directors if isinstance(directors, list) else [directors])
27 )
28 movies.append({
29 "rank": item.get("position"),
30 "title": movie.get("name"),
31 "url": movie.get("url"),
32 "rating": rating_info.get("ratingValue"),
33 "votes": rating_info.get("ratingCount"),
34 "year": movie.get("datePublished", "")[:4],
35 "genre": ", ".join(movie.get("genre", [])),
36 "director": director_names,
37 "description": movie.get("description"),
38 })
39df = pd.DataFrame(movies)
40df.to_csv("imdb_top250_jsonld.csv", index=False)
41print(f"已保存 {len(df)} 部电影")
42print(df.head())
这两段脚本都包含错误处理,并能输出干净的 CSV。脚本 B 能给你更丰富的数据——导演、描述、URL——而且对布局变化更有韧性。
不写代码也能抓 IMDb(用 Thunderbit)
不是每个人都需要或想写 Python。也许你只是一个运营分析师,只想把本周评分最高的电影导到表格里。也许你是内容策略人员,想比较不同年份的类型趋势。在这些场景下,自己写爬虫就太大材小用了。
下面是用 获取同样数据的方法:
开始前:
- 难度:初学者
- 所需时间:约 2 分钟
- 你需要准备: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 还支持子页面抓取——你可以点进每部电影的详情页,把演员、导演、时长以及列表页看不到的其他字段补充进表格里。如果你想看实际效果,可以查看 。
抓取 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 万+ 条目,每日更新,无需抓取。仅限个人/非商业用途。
- :适合不写代码的人,或者想快速拿到数据、又不想维护代码的人。AI 会自动适配布局变化,支持分页,并可导出到 Excel/Sheets/Airtable/Notion。
把这篇指南收藏起来——等 IMDb 下次改结构时我会更新它。如果你想完全跳过代码,,看看从一个 IMDb 页面到一份干净表格能有多快。如果你还会处理其他网站,我们关于的指南会覆盖更完整的工作流。
常见问题
抓取 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),避免被封。或者直接使用 上的 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)使用 ,它会读取 IMDb 页面,自动建议提取字段,并且只需两步就能导出到 Excel、Google Sheets 或 CSV——不需要代码,也不需要维护选择器。
了解更多
