如果您最近搜尋過「使用 Python 抓取 IMDb」,大概已經發現一件事:大多數找到的教學都壞了。不是那種「稍微過時」的壞,而是更像「回傳零結果,還冒出一整片 NoneType 錯誤」的壞。
過去幾週,我測試了我能找到的所有主流 IMDb 抓取教學——GeeksforGeeks、Medium、freeCodeCamp、Kaggle notebooks,想到的我都試了。在 214+ 個公開 GitHub repository 中,絕大多數都還在引用 td.titleColumn、td.ratingColumn 這些 CSS selector,但它們自從 2023 年 6 月 IMDb 重新設計 Top 250 頁面後就不存在了。結果呢?論壇裡滿是開發者在問「為什麼我的程式回傳空值?」;而像 Cinemagoer 這類熱門函式庫的維護者也坦承:「除了把每個 parser 都修一遍,我們也沒太多辦法。」本指南會介紹兩種目前真正可用的 Python 方法、如何處理分頁和常見錯誤、什麼時候 Python 其實不是最佳工具,以及如何讓你的爬蟲更耐用,避免很快就進入廢墟名單。
用 Python 抓取 IMDb 是什麼意思?
網頁爬蟲是指以程式方式從網頁擷取資料——不必手動複製貼上,而是寫一段程式替您完成。當我們說「抓取 IMDb」時,指的是用 Python 從 IMDb 的網頁中擷取結構化電影資料(片名、評分、類型、演員、片長、投票數等)。
Python 常見的工具組合通常有三個函式庫:requests(用來取得網頁)、BeautifulSoup(解析 HTML 並找出資料)、pandas(整理與匯出結果)。有些教學也會用 Selenium 或 Playwright 來處理需要 JavaScript 渲染的頁面,但您會看到,其實還有更快的方法。
有一個重要提醒:本指南中的所有內容都已依照 IMDb 目前的頁面結構驗證,截至 2025 年中仍可使用。IMDb 大約每 6–12 個月就會變動一次,因此如果您是在 2027 年閱讀這篇文章,部分 selector 可能已經改了。(我也會說明怎麼處理這種情況。)
為什麼要用 Python 抓取 IMDb?實際應用場景有哪些?
在寫任何一行程式之前,先想想:您實際會拿 IMDb 資料做什麼?答案取決於您的角色。
IMDb 評論資料集是全球最常用的 NLP 基準資料集之一——Maas 等人 2011 年的基礎論文已累積 5,706 次引用,而且這份資料集已內建於 TensorFlow、Keras 和 PyTorch。Hugging Face 上的 stanfordnlp/imdb 資料集每月下載量達 213,321 次,並被用來訓練超過 1,500 個模型。所以如果您在做機器學習,應該對 IMDb 資料不陌生。
但它的用途遠不只學術研究:
| 用途 | 適合對象 | 需要的資料欄位 |
|---|---|---|
| 電影推薦引擎 | 資料科學家、愛好者 | 片名、類型、評分、演員 |
| 串流平台內容策略 | 產品/內容團隊 | 評分、投票數、上映年份、類型 |
| 情感分析/NLP 訓練 | ML 研究人員、學生 | 評論、評分 |
| 競品內容分析 | 娛樂產業分析師 | 票房、上映日期、評分趨勢 |
| 影視觀光研究 | 觀光局、旅遊公司 | 拍攝地點、熱度指標 |
| 學術研究 | 大學研究人員 | 任何結構化電影中繼資料 |
光是影視觀光市場在 2025 年全球估值就達 $660 億美元。Netflix 在 2024 年就投入超過 170 億美元製作內容,且 80% 的觀看行為 都是由個人化推薦驅動。重點是:IMDb 資料正在支持各產業的真實決策。
取得 IMDb 資料的選項:在寫程式前先看這裡
這一段通常是大多數教學完全跳過的。他們直接叫您 pip install beautifulsoup4,卻沒有先問:在您的情境下,Python 抓取真的最適合嗎?
完整選項如下:
| 方式 | 最適合 | 優點 | 缺點 |
|---|---|---|---|
| Python + BeautifulSoup | 學習、客製化擷取 | 完全掌控、彈性高 | selector 脆弱、常常壞掉 |
JSON-LD / __NEXT_DATA__ 擷取 | 想要穩定性的開發者 | 可處理 JS 內容、韌性更高 | 需要理解 JSON 結構 |
| IMDb 官方資料集 | 大規模分析、學術用途 | 合法、完整、2600 萬+ 標題、每日更新 | TSV 格式、沒有評論/圖片 |
| Cinemagoer(IMDbPY)函式庫 | 逐筆查詢單一標題 | Python 風格 API、欄位豐富 | 88 個未解問題,最後一次發版是 2023 年 5 月 |
| TMDb API | 電影中繼資料 + 圖片 | 免費 API 金鑰、JSON、文件完整 | 來源不同(不是 IMDb 評分) |
| Thunderbit(無程式碼) | 不寫程式的人、快速匯出 | 2 步驟抓取、AI 自動建議欄位、可匯出到 Excel/Sheets | 大量抓取需使用點數制 |
這些選項有幾點要注意。Cinemagoer 自 2023 年 5 月後就沒有 PyPI 發版,而且 IMDb 在 2025 年 6 月重新設計頁面後,它的大多數 parser 都壞了——我目前不建議拿它直接上線生產環境。TMDb 很不錯,但它使用的是自己的評分系統,不是 IMDb 的。至於 IMDb 的官方企業 API,透過 AWS Data Exchange 每年要價 $50,000 以上,所以對我們大多數人來說並不實際。
如果您完全不想寫程式,Thunderbit 的人工智慧網頁爬蟲 可以直接讀取 IMDb 頁面,自動建議擷取欄位(片名、評分、年份、類型),並在兩次點擊內匯出到 Excel、Google Sheets、Airtable 或 Notion。當 IMDb 改版時,AI 也會自動適應,所以不需要維護任何 selector。後面我會再詳細說明。
現在,對於真的想寫 Python 的人——下面是兩種可用的方法。
方法 1:使用 BeautifulSoup 用 Python 抓取 IMDb(傳統作法)
這是大多數教學裡最常見的經典做法。它能用,但我先講清楚:在我介紹的方法裡,這是最脆弱的一種。IMDb 的 CSS class 名稱是自動產生的,頁面改版時就會變。話雖如此,它仍然是學習網頁爬蟲基礎最好的方式。
步驟 1:安裝並匯入 Python 函式庫
您需要四個套件:
pip install requests beautifulsoup4 pandas lxml
各自的用途如下:
requests— 發送 HTTP 請求,取得網頁內容beautifulsoup4— 解析 HTML,方便您搜尋特定元素pandas— 將擷取到的資料整理成表格,並匯出為 CSV/Excellxml— 速度很快的 HTML parser(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 頁面使用以下 selector:
- 電影容器:
li.ipc-metadata-list-summary-item - 標題:
h3.ipc-title__text - 年份:
span.cli-title-metadata-item(第一個 span) - 評分:
span.ipc-rating-star--rating
提醒一下:那些以 ipc- 開頭的 class 名稱是 IMDb 元件系統自動產生的。它們自 2023 年 6 月改版後一直很穩定,但不保證未來不會再變。
步驟 4:擷取電影資料(標題、年份、評分)
這裡我和多數教學不一樣:我加入了 try/except 錯誤處理。我看過的競品教學幾乎都沒有這個步驟,這也是為什麼 selector 一改,他們的程式就默默壞掉。
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 selector 撐著——這也正是我真正推薦的下一種方法。
方法 2:JSON-LD 技巧——完全跳過 HTML 解析
這是沒有競品文章提到的方法,也是我在任何正式專案中會採用的方式。IMDb 會在每個頁面的 <script type="application/ld+json"> 標籤中嵌入結構化資料,也就是 JSON-LD(Linked Data 的 JavaScript Object Notation)。這些資料遵循 Schema.org 標準,Google 也用它來產生豐富搜尋結果,而且變動頻率遠低於 CSS class 名稱。
Apify 的 IMDb Scraper 是一個可上線使用的工具,它採用的擷取優先順序是:「JSON-LD > NEXT_DATA > DOM」。我也會建議您用這個順序。
為什麼 JSON-LD 比 CSS selector 更可靠
| 方法 | 可處理 JS 內容? | 對 UI 變動的韌性? | 速度 | 複雜度 |
|---|---|---|---|---|
| BeautifulSoup + CSS selector | ❌ 否 | ⚠️ 脆弱(class 名稱會變) | 快 | 低 |
| JSON-LD 擷取 | ✅ 是 | ✅ 遵循 Schema.org 標準 | 快 | 低-中 |
__NEXT_DATA__ JSON 擷取 | ✅ 是 | ✅ 相當穩定 | 快 | 低-中 |
| Selenium / Playwright | ✅ 是 | ⚠️ 脆弱 | 慢 | 中-高 |
| Thunderbit(無程式碼,2 步驟) | ✅ 是(AI 讀取頁面) | ✅ AI 會自動適應 | 快 | 無 |
像 ipc-metadata-list-summary-item 這類 CSS class 名稱,是 IMDb React 元件系統自動產生的,每次改版都可能變。JSON-LD schema 表示的是實際資料模型,而不是呈現層。就像您是看一本書的目錄,還是只靠字體大小去猜哪一章是什麼,差別非常大。

逐步操作:從 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 標籤
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、datePublished、genre、description、director 與 actor 陣列。
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 selector 糾纏。更重要的是,這份資料遵循 Schema.org 標準(Google 也依賴它來產生搜尋結果),所以比視覺版面穩定得多。
加碼:單一電影頁面的 __NEXT_DATA__
如果您想從單一標題頁取得更豐富的資料(片長、完整演員表、劇情摘要、海報圖片),IMDb 也會嵌入 __NEXT_DATA__ JSON 物件。這是 React 用來 hydrate 頁面的資料——如果移除它,網站就會壞掉。
# 在單一電影頁,例如 /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 selector |
| 2023 年 4 月 | 標題子頁改版 | Biography、awards、news 抓取器 |
| 2023 年 10 月 | 進階搜尋改版 | 以搜尋頁為基礎的抓取器 |
| 2025 年 6 月 | /reference 頁面改版 | Cinemagoer 函式庫(大多數 parser) |
大約每 6–12 個月就會有一次重大破壞性變更。如果您的爬蟲依賴 CSS class 名稱,那您基本上就是在跑步機上追著問題跑。
常見錯誤與修正方式
空結果/NoneType 錯誤
這是最常見的錯誤。您會看到 AttributeError: 'NoneType' object has no attribute 'text'。這代表 BeautifulSoup 沒找到您要的元素——通常是 CSS class 名稱變了,或內容是由 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 的搜尋頁和「Most Popular」列表會使用 lazy loading——一開始只渲染約 25 筆結果,接著隨著您捲動,再透過 AJAX 載入更多。
修正方式:使用 URL 參數分頁(下一節會講),或改抓 Top 250 頁面,因為它會在單次回應中載入全部 250 部電影。
selector 突然失效
以下是已經失效的舊 selector:td.titleColumn、td.ratingColumn、.lister-item-header、.inline-block.ratings-imdb-rating。如果您的程式用了這些,現在就是壞的。
修正方式:優先使用 data-testid 屬性(例如 h1[data-testid="hero-title-block__title"]),不要再靠自動產生的 class 名稱。更好的做法是直接用 JSON-LD。
決策框架:短期修法 vs 長期修法
- 快速修法:在每個 selector 外面加上
try/except、驗證 HTTP 狀態碼、記錄錯誤而不是直接崩潰 - 中期修法:從 CSS selector 改成 JSON-LD 擷取(方法 2)
- 長期修法:大規模分析時,使用 IMDb 官方批次資料集,或像 Thunderbit 這樣每次都用 AI 重新讀取頁面結構的工具——不需要維護 selector,AI 會自動適應版面變動
超越 25 筆限制:IMDb 分頁與大型資料集抓取
我看過的每一篇競品教學都只抓單一頁面。沒有人講分頁。但如果您需要的不只是一個清單,很快就會撞牆。
不需要分頁的頁面
好消息是:Top 250 頁面會在單次 server-rendered 回應中載入全部 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 title ID)合併
merged = basics.merge(ratings, on="tconst")
top_movies = merged[merged["titleType"] == "movie"].nlargest(250, "averageRating")
這些資料集涵蓋 2,600 萬以上的標題。不用分頁、不用 selector,也沒有 403 錯誤。授權僅限個人與非商業用途——不能重新發布或轉售這些資料。
無程式碼捷徑:Thunderbit 幫您處理分頁
如果您需要有分頁的 IMDb 資料,但又不想自己寫分頁邏輯,Thunderbit 原生支援點擊式分頁與無限捲動。您只要告訴它要抓什麼,其他都交給它——包含處理 lazy-loaded 內容。
用 Python 抓取 IMDb:完整可用程式碼(可直接複製貼上)
下面有兩個可直接執行的完整腳本。
腳本 A:BeautifulSoup 方法(CSS selector)
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 Suggest Fields」。 AI 會掃描頁面並推薦欄位——通常會是標題、年份、評分、類型,以及其他幾個欄位,視頁面而定。您會看到一個預覽表格,裡面列出建議的欄位。
步驟 3:需要的話調整欄位。 移除不需要的欄位,或點擊「+ Add Column」並用白話英文描述您想要的內容(例如「Director name」或「Number of votes」)。
步驟 4:點擊「Scrape」。 Thunderbit 就會擷取資料。若頁面有無限捲動或分頁,它也會自動處理。
步驟 5:匯出。 點擊匯出按鈕並選擇格式——Excel、Google Sheets、CSV、Airtable 或 Notion。資料會在幾秒內送到您的目的地。
這裡真正的優勢不只是方便——而是 Thunderbit 的 AI 每次都會重新讀取頁面結構。當 IMDb 改版時(而且它一定會改),AI 也會跟著適應。不需要更新 selector,也不需要修程式。對任何曾在截止日前的凌晨兩點被壞掉的爬蟲折磨過的人來說,這非常值得。
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 endpoint。它也沒有指定 Crawl-delay。
IMDb 的使用條款:相關條款寫道:「除非獲得我們明確的書面同意,否則你不得在本網站上使用資料探勘、機器人、畫面抓取或類似的資料收集與擷取工具。」 另一條則禁止轉售或商業使用抓取資料。
實務上代表什麼:近期 2024 年的法院判決(Meta v. Bright Data、X Corp v. Bright Data)認為,若使用者從未同意服務條款,那麼條款未必一定能約束使用公開資料的人——如果您是在未登入狀態下抓取公開可見內容,條款的可執行性其實有爭議。但這仍然是一個持續演變中的法律領域。
較安全的替代方案:IMDb 的 官方可下載資料集 明確允許個人與非商業用途。TMDb API 只要免費 API key 也相當寬鬆。如果您想明確站在安全一側,這兩者都是很好的選項。
實務建議:如果您真的要抓,請使用尊重對方伺服器的抓取頻率(每次請求間隔 time.sleep(3))、設定正確的標頭,並避免碰觸被 robots.txt 封鎖的路徑。若是商業專案,請諮詢法律專業人士,或直接使用官方資料集/API。
我們也在 Thunderbit 部落格上深入討論過 網頁爬蟲的法律影響。
結論:選擇最適合的 IMDb Python 抓取方式
簡短總結:
- BeautifulSoup + CSS selector:適合學習基礎。預期它每 6–12 個月就會壞一次。一定要加上錯誤處理。
- JSON-LD 擷取:如果您要長期維護 Python 專案,我最推薦這個方法。它遵循 Schema.org 標準,比 CSS class 變動少得多,而且不需要 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 selector——像 td.titleColumn 和 td.ratingColumn 這些 class 自 2023 年 6 月後就不存在了。解法是改用 JSON-LD 擷取(解析 <script type="application/ld+json"> 標籤),或將 selector 更新為目前以 ipc- 開頭的 class。同時也要確認您有加上正確的 User-Agent 標頭,因為缺少該標頭時會觸發 403 錯誤,看起來就像空結果。
我要怎麼抓超過 25 筆 IMDb 結果?
Top 250 頁面會在單次回應中載入全部 250 部電影——不需要分頁。搜尋結果則可以使用 start= URL 參數(每次增加 50)逐頁抓取。例如:start=1、start=51、start=101。請在每次請求之間加入 time.sleep(3),避免被封鎖。或者,您也可以直接使用 IMDb 官方資料集 datasets.imdbws.com,其中包含 2600 萬+ 標題,不需要分頁。
什麼是 __NEXT_DATA__,為什麼要用它抓 IMDb?
__NEXT_DATA__ 是 IMDb React/Next.js 頁面中嵌在 <script id="__NEXT_DATA__"> 標籤裡的一個 JSON 物件。它包含 React 用來渲染頁面的完整結構化資料——標題、評分、演員、類型、片長等等。因為它代表的是底層資料模型,而不是視覺版面,所以比 CSS selector 更能抵抗 UI 改版。將它和 JSON-LD 搭配使用,是最穩健的擷取方式。
可以不寫程式就抓 IMDb 嗎?
可以。主要有兩種方式:(1) 下載 IMDb 的 官方批次資料集——7 個 TSV 檔,涵蓋 2600 萬+ 標題,每日更新,供非商業用途免費使用。(2) 使用 Thunderbit 的人工智慧網頁爬蟲,它會讀取 IMDb 頁面、自動建議擷取欄位,並在兩次點擊內匯出到 Excel、Google Sheets 或 CSV——不用寫程式,也不用維護 selector。
延伸閱讀


