Thu thập dữ liệu IMDb bằng Python: Mã nguồn thực sự hoạt động

Cập nhật lần cuối vào April 28, 2026

Nếu gần đây bạn đã tìm kiếm "thu thập dữ liệu IMDb bằng Python", có lẽ bạn cũng nhận ra một điều: phần lớn hướng dẫn bạn tìm thấy đề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" cơ.

Tôi đã dành vài tuần qua để thử mọi hướng dẫn lớn về thu thập dữ liệu IMDb mà mình tìm được — GeeksforGeeks, Medium, freeCodeCamp, Kaggle notebooks, đủ cả. Trong số được gắn nhãn cho việc thu thập dữ liệu IMDb, đa số đều tham chiếu tới các bộ chọn CSS (td.titleColumn, td.ratingColumn) đã không còn tồn tại kể từ tháng 6 năm 2023, khi IMDb thiết kế lại trang Top 250. Kết quả? Diễn đàn tràn ngập các lập trình viên hỏi "vì sao code của tôi trả về rỗng?" và các quản trị viên của những thư viện phổ biến như rằng "Không có nhiều điều chúng tôi có thể làm, ngoài việc sửa từng parser." Hướng dẫn này sẽ chỉ bạn hai cách dùng Python đang thực sự hoạt động ở thời điểm hiện tại, cách xử lý phân trang và các lỗi thường gặp, khi nào Python thậm chí không phải công cụ phù hợp, và cách làm cho trình thu thập của bạn bền hơn để không sớm vào “nghĩa địa” công cụ.

Thu thập dữ liệu IMDb bằng Python nghĩa là gì?

Thu thập dữ liệu web là quá trình trích xuất dữ liệu từ trang web bằng chương trình — thay vì tự tay sao chép và dán, bạn viết một script để làm việc đó thay mình. Khi nói đến "thu thập IMDb", chúng ta đang nói về việc lấy dữ liệu phim có cấu trúc (tên phim, điểm số, thể loại, diễn viên, thời lượng, số lượt bình chọn) từ các trang IMDb bằng Python.

Bộ công cụ Python điển hình cho việc này gồm ba thư viện: requests (để lấy 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òn dùng Selenium hoặc Playwright cho những trang cần render JavaScript, nhưng như bạn sẽ thấy, có những cách nhanh hơn.

Một lưu ý quan trọng: mọi thứ trong hướng dẫn này đều đã được kiểm chứng với cấu trúc trang hiện tại của IMDb 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ố bộ chọn có thể đã thay đổi. (Tôi cũng sẽ giải thích cách xử lý chuyện đó.)

Vì sao nên thu thập IMDb bằng Python? Các trường hợp sử dụng thực tế

Trước khi viết một dòng code nào, bạn sẽ làm gì thực sự với dữ liệu IMDb? Câu trả lời phụ 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 rộng rãi nhất trên thế giới — bài báo nền tảng của Maas et al. (2011) đã tích lũy , và bộ dữ liệu này được tích hợp sẵn trong TensorFlow, Keras và PyTorch. Trên Hugging Face, bộ dữ liệu stanfordnlp/imdb nhận 213.321 lượt tải mỗi tháng và đã được dùng để huấn luyện hơn 1.500 mô hình. Vì vậy nếu bạn làm machine learning, rất có thể bạn đã quen với dữ liệu IMDb rồi.

Nhưng ứng dụng của nó còn vượt xa giới học thuật:

Trường hợp sử dụngDành cho aiTrường dữ liệu cần có
Hệ thống gợi ý phimNhà khoa học dữ liệu, người đam mêTên phim, thể loại, điểm số, diễn viên
Chiến lược nội dung cho nền tảng streamingNhóm sản phẩm/nội dungĐiểm số, lượt bình chọn, năm phát hành, thể loại
Phân tích cảm xúc / huấn luyện NLPNhà nghiên cứu ML, sinh viênReview, điểm số
Phân tích nội dung đối thủNhà phân tích ngành giải tríDoanh thu phòng vé, ngày phát hành, xu hướng điểm số
Nghiên cứu du lịch điện ảnhCơ quan du lịch, công ty lữ hànhĐịa điểm quay phim, chỉ số độ phổ biến
Nghiên cứu học thuậtNhà nghiên cứu đại họcBất kỳ siêu dữ liệu phim có cấu trúc nào

Riêng thị trường du lịch điện ảnh đã được ước tính trị giá . Netflix đã chi hơn 17 tỷ USD cho nội dung trong năm 2024, với đến từ gợi ý cá nhân hóa. Ý chính là: dữ liệu IMDb phục vụ những quyết định thực tế ở nhiều ngành khác nhau.

Những lựa chọn để lấy dữ liệu IMDb của bạn (trước khi viết một dòng code)

Đây là phần mà hầu hết hướng dẫn đều bỏ qua hoàn toàn. 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 phù hợp với trường hợp của bạn hay không.

Đây là toàn cảnh các lựa chọn:

Cách làmPhù hợp nhất choƯu điểmNhược điểm
Python + BeautifulSoupHọc tập, trích xuất tùy chỉnhKiểm soát hoàn toàn, linh hoạtBộ chọn mong manh, dễ hỏng
Trích xuất JSON-LD / __NEXT_DATA__Lập trình viên cần sự ổn địnhXử lý được nội dung JS, bền hơnCần hiểu cấu trúc JSON
IMDb Official DatasetsPhân tích quy mô lớn, dùng cho học thuậtHợp pháp, đầy đủ, hơn 26 triệu tiêu đề, cập nhật hằng ngàyĐịnh dạng TSV, không có review/hình ảnh
Thư viện Cinemagoer (IMDbPY)Tra cứu từng tiêu đề theo chương trìnhAPI kiểu Python, nhiều trường dữ liệu88 lỗi đang mở, bản phát hành gần nhất tháng 5/2023
TMDb APISiêu dữ liệu phim + hình ảnhKhóa API miễn phí, JSON, tài liệu tốtNguồn khác (không phải điểm IMDb)
Thunderbit (không cần code)Người không biết code, cần xuất nhanhThu thập bằng 2 cú nhấp, AI gợi ý trường dữ liệu, xuất ra Excel/SheetsTính phí theo credit cho các lượt thu thập lớn

Một 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 kể từ tháng 5/2023 và phần lớn parser của nó đã bị hỏng sau đợt thiết kế lại của IMDb vào tháng 6/2025 — hiện tại tôi không khuyến nghị dùng cho sản xuất. TMDb rất tốt nhưng dùng hệ thống đánh giá riêng, không phải điểm IMDb. Và 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, sẽ đọc trang IMDb, tự động đề xuất các trường cần trích xuất (tên phim, điểm, năm, thể loại), và xuất sang Excel, Google Sheets, Airtable hoặc Notion chỉ trong hai cú nhấp. AI sẽ tự thích ứng khi IMDb thay đổi bố cục, nên bạn không phải bảo trì bộ chọn. Sẽ nói kỹ hơn ở phần sau.

Còn bây giờ, với những ai muốn viết Python — đây là hai cách hoạt động tốt.

Cách 1: Thu thập 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 hầu hết các hướng dẫn. Nó hoạt động, nhưng tôi muốn nói thẳng: đây là phương pháp dễ vỡ nhất trong những cách tôi sẽ trình bày. Tên lớp CSS của IMDb được sinh tự động và thay đổi mỗi khi giao diện được thiết kế lại. Dù vậy, đây vẫn là cách tốt nhất để học các nền tảng của web scraping.

Bước 1: Cài đặt và import các thư viện Python

Bạn cần bốn gói:

1pip install requests beautifulsoup4 pandas lxml

Từng gói làm gì:

  • requests — gửi yêu cầu HTTP để lấy trang web
  • beautifulsoup4 — phân tích HTML để bạn có thể tìm các phần tử cụ thể
  • pandas — sắp xếp dữ liệu đã trích xuất thành bảng và xuất ra CSV/Excel
  • lxml — bộ phân tích HTML nhanh (BeautifulSoup có thể dùng nó làm backend)

Khối import của bạn:

1import requests
2from bs4 import BeautifulSoup
3import pandas as pd

Bước 2: Gửi yêu cầu HTTP tới IMDb

Đây là chỗ mà phần lớn người mới gặp bức tường đầu tiên. IMDb chặn các yêu cầu không có header User-Agent hợp lệ — bạn sẽ nhận lỗi 403 Forbidden. Chuỗi user-agent mặc định của Python Requests (python-requests/2.31.0) bị phát hiện ngay lập tức.

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"Không lấy được trang: \{response.status_code\}")
9else:
10    print("Đã lấy trang thành công")

Header Accept-Language cũng rất quan trọng — nếu không có nó, IMDb có thể trả về nội dung bằng ngôn ngữ khác tùy theo vị trí địa lý của IP.

Bước 3: Phân tích HTML với BeautifulSoup

Khi đã có HTML, hãy tạo một đối tượng BeautifulSoup và bắt đầu tìm đúng phần tử. Mở trang IMDb Top 250 trong Chrome, nhấp chuột phải vào tên một 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 dùng các bộ chọn sau:

  • Khung 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)
  • Điểm số: span.ipc-rating-star--rating

Cảnh báo trước: các tên lớp ipc- này được sinh ra bởi hệ thống component của IMDb. Chúng ổn định kể từ đợt thiết kế lại vào tháng 6/2023, nhưng không có gì đảm bảo chúng sẽ không thay đổi lần nữa.

Bước 4: Trích xuất dữ liệu phim (tên, năm, điểm)

Đây là chỗ tôi làm khác nhiều hướng dẫn khác: tôi thêm xử lý lỗi try/except. Không một hướng dẫn đối thủ nào tôi xem có làm điều này, và đó chính là lý do code của họ âm thầm hỏng khi một bộ chọn 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"Lỗi khi phân tích phim: \{e\}")
18        continue
19print(f"Đã trích xuất {len(movies)} phim")

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 hoạt động. Nhưng nó đang được ghép lại bằng những bộ chọn CSS có thể hỏng bất kỳ lúc nào — và đó là lý do chúng ta chuyển sang cách mà tôi thực sự khuyên dùng.

Cách 2: Mẹo JSON-LD — bỏ qua hẳn việc phân tích HTML

Đây là kỹ thuật mà không bài viết nào của đối thủ đề cập đến, 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 các 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 phong phú, và thay đổi ít hơn rất nhiều so với tên lớp CSS.

Apify IMDb Scraper, một công cụ đạt chuẩn sản xuất, dùng thứ tự ưu tiên trích xuất: "JSON-LD > NEXT_DATA > DOM." Đó cũng là thứ tự tôi khuyên dùng.

Vì sao JSON-LD đáng tin cậy hơn bộ chọn CSS

Cách tiếp cậnXử lý được nội dung JS?Ít bị ảnh hưởng bởi thay đổi giao diện?Tốc độĐộ phức tạp
BeautifulSoup + bộ chọn CSS❌ Không⚠️ Mong manh (tên lớp thay đổi)NhanhThấp
Trích xuất JSON-LD✅ Có✅ Theo chuẩn Schema.orgNhanhThấp-Trung bình
Trích xuất JSON từ __NEXT_DATA__✅ Có✅ Khá ổn địnhNhanhThấp-Trung bình
Selenium / Playwright✅ Có⚠️ Mong manhChậmTrung bình-Cao
Thunderbit (không cần code, 2 cú nhấp)✅ Có (AI đọc trang)✅ AI tự thích ứngNhanhKhông có

Các tên lớp CSS như ipc-metadata-list-summary-item được sinh tự động bởi hệ thống React của IMDb và thay đổi mỗi khi giao diện được thiết kế lại. Lược đồ JSON-LD đại diện cho mô hình dữ liệu thực, chứ không phải lớp trình bày. Nó giống như sự khác nhau giữa việc đọc mục lục của một cuốn sách và cố nhận diện các chương dựa trên cỡ chữ.

css-selectors-vs-json-ld.webp

Từng bước: Trích xuất dữ liệu IMDb từ JSON-LD

Bước 1: Lấy trang

Giống 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("Không tìm thấy JSON-LD trên trang này")
4else:
5    data = json.loads(script_tag.string)
6    print(f"Đã tìm thấy JSON-LD với kiểu: {data.get('@type', 'không xác định')}")

Bước 3: Phân tích dữ liệu có cấu trúc

Trên trang Top 250, JSON-LD chứa một mảng itemListElement với toàn bộ 250 phim. Mỗi mục bao gồm vị trí, tên, URL, aggregateRating, datePublished, genre, description, director và các 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

Toàn bộ 250 phim. Sạch sẽ, có cấu trúc, không phải vật lộn với CSS selector. Và vì dữ liệu này tuân theo chuẩn Schema.org (mà Google dựa vào cho kết quả tìm kiếm), nó ít có khả năng thay đổi hơn bố cục hiển thị.

Mẹo thêm: __NEXT_DATA__ cho các trang phim riêng lẻ

Với dữ liệu phong phú hơn từ trang chi tiết từng phim (thời lượng, toàn bộ dàn diễn viên, tóm tắt cốt truyện, ảnh poster), IMDb cũng nhúng một đối tượng JSON __NEXT_DATA__. Đây là dữ liệu mà React dùng để hydrate trang — nó không thể bị gỡ bỏ mà không làm hỏng site.

1# Trên một trang phim riêng lẻ 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 các trang danh sách/bảng, dùng __NEXT_DATA__ cho trang phim riêng lẻ. Đó là cách làm chuẩn sản xuất.

Vì sao trình thu thập IMDb của bạn cứ bị hỏng (và cách sửa)

Đây là điểm đau được nhắc đến nhiều nhất trên mọi diễn đàn thu thập IMDb mà tôi kiểm tra. Người dùng viết: "Một số code bị hỏng vì thay đổi UI""Không hoạt động trong năm 2024!" — và phản hồi thường là im lặng hoặc "thử Selenium đi."

Nguyên nhân gốc là quá trình IMDb liên tục chuyển sang frontend React/Next.js. Đây là dòng thời gian của những thay đổi lớn gây hỏng code:

NgàyĐiều thay đổiCái gì bị hỏng
11/2022Thiết kế lại trang Name PagesCác trình thu thập trang name cũ
6/2023Thiết kế lại trang Top 250Toàn bộ bộ chọn td.titleColumn / td.ratingColumn
4/2023Thiết kế lại trang con của titleCác trình thu thập bio, giải thưởng, tin tức
10/2023Thiết kế lại Advanced SearchCác trình thu thập dựa trên tìm kiếm
6/2025Thiết kế lại các trang /referenceThư viện Cinemagoer (phần lớn parser)

Đó là khoảng một thay đổi lớn mỗi 6–12 tháng. Nếu trình thu thập của bạn phụ thuộc vào tên lớp CSS, bạn đang chạy trên một băng chuyền.

Các lỗi thường gặp và cách khắc phục

Kết quả rỗ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 đó có nghĩa là BeautifulSoup không tìm thấy phần tử bạn đang cần — thường là vì tên lớp CSS đã thay đổ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 trong phản hồi 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 kích hoạt số 1 là thiếu hoặc quá giả tạo header User-Agent. Điều này đã được ghi nhận trong dự án mã nguồn mở và cả , nơi một nhân viên IMDb đã thừa nhận vấn đề.

Cách sửa: Luôn thêm chuỗi User-Agent của 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ả

Các trang tìm kiếm và danh sách "Most Popular" của IMDb dùng lazy loading — ban đầu chỉ hiển thị khoảng 25 kết quả và tải thêm bằng AJAX khi bạn cuộn.

Cách sửa: Dùng phân trang qua tham số URL (được đề cập ở phần tiếp theo) hoặc chuyển sang trang Top 250, trang này tải toàn bộ 250 phim trong một phản hồi duy nhất.

Bộ chọn đột nhiên ngừng hoạt động

Các bộ chọn 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 dùng bất kỳ bộ chọn nào trong số này, nó đã hỏng.

Cách sửa: Ưu tiên các thuộc tính data-testid (như h1[data-testid="hero-title-block__title"]) thay vì các tên lớp được sinh tự động. Tốt hơn nữa là dùng JSON-LD.

Khung ra quyết định: cách sửa ngắn hạn vs dài hạn

  • Sửa nhanh: Thêm các khối try/except quanh mọi bộ chọn, kiểm tra mã trạng thái HTTP, ghi log lỗi thay vì để chương trình sập
  • Sửa trung hạn: Chuyển từ bộ chọn CSS sang trích xuất JSON-LD (Cách 2)
  • Sửa dài hạn: Dùng cho phân tích quy mô lớn, hoặc dùng một công cụ như sử dụng AI để đọc lại cấu trúc trang mỗi lần — không cần duy trì selector, AI tự thích ứng với thay đổi bố cục

Vượt qua giới hạn 25 kết quả: thu thập phân trang và dữ liệu lớn của IMDb

Hầu như mọi hướng dẫn đối thủ tôi xem đều chỉ thu thập đú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 đơn lẻ, bạn sẽ nhanh chóng đụng tường.

Những trang không cần phân trang

Tin tốt: trang Top 250 tải toàn bộ 250 phim trong một phản hồi render phía máy chủ. Cả JSON-LD và __NEXT_DATA__ đều chứa đầy đủ bộ dữ liệu. Không cần phân trang.

Phân trang tìm kiếm của IMDb hoạt động thế nào

Các trang tìm kiếm của IMDb dùng tham số URL start=, tăng theo 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 các 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"Lỗi tại start=\{start\}: \{response.status_code\}")
8        break
9    soup = BeautifulSoup(response.text, "lxml")
10    # Trích xuất phim bằng phương pháp bạn thích
11    # ...
12    print(f"Đã thu thập trang bắt đầu từ \{start\}")
13    time.sleep(3)  # Hãy lịch sự — IMDb chặn sau khoảng ~50 yêu cầu nhanh

Dòng time.sleep(3) đó rất quan trọng. Báo cáo từ cộng đồng cho thấy IMDb bắt đầu chặn IP sau khoảng 50 yêu cầu nhanh. Chèn độ trễ ngẫu nhiên 2–5 giây là thực hành tốt.

Khi nào nên bỏ hẳn scraping: bộ dữ liệu hàng loạt chính thức của IMDb

Với nhu cầu thật sự lớn, IMDb cung cấp 7 file TSV miễn phí tại , được làm mới hằng ngày:

| Tệp | Nội dung | Dung lượng | |---|---|---|---| | title.basics.tsv.gz | Tên phim, loại, thể loại, thời lượng, năm | ~800 MB | | title.ratings.tsv.gz | Điểm trung bình, số lượt bình chọn | ~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ựa đề thay thế theo khu vực | ~1.5 GB | | title.episode.tsv.gz | Thông tin tập phim truyền hình | ~200 MB | | name.basics.tsv.gz | Con người: tên, năm sinh, các tiêu đề nổi bật | ~700 MB |

Nạp chúng vào Pandas rất đơ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 (mã tiêu đề IMDb)
4merged = basics.merge(ratings, on="tconst")
5top_movies = merged[merged["titleType"] == "movie"].nlargest(250, "averageRating")

Các bộ dữ liệu này bao phủ hơn 26 triệu tiêu đề. Không cần phân trang, không cần selector, không lo lỗi 403. Giấy phép chỉ dành cho mục đích cá nhân và phi thương mại — bạn không thể tái xuất bản hay bán lại dữ liệu.

Cách tắt 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ợ cả phân trang bằng click lẫn cuộn vô hạn một cách tự nhiên. Bạn chỉ cần bảo nó thu thập, phần còn lại nó lo — kể cả cuộn qua nội dung tải lười.

Thu thập IMDb bằng Python: Mã nguồn hoàn chỉnh hoạt động được ngay (copy-paste)

Dưới đây là hai script độc lập bạn có thể chạy ngay bây giờ.

Script A: Phương pháp BeautifulSoup (bộ chọn 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"Lỗi: \{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"Bỏ qua phim vì lỗi: \{e\}")
28df = pd.DataFrame(movies)
29df.to_csv("imdb_top250_bs4.csv", index=False)
30print(f"Đã lưu {len(df)} phim")
31print(df.head())

Script B: Phương pháp 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"Lỗi: \{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("Không tìm thấy dữ liệu 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"Đã lưu {len(df)} phim")
42print(df.head())

Cả hai script đều có xử lý lỗi và tạo 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 bố cục.

Cách thu thập IMDb mà không cần viết bất kỳ code nào (dùng Thunderbit)

Không phải ai cũng cần hoặc muốn viết Python. Có thể bạn là chuyên viên 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 và muốn so sánh xu hướng thể loại theo năm. Trong những trường hợp đó, viết scraper là quá mức cần thiết.

Đâ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 bắt đầu
  • Thời gian cần: ~2 phút
  • Bạn cần gì: Trình duyệt Chrome, (gói miễn phí là đủ)

Bước 1: Mở trang IMDb bạn muốn thu thập. Mở IMDb Top 250 (hoặc bất kỳ trang danh sách/tìm kiếm nào khác của IMDb) trong Chrome.

Bước 2: Nhấp "AI Suggest Fields" trong thanh bên của Thunderbit. AI sẽ quét trang và đề xuất các cột — thường là Tên phim, Năm, Điểm số, Thể loại, và một 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: Điều chỉnh trường nếu cần. Xóa các cột bạn không cần, hoặc thêm cột tùy chỉnh bằng cách nhấp "+ Add Column" và mô tả bằng tiếng Anh tự nhiên những gì bạn muốn (ví dụ: "Tên đạo diễn" hoặc "Số lượt bình chọn").

Bước 4: Nhấp "Scrape." Thunderbit sẽ trích xuất dữ liệu. Với các trang có infinite scroll hoặc phân trang, công cụ sẽ tự xử lý việc cuộn.

Bước 5: Xuất dữ liệu. Nhấp nút xuất và chọn định dạng — Excel, Google Sheets, CSV, Airtable hoặc Notion. Dữ liệu sẽ được đưa đến đích của bạn chỉ trong vài giây.

Lợi thế chính ở đây không chỉ là sự tiện lợi — mà là AI của Thunderbit đọc lại cấu trúc trang mỗi lần. Khi IMDb thay đổi bố cục (và nó sẽ thay đổi), AI sẽ tự thích ứng. Không cần cập nhật selector, không cần sửa code. Với bất kỳ ai từng bị một scraper hỏng lúc 2 giờ sáng trước hạn chót, điều đó đáng giá lắm.

Thunderbit cũng hỗ trợ thu thập trang con — bạn có thể mở từng trang chi tiết phim và làm giàu bảng của mình với dàn diễn viên, đạo diễn, thời lượng và các trường khác không hiển thị trên trang danh sách. Nếu muốn xem nó hoạt động, hãy xem .

Có hợp pháp để thu thập dữ liệu IMDb không? Những điều bạn cần biết

Người dùng hỏi thẳng điều này trên diễn đàn: "Cái này có hợp pháp không?… IMDb không muốn người ta thu thập dữ liệu trên website của họ." Đây là câu hỏi hợp lý, và không có bài viết nào của đối thủ xử lý nó.

robots.txt của IMDb: Biểu đồ Top 250 (/chart/top/), các trang tiêu đề riêng lẻ (/title/ttXXXXXXX/) và các trang name (/name/nmXXXXXXX/) KHÔNG bị chặn bởi robots.txt. Các đường dẫn bị chặn bao gồm /find, /_json/*, /search/name-text, /user/ur*/ratings, và nhiều endpoint AJAX khác. Không có chỉ thị Crawl-delay nào được chỉ định.

Điều khoản sử dụng của IMDb: Điều khoản liên quan nêu rõ: "Bạn không được sử dụng data mining, robot, screen scraping hoặc các công cụ thu thập và trích xuất dữ liệu tương tự trên trang này, trừ khi có sự đồng ý bằng văn bản từ 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 đã thu thập 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 tòa án gần đây trong năm 2024 (Meta v. Bright Data, X Corp v. Bright Data) cho rằng Điều khoản dịch vụ có thể không ràng buộc người dùng chưa từng đồng ý với chúng — nếu bạn đang thu thập dữ liệu công khai mà không đăng nhập, tính ràng buộc của ToS là vấn đề còn tranh cãi. Nhưng đây là một lĩnh vực pháp lý đang thay đổi.

Lựa chọn an toàn hơn: Các được cho phép rõ ràng cho mục đích cá nhân và phi thương mại. API của TMDb khá thoáng với một khóa API miễn phí. Cả hai đều là lựa chọn vững chắc nếu bạn muốn đi theo con đường rõ ràng.

Hướng dẫn thực tế: Nếu bạn vẫn thu thập, hãy dùng tốc độ crawl lịch sự (time.sleep(3) giữa các yêu cầu), đặt header phù hợp, và đừng truy cập các đường dẫn bị robots.txt chặn. Với dự án thương mại, hãy tham khảo chuyên gia pháp lý hoặc dùng bộ dữ liệu/API chính thức.

Chúng tôi đã phân tích sâu trên blog Thunderbit.

Kết luận: Chọn đúng cách để thu thập IMDb bằng Python

Tóm gọn lại:

  • BeautifulSoup + bộ chọn CSS: Tốt để học nền tảng. Hãy chuẩn bị tinh thần nó sẽ hỏng sau mỗi 6–12 tháng. Luôn thêm xử lý lỗi.
  • Trích xuất JSON-LD: Là cách tôi khuyên cho mọi dự án Python đang tiếp diễn. Theo chuẩn Schema.org, ít thay đổi hơn nhiều so với class CSS, và cho bạn dữ liệu có cấu trúc sạch mà không cần render JavaScript.
  • JSON __NEXT_DATA__: Dùng như phần bổ sung để lấy dữ liệu phong phú hơn trên các trang tiêu đề riêng lẻ (thời lượng, toàn bộ dàn diễn viên, cốt truyện, ả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 tiêu đề, cập nhật hằng ngày, không cần scraping. Chỉ dùng cho mục đích 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 bất kỳ ai muốn lấy dữ liệu nhanh mà không phải bảo trì code. AI tự thích ứng với thay đổi bố cục, xử lý phân trang, xuất ra Excel/Sheets/Airtable/Notion.

Hãy đánh dấu trang hướng dẫn này — tôi sẽ cập nhật nó khi cấu trúc IMDb thay đổi lần tiếp theo. Và nếu bạn muốn bỏ qua hoàn toàn phần code, và xem bạn có thể đi từ một trang IMDb đến một bảng tính sạch nhanh đến mức nào. Nếu bạn cũng làm việc với các trang web khác, hướng dẫn của chúng tôi về sẽ bao quát quy trình tổng quát hơn.

Dùng thử AI Web Scraper cho IMDb và nhiều hơn nữa

Câu hỏi thường gặp

Có hợp pháp để thu thập dữ liệu IMDb không?

Điều khoản sử dụng của IMDb cấm thu thập dữ liệu mà không có sự đồng ý, nhưng tính ràng buộc của điều khoản này với dữ liệu công khai vẫn còn là vấn đề pháp lý gây tranh cãi sau các phán quyết tòa án gần đây trong năm 2024. Lựa chọn an toàn nhất là dùng (cho mục đích cá nhân/phi thương mại) hoặc TMDb API (khóa miễn phí). Nếu vẫn thu thập, hãy tôn trọng robots.txt, đặt độ trễ hợp lý giữa các yêu cầu và tránh các đường dẫn bị chặn. Với mục đích thương mại, hãy tham khảo chuyên gia pháp lý.

Vì sao trình thu thập IMDb của tôi trả về kết quả rỗng?

Gần như luôn luôn là do bộ chọn CSS lỗi thời — các lớp như td.titleColumntd.ratingColumn đã không còn tồn tại kể từ tháng 6/2023. Cách sửa 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 bộ chọn sang các lớp ipc- hiện tại. Đồng thời hãy kiểm tra xem bạn có đang thêm header User-Agent phù hợp không, vì thiếu header này sẽ gây lỗi 403 và có thể biểu hiện như kết quả rỗng.

Làm sao thu thập hơn 25 kết quả từ IMDb?

Trang Top 250 tải toàn bộ 250 phim trong một phản hồi duy nhất — không cần phân trang. Với kết quả tìm kiếm, hãy dùng tham số URL start= (tăng theo 50) để phân trang qua kết quả. Ví dụ: start=1, start=51, start=101. Thêm time.sleep(3) giữa các yêu cầu để tránh bị chặn. Hoặc bạn có thể dùng bộ dữ liệu chính thức của IMDb tại , nơi có hơn 26 triệu tiêu đề mà không cần phân trang.

__NEXT_DATA__ là gì và vì sao nên dùng nó để thu thập IMDb?

__NEXT_DATA__ là một đối tượng 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, điểm số, dàn diễn viên, thể loại, thời lượng, và nhiều hơn nữa. Vì nó đại diện cho mô hình dữ liệu bên dưới chứ không phải bố cục hiển thị, nó bền hơn trước các lần thiết kế lại giao diện so với bộ chọn CSS. Hãy dùng nó cùng JSON-LD để có cách trích xuất vững chắc nhất.

Có thể thu thập IMDb mà không cần code không?

Có. Hai lựa chọn chính: (1) Tải — 7 tệp TSV bao phủ hơn 26 triệu tiêu đề, 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ự đề xuất trường trích xuất, và xuất sang Excel, Google Sheets hoặc CSV chỉ trong hai cú nhấp — không cần code, không cần duy trì selector.

Tìm hiểu thêm

Thử Thunderbit

Lấy leads và dữ liệu khác chỉ với 2 cú nhấp. Vận hành bằng AI.

Nhận Thunderbit Miễn phí