Nếu gần đây bạn từng tìm kiếm “scrape IMDb with Python”, có lẽ bạn đã nhận ra một điều: phần lớn các hướng dẫn bạn tìm được đều đã hỏng. Không phải kiểu “hơi lỗi thời” đâu — mà là kiểu “trả về 0 kết quả và hàng loạt lỗi NoneType” mới đúng.
Tôi đã dành vài tuần qua để thử gần như mọi hướng dẫn scrape IMDb lớn nhất mà tôi có thể tìm thấy — GeeksforGeeks, Medium, freeCodeCamp, notebook trên Kaggle, đủ cả. Trong hơn gắn thẻ IMDb scraping, phần lớn vẫn dùng các CSS selector (td.titleColumn, td.ratingColumn) đã biến mất từ tháng 6/2023, khi IMDb thiết kế lại trang Top 250. Kết quả là gì? Các forum đầy rẫy câu hỏi của developer kiểu “sao code của tôi trả về trống?”, còn những người duy trì thư viện phổ biến như “Chúng tôi cũng chẳng làm được gì nhiều, ngoài việc sửa từng parser một.” Hướng dẫn này sẽ chỉ bạn hai cách dùng Python hiện vẫn hoạt động, cách xử lý phân trang và lỗi thường gặp, khi nào Python không phải lựa chọn phù hợp, và cách làm scraper bền hơn để nó không sớm gia nhập “nghĩa địa” của các script hỏng.
Scrape IMDb bằng Python nghĩa là gì?
Web scraping là quá trình lấy dữ liệu từ trang web bằng chương trình — thay vì copy-paste thủ công, bạn viết một script để tự động làm việc đó. Khi nói “scraping IMDb”, ý là lấy dữ liệu phim có cấu trúc (tên phim, điểm rating, thể loại, dàn cast, thời lượng, số lượt vote) từ các trang IMDb bằng Python.
Bộ công cụ Python thường dùng cho việc này gồm ba thư viện: requests (để tải trang web), BeautifulSoup (để phân tích HTML và tìm dữ liệu), và pandas (để sắp xếp và xuất kết quả). Một số hướng dẫn cũng dùng Selenium hoặc Playwright cho các trang cần render JavaScript, nhưng như bạn sẽ thấy, vẫn có cách nhanh hơn.
Một lưu ý quan trọng: mọi nội dung trong hướng dẫn này đều đã được kiểm tra theo cấu trúc trang IMDb hiện tại tính đến giữa năm 2025. IMDb thay đổi giao diện khoảng mỗi 6–12 tháng, nên nếu bạn đọc bài này vào năm 2027, một số selector có thể đã khác. (Tôi cũng sẽ chỉ bạn cách xử lý chuyện đó.)
Vì sao nên scrape IMDb bằng Python? Các tình huống thực tế
Trước khi viết bất kỳ dòng code nào, hãy tự hỏi: bạn sẽ làm gì với dữ liệu IMDb? Câu trả lời tùy thuộc vào bạn là ai.
Bộ dữ liệu review của IMDb là một trong những benchmark NLP được dùng nhiều nhất trên thế giới — bài nghiên cứu nền tảng của Maas et al. (2011) đã nhận , và bộ dữ liệu này còn được tích hợp sẵn trong TensorFlow, Keras và PyTorch. Trên Hugging Face, dataset stanfordnlp/imdb được tải xuống 213.321 lần mỗi tháng và đã được dùng để huấn luyện hơn 1.500 mô hình. Nếu bạn làm machine learning, nhiều khả năng bạn đã quen với dữ liệu IMDb rồi.
Nhưng ứng dụng của nó không chỉ dừng ở học thuật:
| Trường hợp sử dụng | Đối tượng phù hợp | Trường dữ liệu cần có |
|---|---|---|
| Hệ thống gợi ý phim | Data scientist, người thích tự làm dự án | Tên phim, thể loại, rating, dàn cast |
| Chiến lược nội dung cho nền tảng streaming | Nhóm sản phẩm/nội dung | Rating, lượt vote, năm phát hành, thể loại |
| Phân tích cảm xúc / huấn luyện NLP | Nhà nghiên cứu ML, sinh viên | Review, rating |
| Phân tích nội dung đối thủ | Chuyên gia phân tích ngành giải trí | Doanh thu phòng vé, ngày phát hành, xu hướng rating |
| Nghiên cứu du lịch phim ảnh | Cơ quan du lịch, công ty lữ hành | Địa điểm quay phim, chỉ số độ nổi tiếng |
| Nghiên cứu học thuật | Nhà nghiên cứu đại học | Bất kỳ metadata phim nào có cấu trúc |
Riêng thị trường du lịch phim ảnh toàn cầu được ước tính trị giá khoảng . Netflix đã chi hơn 17 tỷ USD cho nội dung trong năm 2024, với được thúc đẩy bởi hệ thống gợi ý cá nhân hóa. Ý chính là: dữ liệu IMDb đang nuôi dưỡng các quyết định thực tế ở nhiều ngành nghề.
Những lựa chọn để lấy dữ liệu IMDb trước khi viết code
Đây là phần mà hầu hết các hướng dẫn thường bỏ qua. Họ nhảy thẳng vào pip install beautifulsoup4 mà không hỏi liệu scraping bằng Python có thật sự là cách tốt nhất cho trường hợp của bạn hay không.
Đây là toàn cảnh đầy đủ:
| Cách làm | Phù hợp nhất cho | Ưu điểm | Nhược điểm |
|---|---|---|---|
| Python + BeautifulSoup | Học tập, trích xuất tùy chỉnh | Toàn quyền kiểm soát, linh hoạt | Selector dễ gãy, hay hỏng |
Trích xuất JSON-LD / __NEXT_DATA__ | Developer muốn ổn định | Xử lý được nội dung JS, bền hơn | Cần hiểu cấu trúc JSON |
| IMDb Official Datasets | Phân tích quy mô lớn, dùng trong học thuật | Hợp pháp, đầy đủ, hơn 26 triệu title, cập nhật hằng ngày | Dạng TSV, không có review/hình ảnh |
| Thư viện Cinemagoer (IMDbPY) | Tra cứu theo từng title bằng chương trình | API kiểu Python, nhiều trường dữ liệu | 88 issue đang mở, bản phát hành gần nhất là tháng 5/2023 |
| TMDb API | Metadata phim + hình ảnh | Có API key miễn phí, trả JSON, tài liệu tốt | Nguồn khác IMDb (không phải rating IMDb) |
| Thunderbit (no-code) | Người không biết code, cần xuất nhanh | Scrape 2 click, AI gợi ý trường dữ liệu, xuất ra Excel/Sheets | Dùng credit khi scrape lớn |
Vài ghi chú về các lựa chọn này. Cinemagoer chưa có bản phát hành PyPI nào từ tháng 5/2023, và hầu hết parser của nó đã hỏng sau lần IMDb redesign vào tháng 6/2025 — hiện tôi không khuyến nghị dùng cho production. TMDb thì rất tốt, nhưng dùng hệ thống rating riêng chứ không phải rating IMDb. Còn API doanh nghiệp chính thức của IMDb có giá thông qua AWS Data Exchange, nên không phải lựa chọn cho đa số chúng ta.
Với những ai không muốn viết code, có thể đọc trang IMDb, tự gợi ý trường cần trích xuất (title, rating, year, genre), rồi xuất sang Excel, Google Sheets, Airtable hoặc Notion chỉ trong hai cú click. AI sẽ tự thích ứng khi IMDb thay đổi bố cục, nên bạn không phải bảo trì selector. Phần đó tôi sẽ nói kỹ hơn sau.
Còn nếu bạn thực sự muốn viết Python — dưới đây là hai cách đang hoạt động.
Cách 1: Scrape IMDb bằng Python với BeautifulSoup (cách truyền thống)
Đây là cách kinh điển bạn sẽ thấy trong đa số hướng dẫn. Nó hoạt động, nhưng nói thẳng luôn: đây là phương pháp dễ vỡ nhất trong các cách tôi sẽ trình bày. Tên class CSS của IMDb được sinh tự động và thay đổi mỗi lần redesign. Dù vậy, đây vẫn là cách tốt nhất để học nền tảng web scraping.
Bước 1: Cài đặt và import thư viện Python
Bạn cần bốn gói sau:
1pip install requests beautifulsoup4 pandas lxml
Mỗi gói làm gì:
requests— gửi HTTP request để tải trang webbeautifulsoup4— phân tích HTML để bạn tìm các phần tử cụ thểpandas— tổ chức dữ liệu đã trích xuất thành bảng và xuất ra CSV/Excellxml— bộ phân tích HTML nhanh (BeautifulSoup có thể dùng làm backend)
Phần import:
1import requests
2from bs4 import BeautifulSoup
3import pandas as pd
Bước 2: Gửi HTTP request đến IMDb
Đây là chỗ nhiều người mới thường đụng “bức tường” đầu tiên. IMDb chặn các request không có header User-Agent phù hợp — bạn sẽ gặp lỗi 403 Forbidden. Chuỗi user-agent mặc định của Python Requests (python-requests/2.31.0) thường bị nhận ra ngay.
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"Failed to fetch page: {response.status_code}")
9else:
10 print("Page fetched successfully")
Header Accept-Language cũng quan trọng — nếu thiếu nó, IMDb có thể trả nội dung bằng ngôn ngữ khác tùy theo vị trí IP của bạn.
Bước 3: Phân tích HTML bằng BeautifulSoup
Khi đã có HTML, hãy tạo đối tượng BeautifulSoup và bắt đầu tìm phần tử phù hợp. Mở trang Top 250 của IMDb trong Chrome, nhấp chuột phải vào một tên phim và chọn “Inspect” để xem cấu trúc HTML bên dưới.
1soup = BeautifulSoup(response.text, "lxml")
Tính đến giữa năm 2025, trang Top 250 đang dùng các selector sau:
- Container phim:
li.ipc-metadata-list-summary-item - Tên phim:
h3.ipc-title__text - Năm:
span.cli-title-metadata-item(span đầu tiên) - Rating:
span.ipc-rating-star--rating
Lưu ý: các class bắt đầu bằng ipc- này được tạo bởi hệ thống component của IMDb. Chúng ổn định kể từ đợt redesign tháng 6/2023, nhưng không có gì đảm bảo chúng sẽ không đổi tiếp.
Bước 4: Trích xuất dữ liệu phim (Tên, Năm, Rating)
Điểm khác biệt của tôi so với nhiều hướng dẫn là có thêm xử lý lỗi try/except. Hầu như không có guide nào trong số đối thủ tôi xem làm điều này, và đó chính là lý do code của họ thường hỏng âm thầm khi selector thay đổi.
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"Error parsing movie: {e}")
18 continue
19print(f"Extracted {len(movies)} movies")
Bước 5: Lưu ra CSV hoặc Excel bằng Pandas
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())
Kết quả mẫu:
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
Cách này chạy được. Nhưng nó dựa vào selector CSS có thể gãy bất cứ lúc nào — và đó là lý do tôi khuyên bạn nên dùng cách tiếp theo.
Cách 2: Mẹo JSON-LD — bỏ qua luôn việc parse HTML
Đây là kỹ thuật mà gần như không có bài viết đối thủ nào nhắc tới, và cũng là cách tôi sẽ dùng cho bất kỳ dự án nghiêm túc nào. IMDb nhúng dữ liệu có cấu trúc dưới dạng (JavaScript Object Notation for Linked Data) trong thẻ <script type="application/ld+json"> trên mọi trang. Dữ liệu này tuân theo chuẩn Schema.org, được Google dùng cho kết quả tìm kiếm nổi bật, và thay đổi ít hơn rất nhiều so với tên class CSS.
Công cụ IMDb Scraper của Apify ở cấp production dùng thứ tự ưu tiên trích xuất là: “JSON-LD > NEXT_DATA > DOM.” Đó cũng là thứ tự tôi khuyên bạn nên theo.
Vì sao JSON-LD đáng tin hơn selector CSS
| Cách tiếp cận | Xử lý được nội dung JS không? | Chống chịu thay đổi giao diện không? | Tốc độ | Độ phức tạp |
|---|---|---|---|---|
| BeautifulSoup + CSS selector | ❌ Không | ⚠️ Dễ hỏng (class đổi) | Nhanh | Thấp |
| Trích xuất JSON-LD | ✅ Có | ✅ Theo chuẩn Schema.org | Nhanh | Thấp-Trung bình |
Trích xuất JSON __NEXT_DATA__ | ✅ Có | ✅ Khá ổn định | Nhanh | Thấp-Trung bình |
| Selenium / Playwright | ✅ Có | ⚠️ Dễ gãy | Chậm | Trung bình-Cao |
| Thunderbit (no-code, 2 click) | ✅ Có (AI đọc trang) | ✅ AI tự thích ứng | Nhanh | Không cần code |
Các class CSS như ipc-metadata-list-summary-item được sinh tự động bởi hệ thống React của IMDb và thay đổi theo từng lần redesign. Trong khi đó, JSON-LD phản ánh mô hình dữ liệu thật, chứ không phải lớp trình bày. Nó giống như khác biệt giữa đọc mục lục của cuốn sách và cố đoán chương dựa vào cỡ chữ của tiêu đề.

Từng bước: lấy dữ liệu IMDb từ JSON-LD
Bước 1: Tải trang
Tương tự như trước — dùng requests với header User-Agent phù hợp.
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")
Bước 2: Tìm thẻ script JSON-LD
1script_tag = soup.find("script", {"type": "application/ld+json"})
2if not script_tag:
3 print("No JSON-LD found on this page")
4else:
5 data = json.loads(script_tag.string)
6 print(f"Found JSON-LD with type: {data.get('@type', 'unknown')}")
Bước 3: Phân tích dữ liệu có cấu trúc
Ở trang Top 250, JSON-LD chứa mảng itemListElement với toàn bộ 250 bộ phim. Mỗi mục gồm vị trí, tên, URL, aggregateRating, datePublished, genre, description, director và mảng actor.
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 })
Bước 4: Xuất ra CSV
1df = pd.DataFrame(movies)
2df.to_csv("imdb_top_250_json_ld.csv", index=False)
3print(df.head())
Kết quả mẫu:
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 phim. Sạch sẽ, có cấu trúc, không cần vật lộn với selector CSS. Và vì dữ liệu này tuân theo chuẩn Schema.org (thứ Google cũng dựa vào cho kết quả tìm kiếm), nó ít có khả năng thay đổi hơn nhiều so với giao diện hiển thị.
Mẹo thêm: __NEXT_DATA__ cho trang phim riêng lẻ
Với dữ liệu phong phú hơn trên từng trang phim riêng (thời lượng, full cast, tóm tắt nội dung, ảnh poster), IMDb còn nhúng một đối tượng JSON __NEXT_DATA__. Đây là dữ liệu React dùng để hydrate trang — không thể xóa đi nếu không làm hỏng site.
1# Trên trang phim riêng như /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"]
Dùng JSON-LD cho trang danh sách/bảng xếp hạng, và __NEXT_DATA__ cho trang phim riêng lẻ. Đó là cách làm ở mức production.
Vì sao scraper IMDb của bạn cứ hỏng hoài? Và cách sửa
Đây là vấn đề được nhắc đến nhiều nhất trên mọi forum scraping IMDb mà tôi đã kiểm tra. Người dùng viết: “Một số code hỏng vì thay đổi giao diện” và “Năm 2024 mà vẫn không chạy!” — phản hồi thường là im lặng, hoặc “thử Selenium đi”.
Nguyên nhân gốc là IMDb đang dần chuyển sang frontend React/Next.js. Dưới đây là dòng thời gian của các thay đổi lớn làm hỏng scraper:
| Ngày | Thay đổi | Phần bị hỏng |
|---|---|---|
| 11/2022 | Thiết kế lại trang Name Pages | Các scraper cũ cho trang tên người |
| 06/2023 | Thiết kế lại trang Top 250 | Tất cả selector td.titleColumn / td.ratingColumn |
| 04/2023 | Thiết kế lại các trang con của title | Scraper bio, awards, news |
| 10/2023 | Thiết kế lại Advanced Search | Scraper dựa trên trang tìm kiếm |
| 06/2025 | Thiết kế lại các trang /reference | Thư viện Cinemagoer (phần lớn parser) |
Nói cách khác, cứ khoảng 6–12 tháng là lại có một lần gãy lớn. Nếu scraper của bạn phụ thuộc vào class CSS, bạn đang chạy trên một chiếc máy chạy bộ không bao giờ dừng.
Lỗi thường gặp và cách sửa
Kết quả trống / lỗi NoneType
Đây là lỗi phổ biến nhất. Bạn sẽ thấy AttributeError: 'NoneType' object has no attribute 'text'. Điều này nghĩa là BeautifulSoup không tìm thấy phần tử bạn cần — thường vì class CSS đã đổi, hoặc nội dung đó được render bằng JavaScript.
Cách sửa: Chuyển sang trích xuất JSON-LD (Cách 2 ở trên). Dữ liệu nằm ngay trong HTML ban đầu, không cần JavaScript.
403 Forbidden
IMDb dùng để phát hiện và chặn bot. Tác nhân số 1 là thiếu header User-Agent hợp lệ hoặc dùng chuỗi giả quá lộ liễu. Điều này đã được ghi nhận trên dự án mã nguồn mở và cả , nơi một nhân viên IMDb cũng đã thừa nhận vấn đề.
Cách sửa: Luôn thêm User-Agent giống trình duyệt thật và header Accept-Language: en-US. Dùng requests.Session() để gom kết nối.
Chỉ trả về 25 kết quả
Trang tìm kiếm IMDb và các danh sách “Most Popular” dùng lazy loading — ban đầu chỉ render khoảng 25 kết quả rồi tải thêm bằng AJAX khi bạn cuộn xuống.
Cách sửa: Dùng phân trang qua tham số URL (phần tiếp theo sẽ nói), hoặc chuyển sang trang Top 250, vì nó tải cả 250 phim trong một response duy nhất.
Selector bỗng nhiên ngừng hoạt động
Các selector cũ không còn dùng được: td.titleColumn, td.ratingColumn, .lister-item-header, .inline-block.ratings-imdb-rating. Nếu code của bạn có mấy cái này thì coi như đã gãy.
Cách sửa: Ưu tiên thuộc tính data-testid (ví dụ h1[data-testid="hero-title-block__title"]) thay vì các class tự sinh. Tốt hơn nữa là dùng JSON-LD.
Khung ra quyết định: sửa ngắn hạn vs. dài hạn
- Cách sửa nhanh: Bọc mọi selector bằng
try/except, kiểm tra mã HTTP, ghi log lỗi thay vì để chương trình crash - Cách sửa trung hạn: Chuyển từ CSS selector sang trích xuất JSON-LD (Cách 2)
- Cách sửa dài hạn: Dùng cho phân tích quy mô lớn, hoặc dùng công cụ như để AI đọc lại cấu trúc trang mỗi lần — không có selector phải bảo trì, AI tự thích ứng với thay đổi bố cục
Vượt qua giới hạn 25 kết quả: scrape phân trang và dữ liệu lớn trên IMDb
Hầu hết các hướng dẫn tôi xem chỉ scrape đúng một trang. Không ai nói về phân trang. Nhưng nếu bạn cần nhiều hơn một danh sách, bạn sẽ nhanh chóng đụng giới hạn.
Những trang không cần phân trang
Tin vui: trang Top 250 tải toàn bộ 250 phim trong một response render từ server. JSON-LD và __NEXT_DATA__ đều chứa đủ dữ liệu. Không cần phân trang.
Cách phân trang trên trang tìm kiếm IMDb
Trang search của IMDb dùng tham số URL start=, tăng theo bội số 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
Đây là một vòng lặp Python để đi qua từng trang kết quả:
1import time
2all_movies = []
3for start in range(1, 1001, 50): # Duyệt qua 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"Failed at start={start}: {response.status_code}")
8 break
9 soup = BeautifulSoup(response.text, "lxml")
10 # Trích xuất phim bằng cách bạn chọn
11 # ...
12 print(f"Scraped page starting at {start}")
13 time.sleep(3) # Hãy lịch sự — IMDb thường chặn sau khoảng 50 request nhanh liên tiếp
Dòng time.sleep(3) này rất quan trọng. Các báo cáo cộng đồng cho thấy IMDb bắt đầu chặn IP sau khoảng 50 request liên tục quá nhanh. Một khoảng trễ ngẫu nhiên từ 2–5 giây là thực hành tốt.
Khi nào nên bỏ hẳn scraping: IMDb Official Bulk Datasets
Nếu bạn thật sự cần dữ liệu quy mô lớn, IMDb cung cấp 7 file TSV miễn phí tại , được làm mới hằng ngày:
| File | Nội dung | Dung lượng |
|---|---|---|
| title.basics.tsv.gz | Title, loại, thể loại, thời lượng, năm | ~800 MB |
| title.ratings.tsv.gz | Rating trung bình, số lượt vote | ~25 MB |
| title.crew.tsv.gz | Đạo diễn, biên kịch | ~300 MB |
| title.principals.tsv.gz | Dàn cast/crew chính | ~2 GB |
| title.akas.tsv.gz | Tên thay thế theo khu vực | ~1.5 GB |
| title.episode.tsv.gz | Thông tin tập phim TV | ~200 MB |
| name.basics.tsv.gz | Người: tên, năm sinh, các title nổi bật | ~700 MB |
Việc nạp vào Pandas cũng khá đơn giản:
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# Ghép theo tconst (IMDb title ID)
4merged = basics.merge(ratings, on="tconst")
5top_movies = merged[merged["titleType"] == "movie"].nlargest(250, "averageRating")
Các dataset này bao phủ hơn 26 triệu title. Không cần phân trang, không cần selector, không dính lỗi 403. Tuy nhiên, giấy phép chỉ cho dùng cá nhân và phi thương mại — bạn không được phép tái bản hay bán lại dữ liệu.
Cách nhanh không cần code: Thunderbit xử lý phân trang cho bạn
Với những ai cần dữ liệu IMDb có phân trang nhưng không muốn viết logic phân trang, hỗ trợ sẵn cả pagination bằng click và infinite scroll. Bạn chỉ cần ra lệnh scrape, phần còn lại nó tự xử lý — kể cả cuộn qua nội dung lazy-load.
Scrape IMDb bằng Python: Mã code hoàn chỉnh có thể copy-paste
Dưới đây là hai script độc lập bạn có thể chạy ngay.
Script A: Cách dùng BeautifulSoup (CSS selector)
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"Error: {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"Skipping movie due to error: {e}")
28df = pd.DataFrame(movies)
29df.to_csv("imdb_top250_bs4.csv", index=False)
30print(f"Saved {len(df)} movies")
31print(df.head())
Script B: Cách JSON-LD (khuyến nghị)
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"Error: {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("No JSON-LD data found")
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"Saved {len(df)} movies")
42print(df.head())
Cả hai script đều có xử lý lỗi và xuất ra CSV sạch sẽ. Script B cho bạn dữ liệu phong phú hơn — đạo diễn, mô tả, URL — và bền hơn trước thay đổi giao diện.
Cách scrape IMDb mà không cần viết code (dùng Thunderbit)
Không phải ai cũng cần hoặc muốn viết Python. Có thể bạn là analyst vận hành, chỉ cần danh sách phim được đánh giá cao nhất tuần này trong một bảng tính. Có thể bạn là người làm chiến lược nội dung, muốn so sánh xu hướng thể loại qua từng năm. Trong những trường hợp đó, viết scraper là quá tay.
Đây là cách lấy cùng loại dữ liệu bằng :
Trước khi bắt đầu:
- Độ khó: Người mới
- Thời gian cần: ~2 phút
- Bạn cần: Trình duyệt Chrome, (gói miễn phí là đủ)
Bước 1: Mở trang IMDb bạn muốn scrape. Mở Top 250 của IMDb (hoặc bất kỳ trang list/search nào khác) trong Chrome.
Bước 2: Nhấn “AI Suggest Fields” ở thanh bên Thunderbit. AI sẽ quét trang và đề xuất các cột — thường là Title, Year, Rating, Genre và vài trường khác tùy trang. Bạn sẽ thấy bảng xem trước với các trường được gợi ý.
Bước 3: Chỉnh trường nếu cần. Xóa các cột không cần, hoặc thêm cột tùy chỉnh bằng cách nhấn “+ Add Column” và mô tả bằng tiếng Anh tự nhiên (ví dụ: “Director name” hoặc “Number of votes”).
Bước 4: Nhấn “Scrape.” Thunderbit sẽ trích xuất dữ liệu. Với trang có infinite scroll hoặc pagination, nó tự cuộn và xử lý luôn.
Bước 5: Xuất dữ liệu. Nhấn nút export và chọn định dạng — Excel, Google Sheets, CSV, Airtable hoặc Notion. Dữ liệu sẽ được đưa đến nơi bạn muốn chỉ trong vài giây.
Ưu điểm cốt lõi ở đây không chỉ là tiện — mà là AI của Thunderbit đọc lại cấu trúc trang từ đầu mỗi lần. Khi IMDb đổi bố cục (và chắc chắn là sẽ đổi), AI sẽ tự thích nghi. Không cần cập nhật selector, không phải sửa code. Với ai từng bị một scraper hỏng vào 2 giờ sáng trước deadline, giá trị của điều này là rất lớn.
Thunderbit còn hỗ trợ scrape trang con — bạn có thể bấm vào từng trang chi tiết phim và làm giàu bảng dữ liệu với cast, đạo diễn, thời lượng và các trường khác không hiển thị ở trang danh sách. Nếu muốn xem trực tiếp, hãy ghé .
Scrape IMDb có hợp pháp không? Bạn cần biết gì
Người dùng thường hỏi thẳng trong forum: “Cái này có hợp pháp không?… IMDb không muốn người ta scrape website của họ.” Đây là câu hỏi hoàn toàn hợp lý, và không có bài nào trong số đối thủ nói rõ.
robots.txt của IMDb: Trang Top 250 (/chart/top/), các trang title riêng lẻ (/title/ttXXXXXXX/), và trang name (/name/nmXXXXXXX/) đều KHÔNG bị chặn bởi robots.txt. Các đường dẫn bị chặn gồm /find, /_json/*, /search/name-text, /user/ur*/ratings, và nhiều endpoint AJAX khác. Không có chỉ thị Crawl-delay.
Điều khoản sử dụng của IMDb: Điều khoản liên quan ghi rõ: “Bạn không được sử dụng data mining, robots, screen scraping hoặc các công cụ thu thập/trích xuất dữ liệu tương tự trên trang này, trừ khi có sự chấp thuận bằng văn bản của chúng tôi.” Một điều khoản khác cấm bán lại hoặc sử dụng dữ liệu scrape cho mục đích thương mại.
Điều này có nghĩa gì trong thực tế: Các phán quyết gần đây năm 2024 (Meta v. Bright Data, X Corp v. Bright Data) cho thấy Terms of Service có thể không ràng buộc với người dùng chưa từng đồng ý — nếu bạn scrape dữ liệu công khai mà không đăng nhập, tính cưỡng hành của ToS vẫn còn tranh cãi. Nhưng đây là một lĩnh vực pháp lý đang thay đổi.
Phương án an toàn hơn: của IMDb được cho phép rõ ràng cho mục đích cá nhân và phi thương mại. API của TMDb cũng khá thoáng với key miễn phí. Đây đều là lựa chọn tốt nếu bạn muốn an toàn, rõ ràng.
Khuyến nghị thực tế: Nếu vẫn scrape, hãy giữ tốc độ hợp lý (time.sleep(3) giữa các request), đặt header chuẩn, và đừng truy cập những đường dẫn bị robots.txt chặn. Với dự án thương mại, hãy hỏi chuyên gia pháp lý hoặc dùng official datasets/API.
Chúng tôi đã phân tích sâu về trên blog Thunderbit.
Kết luận: Chọn đúng cách scrape IMDb bằng Python
Tóm tắt ngắn gọn:
- BeautifulSoup + CSS selector: Phù hợp để học kiến thức nền. Hãy chấp nhận rằng nó có thể gãy sau mỗi 6–12 tháng. Luôn thêm xử lý lỗi.
- Trích xuất JSON-LD: Cách tôi khuyên dùng cho mọi dự án Python đang chạy lâu dài. Tuân theo chuẩn Schema.org, thay đổi ít hơn nhiều so với CSS class, và cho bạn dữ liệu có cấu trúc sạch sẽ mà không cần render JavaScript.
- JSON
__NEXT_DATA__: Dùng như dữ liệu bổ sung cho trang phim riêng lẻ để lấy thông tin phong phú hơn (thời lượng, full cast, plot, ảnh poster). - IMDb Official Datasets: Lựa chọn tốt nhất cho phân tích quy mô lớn. Hơn 26 triệu title, cập nhật hằng ngày, không cần scraping. Chỉ dùng cá nhân/phi thương mại.
- : Lựa chọn tốt nhất cho người không biết code hoặc ai muốn lấy dữ liệu nhanh mà không phải bảo trì script. AI tự thích nghi với thay đổi bố cục, xử lý pagination, xuất ra Excel/Sheets/Airtable/Notion.
Hãy lưu lại hướng dẫn này — tôi sẽ cập nhật khi IMDb thay đổi cấu trúc tiếp theo. Và nếu bạn muốn bỏ qua luôn phần code, hãy để xem bạn có thể đi từ một trang IMDb đến một bảng tính sạch sẽ nhanh đến mức nào. Nếu bạn cũng làm việc với các website khác, bài hướng dẫn của chúng tôi về sẽ giúp bạn nắm được quy trình tổng thể.
Câu hỏi thường gặp
Scrape IMDb có hợp pháp không?
Điều khoản của IMDb cấm scraping khi chưa được đồng ý, nhưng mức độ có thể thực thi của ToS đối với dữ liệu công khai vẫn còn tranh cãi về mặt pháp lý sau các phán quyết năm 2024. Lựa chọn an toàn nhất là của IMDb (dùng cá nhân/phi thương mại) hoặc TMDb API (miễn phí key). Nếu vẫn scrape, hãy tôn trọng robots.txt, giãn cách giữa các request, và tránh các đường dẫn bị chặn. Với mục đích thương mại, nên hỏi chuyên gia pháp lý.
Vì sao scraper IMDb của tôi trả về kết quả trống?
Gần như luôn do selector CSS đã lỗi thời — những class như td.titleColumn và td.ratingColumn đã không còn tồn tại từ tháng 6/2023. Cách khắc phục là chuyển sang trích xuất JSON-LD (phân tích thẻ <script type="application/ld+json">) hoặc cập nhật selector sang các class ipc- hiện tại. Đồng thời hãy kiểm tra xem bạn đã thêm header User-Agent phù hợp chưa, vì thiếu header này có thể gây lỗi 403 khiến nhìn giống như kết quả trống.
Làm sao để scrape hơn 25 kết quả từ IMDb?
Trang Top 250 tải toàn bộ 250 phim trong một response — không cần phân trang. Với trang tìm kiếm, hãy dùng tham số URL start= (tăng theo bước 50) để đi qua từng trang. Ví dụ: start=1, start=51, start=101. Thêm time.sleep(3) giữa các request để tránh bị chặn. Hoặc dùng của IMDb, nơi có hơn 26 triệu title và không cần phân trang.
__NEXT_DATA__ là gì và vì sao nên dùng nó để scrape IMDb?
__NEXT_DATA__ là một object JSON được nhúng trong thẻ <script id="__NEXT_DATA__"> trên các trang React/Next.js của IMDb. Nó chứa toàn bộ dữ liệu có cấu trúc mà React dùng để render trang — tên phim, rating, cast, thể loại, thời lượng và nhiều hơn nữa. Vì nó phản ánh mô hình dữ liệu bên dưới thay vì bố cục hiển thị, nó bền hơn selector CSS trước các lần redesign giao diện. Hãy dùng nó song song với JSON-LD để có cách trích xuất ổn định nhất.
Có thể scrape IMDb mà không cần code không?
Có. Hai lựa chọn chính: (1) Tải của IMDb — 7 file TSV bao phủ hơn 26 triệu title, cập nhật hằng ngày, miễn phí cho mục đích phi thương mại. (2) Dùng , công cụ sẽ đọc trang IMDb, tự gợi ý trường dữ liệu, và xuất ra Excel, Google Sheets hoặc CSV chỉ trong hai cú click — không cần code, không cần bảo trì selector.
Tìm hiểu thêm
