PythonでIMDbをスクレイピングする方法:本当に動くコード

最終更新日 June 3, 2026
PythonでIMDbをスクレイピングする方法:本当に動くコード

「Python IMDb スクレイピング」で検索してチュートリアルをいくつか開いてみた人なら、おそらく同じ壁にぶつかったはずです。出てくるサンプルコードのほとんどが、もはや機能しません。微妙に古いという話ではなく、走らせると結果ゼロ件、ターミナルには NoneType のエラーが延々と流れる、という意味で完全に死んでいます。

私自身、この数週間で主要なIMDbスクレイピング記事をひととおり実際に動かしてみました。GeeksforGeeks、Medium、freeCodeCamp、Kaggleの公開ノートブック——どれもです。IMDb向けとして公開されている214件以上のGitHubリポジトリを見ても、その多くが td.titleColumntd.ratingColumn といったCSSセレクタを前提にしています。ところがこれらは、IMDbがTop 250ページを刷新した2023年6月を境に消えてしまいました。結果として、フォーラムには「なぜ空のリストしか返らないのか」という質問が積み上がり、Cinemagoerのような有名ライブラリのメンテナー本人ですら「パーサーを全部書き直すしか手がない」とこぼしているありさまです。本稿では、2025年現在もきちんと動く2つのPython手法、ページ送りとエラーの実際的な対処、Pythonを選ぶべきでない場面、そして将来の仕様変更にも耐えやすいスクレイパーの設計まで、順を追って整理していきます。

PythonでIMDbをスクレイピングするとはどういうことか?

ウェブスクレイピングとは、Webページ上のデータをプログラムで自動的に取り出す作業のことです。手でコピー&ペーストする代わりに、スクリプトに代行させるわけです。「IMDbをスクレイピングする」と言うときは、Pythonを使ってIMDbのページから、映画タイトル、評価、ジャンル、出演者、上映時間、投票数といった構造化データを抜き出すことを指します。

Pythonでの定番の組み合わせは、3つのライブラリで成り立っています。Webページを取得する requests、HTMLを読み解いて目的の要素を探す BeautifulSoup、そして結果を表として整える pandas です。JavaScriptで描画されるページに対して SeleniumPlaywright を持ち出すチュートリアルもありますが、後述するようにもっと手軽な道があります。

ひとつ前提を確認しておきます。本稿のコードはすべて、2025年半ば時点のIMDbの構造で動作を確認しています。IMDbはおおむね6〜12か月のサイクルで仕様を変えるので、もしあなたがこれを2027年に読んでいるなら、一部のセレクタはすでに別物になっているかもしれません。(その場合の対処も後でまとめます。)

なぜPythonでIMDbをスクレイピングするのか?実用例

コードに手をつける前に、IMDbのデータが実際に何の役に立つのかを押さえておきましょう。答えは、あなたの立場によって変わってきます。

IMDbのレビューデータセットは、自然言語処理の世界で最も広く使われているベンチマークのひとつです。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公式データセット大規模分析、学術用途合法、完全、2,600万件以上のタイトル、毎日更新TSV形式、レビュー/画像なし
Cinemagoer(IMDbPY)ライブラリタイトルごとのプログラム取得PythonらしいAPI、豊富な項目88件の未解決Issue、最終リリースは2023年5月
TMDb API映画メタデータ+画像無料APIキー、JSON、ドキュメントが充実情報源がIMDbではない(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へ2クリックで書き出せます。レイアウトが変わってもAIがそのつど追従するため、セレクタを保守する必要はありません。この点はあとで詳しく扱います。

それではここから、Pythonで手を動かしたい人に向けて、実際に動く2つの手法を見ていきましょう。

方法1:BeautifulSoupを使ってPythonでIMDbをスクレイピングする(従来型)

多くのチュートリアルが取り上げる王道の方法です。動くことは動きますが、率直に言って、本稿で紹介する中では最も脆い手法でもあります。IMDbのCSSクラス名は自動生成されるので、刷新のたびに別物へ変わってしまうからです。ただ、スクレイピングの基本を体で覚えるには適しています。

ステップ1:Pythonライブラリをインストールして読み込む

必要なのは4つのパッケージです。

pip install requests beautifulsoup4 pandas lxml

それぞれの役割は次のとおりです。

  • requests — HTTPリクエストを送ってWebページを取得する
  • beautifulsoup4 — HTMLを解析し、特定の要素を検索できるようにする
  • pandas — 抽出したデータを表にまとめ、CSV/Excelへ出力する
  • lxml — 高速なHTMLパーサー(BeautifulSoupのバックエンドとして使える)

インポート部分はこちらです。

import requests
from bs4 import BeautifulSoup
import pandas as pd

ステップ2:IMDbにHTTPリクエストを送る

初心者が最初につまずきやすいのがここです。IMDbは、まともな User-Agent ヘッダーのないリクエストを弾きます。403 Forbiddenが返ってくるのです。Python Requestsが既定で送る文字列(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セレクタ1本だけ。いつ崩れてもおかしくありません。そこで次に、私が本当におすすめしたい方法を紹介します。

方法2:JSON-LDを使う方法 — HTML解析を丸ごと飛ばす

これは競合記事のどれもが触れていない手法で、本気のプロジェクトであれば私はこちらを選びます。IMDbは各ページの <script type="application/ld+json"> タグの中に、JSON-LD(Linked Data向けのJavaScript Object Notation)という形で構造化データを埋め込んでいます。これはSchema.org標準に沿ったもので、Googleのリッチ検索結果にも利用されており、CSSクラス名よりはるかに変わりにくいのが強みです。

ApifyのIMDbスクレイパーのような実運用ツールでも、抽出の優先順位は「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 のようなクラス名は、IMDbのReactコンポーネントによって自動で振られ、刷新のたびに入れ替わります。一方JSON-LDのスキーマが表すのは、見た目の層ではなく実データのモデルそのものです。本の目次を見て章を把握するのと、文字の大きさだけで章の切れ目を当てようとするのとの違い、と言えばイメージしやすいでしょうか。

css-selectors-vs-json-ld.webp

手順: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には、250本すべての映画を収めた itemListElement 配列が入っています。各要素には、順位、名前、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セレクタを微調整する手間もありません。しかも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月Name Pagesを再設計旧nameページ用スクレイパー
2023年6月Top 250ページを再設計すべての td.titleColumn / td.ratingColumn セレクタ
2023年4月タイトル下層ページを再設計biography、受賞歴、ニュース用スクレイパー
2023年10月Advanced Searchを再設計検索ベースのスクレイパー
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 ヘッダーが欠けている、あるいは見るからに不自然なことです。これは複数のOSSプロジェクトでも、IMDb社員自身が問題を認めたIMDb公式コミュニティフォーラムでも報告されています。

対処法: 現実的なブラウザの User-Agent 文字列と Accept-Language: en-US ヘッダーを欠かさず付けます。接続を使い回すために requests.Session() を併用しましょう。

25件しか返らない

IMDbの検索ページや「Most Popular」リストは遅延読み込みを採用しています。最初は約25件だけ描画し、スクロールに応じてAJAXで残りを追加していくのです。

対処法: URLパラメータでページ送りする(次のセクションで解説)か、250件すべてが一度のレスポンスに載るTop 250ページへ切り替えます。

セレクタが突然動かなくなる

すでに無効になった古いセレクタには、td.titleColumntd.ratingColumn.lister-item-header.inline-block.ratings-imdb-rating などがあります。これらを使っているコードはもう機能しません。

対処法: 自動生成のクラス名よりも、data-testid 属性(たとえば h1[data-testid="hero-title-block__title"])を優先します。さらに望ましいのはJSON-LDを使うことです。

判断のフレームワーク:短期修正と長期修正

  • 短期修正: 各セレクタを try/except で囲み、HTTPステータスコードを確認し、クラッシュせずにエラーをログに残す
  • 中期修正: CSSセレクタからJSON-LD抽出へ切り替える(方法2)
  • 長期修正: 大規模分析にはIMDb公式の一括データセットを使うか、ページ構造を毎回AIが読み直すThunderbitのようなツールを使う。セレクタの保守は不要で、AIがレイアウト変更に自動対応します

25件の壁を超える:IMDbのページネーションと大規模データセットのスクレイピング

私が調べた競合チュートリアルは、判で押したように1ページしか取得していませんでした。ページ送りの説明がまるでないのです。しかし単一の一覧を超えてデータが欲しくなった途端、この壁にぶつかります。

ページネーションが不要なページ

ここはひと安心です。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={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.gzTVエピソード情報約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 がクリック式ページネーションと無限スクロールの両方にそのまま対応しています。取得したいページを指定するだけで、あとは任せきりにできます。遅延読み込みのコンテンツをスクロールしながら拾っていくのも自動です。

ページネーション付きIMDbデータにThunderbitを試す

PythonでIMDbをスクレイピングする:完全に動くコード(コピペ可)

そのまま実行できる、自己完結型のスクリプトを2本用意しました。

スクリプト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 で同じデータを取得する流れは、次のとおりです。

始める前に:

ステップ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ページ(/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も、無料APIキーで柔軟に扱えます。グレーゾーンを避けたいなら、どちらも有力です。

実践的な指針: スクレイピングする場合は、配慮あるクロール速度(リクエスト間に time.sleep(3))、適切なヘッダー設定、robots.txtで禁止されたパスを叩かないこと、この3点を守りましょう。商用プロジェクトなら、法務の専門家に相談するか、公式データセット/APIを使うのが堅実です。

Thunderbitブログでは、ウェブスクレイピングの法的論点をさらに掘り下げて解説しています。

結論:PythonでIMDbをスクレイピングする正しい方法を選ぶ

要点を整理すると、次のようになります。

  • BeautifulSoup + CSSセレクタ: 基礎を学ぶのに向いています。6〜12か月ごとに壊れる前提で使いましょう。必ずエラー処理を入れてください。
  • JSON-LD抽出: 継続的に使うPythonプロジェクトなら、私ならこれを推します。Schema.org標準に従い、CSSクラスよりずっと変わりにくく、JavaScriptレンダリングなしで整った構造化データが得られます。
  • __NEXT_DATA__ JSON: 個別タイトルページのより豊富なデータ(上映時間、フルキャスト、あらすじ、ポスター画像)の補助として使います。
  • IMDb公式データセット: 大規模分析に最適です。2,600万件以上のタイトル、毎日更新、スクレイピング不要。個人・非商用利用のみ。
  • Thunderbit: コーディング不要で、保守なしで素早くデータが欲しい人に最適です。AIがレイアウト変更に追従し、ページネーションにも対応し、Excel/Sheets/Airtable/Notionへ出力できます。

このガイドはブックマークしておいてください。IMDbの構造が次に変わったら更新します。コードをまったく使いたくないなら、Thunderbitの無料プランを試してみてください。IMDbページからきれいなスプレッドシートまで、どれだけ速く進めるか実感できるはずです。他のサイトも扱うなら、WebサイトのデータをExcelにスクレイピングする方法も、より広いワークフローをカバーしています。

IMDbなどに使えるAIウェブスクレイパーを試す Get Started Free

FAQ

IMDbをスクレイピングするのは合法ですか?

IMDbの利用規約では、同意なしのスクレイピングは禁止されています。ただし、2024年の最近の判例以降、公開データに対する利用規約の拘束力は法的に争点になっています。最も安全なのは、IMDbの公式無料データセット(個人・非商用利用)かTMDb API(無料キー)です。スクレイピングする場合は、robots.txtを尊重し、リクエスト間に十分な遅延を入れ、ブロックされたパスは避けてください。商用利用では法務の専門家に相談しましょう。

IMDbスクレイパーが空の結果を返すのはなぜですか?

ほとんどの場合、原因は古いCSSセレクタです。td.titleColumntd.ratingColumn のようなクラス名は、2023年6月以降存在していません。解決策は、JSON-LD抽出(<script type="application/ld+json"> タグを解析する)へ切り替えるか、現在の ipc- 系クラスにセレクタを更新することです。また、適切な User-Agent ヘッダーを付けているかも確認してください。ヘッダーがないと403エラーになり、空結果のように見えることがあります。

IMDbから25件以上を取得するにはどうすればいいですか?

Top 250ページは250本すべてを1回のレスポンスで読み込むので、ページネーションは不要です。検索結果では、start= URLパラメータを50件ずつ増やしてページを進めます。たとえば start=1start=51start=101 です。ブロックを避けるため、リクエスト間に time.sleep(3) を入れてください。別の方法として、datasets.imdbws.com のIMDb公式データセットには2,600万件以上のタイトルが含まれており、ページネーションは不要です。

__NEXT_DATA__ とは何ですか? IMDbのスクレイピングに使うべき理由は?

__NEXT_DATA__ は、IMDbのReact/Next.jsページ内の <script id="__NEXT_DATA__"> タグに埋め込まれたJSONオブジェクトです。Reactがページを描画するために使う完全な構造化データ、つまりタイトル、評価、出演者、ジャンル、上映時間などが入っています。見た目のレイアウトではなく、基礎となるデータモデルを表しているため、CSSセレクタよりUI再設計に強いです。最も堅牢な抽出方法として、JSON-LDと併用してください。

コーディングなしでIMDbをスクレイピングできますか?

はい。主な方法は2つあります。(1) IMDbの公式一括データセットをダウンロードする — 2,600万件以上のタイトルをカバーする7つのTSVファイルで、毎日更新、非商用利用なら無料です。(2) ThunderbitのAIウェブスクレイパーを使う — IMDbページを読み取り、抽出項目を自動提案し、Excel、Google Sheets、CSVへ2クリックで出力できます。コード不要、保守すべきセレクタもありません。

詳しく知る

Ke
Ke
Thunderbit の CTO | シニアデータサイエンティスト & ML エキスパート 機械学習とデータサイエンスで約10年の経験を持つ Ke Shen は、コロンビア大学の卒業生であり、Walmart Labs の元シニアデータサイエンティストです。Python、R、Java、統計学における深い専門性は同業者からも高く評価されており、理論段階の複雑な AI アルゴリズムを本番運用レベルのアーキテクチャへと落とし込むための、実践に裏打ちされた知見を共有しています。
目次

ただ伝えるだけで、Webページをスクレイピング

必要なことをそのまま英語で伝えるだけ。いや、何も言わなくてもOKです。

Thunderbitを試す 無料
AIでデータを抽出
Google Sheets、Airtable、Notionへ簡単にデータを移行できます
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week