Cách scrape các website được render bằng JavaScript
Nhiều website hiện đại gửi một tài liệu HTML gần như trống và xây dựng nội dung trong trình duyệt bằng JavaScript. Nếu scraper của bạn tải trang và không thấy sản phẩm, giá hay nội dung bài viết nào, thì nội dung vẫn ở đó, chỉ là đến muộn hơn. Hướng dẫn này chỉ cách nhận biết bạn đang gặp loại trang nào, cách tìm một đường nhanh hơn trình duyệt khi có, và cách scrape trang đã render bằng Playwright với Python. Cuối bài có một lưu ý về việc phải làm gì khi trang đã render hiển thị captcha.
Bước 1: kiểm tra nội dung có nằm trong HTML không
Mở trang bằng view-source: trong trình duyệt của bạn, hoặc tải nó bằng một request HTTP đơn giản, rồi tìm trong mã nguồn một đoạn văn bản bạn thấy trên màn hình. Có ba kết quả thường gặp:
- Văn bản có sẵn. Trang được render phía server. Một client HTTP đơn giản và một bộ phân tích HTML là đủ, và bạn nên dùng chúng, vì chúng nhanh hơn và đơn giản hơn.
- Văn bản không có, nhưng có một khối JSON. Nhiều trang nhúng trạng thái của họ trong một thẻ script, như
__NEXT_DATA__. Hãy phân tích JSON đó trực tiếp. - Văn bản không có và không có khối JSON. Trang tải nội dung qua các request khác. Hãy xem tab Network trong DevTools, lọc theo Fetch/XHR, trong khi trang tải.
Bước 2: gọi endpoint JSON nếu có
Khi trang lấy dữ liệu từ một endpoint, gọi trực tiếp endpoint đó thường là lựa chọn tốt nhất. Nó nhỏ hơn, nhanh hơn và ổn định hơn trang đã render. Sao chép URL request, kiểm tra header nào trang yêu cầu, và thử từ một script. Chỉ làm điều này khi điều khoản của trang cho phép, và tôn trọng mọi giới hạn tốc độ hoặc xác thực mà trang mong đợi. Một endpoint không được tài liệu hóa có thể thay đổi mà không báo trước, nên hãy viết bộ phân tích của bạn để báo lỗi rõ ràng khi cấu trúc phản hồi thay đổi.
Bước 3: render trang khi bắt buộc
Nếu nội dung thực sự cần trình duyệt, hãy điều khiển một headless browser. Playwright là lựa chọn tốt cho Python. Ví dụ dưới đây mở một danh sách sản phẩm, chờ nội dung xuất hiện, và đọc văn bản từ mỗi thẻ:
from playwright.sync_api import sync_playwright
URL = "https://example.com/products" # một trang bạn được phép scrape
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto(URL, wait_until="domcontentloaded", timeout=60_000)
# Chờ phần tử chứng minh dữ liệu đã về.
page.wait_for_selector(".product-card", timeout=30_000)
titles = page.locator(".product-card h2").all_inner_texts()
browser.close()
for title in titles:
print(title)
Chờ một selector cụ thể đáng tin hơn nhiều so với sleep cố định. Một khoảng dừng cố định hoặc quá ngắn vào một ngày chậm, hoặc lãng phí thời gian vào một ngày nhanh. Hãy chọn phần tử chỉ xuất hiện khi dữ liệu đã tải xong.
Bước 4: xử lý cuộn vô hạn và phân trang
Một số trang tải thêm mục khi bạn cuộn. Cuộn xuống đáy, chờ số mục tăng, và dừng khi nó không tăng nữa. Các trang có phân trang đánh số đơn giản hơn: tìm liên kết trang tiếp theo, theo đó, và dừng ở trang cuối. Luôn đặt số trang hoặc số lần cuộn tối đa, để một lỗi không khiến crawler của bạn chạy mãi.
def scroll_until_done(page, selector: str, max_rounds: int = 20) -> int:
previous = -1
for _ in range(max_rounds):
count = page.locator(selector).count()
if count == previous:
break
previous = count
page.mouse.wheel(0, 4000)
page.wait_for_timeout(1500) # cho lô tiếp theo thời gian tải
return previous
Bước 5: giữ trình duyệt tiết kiệm
Render rất tốn kém. Chặn ảnh, font và media mà bạn không cần, và dùng lại một trình duyệt cho nhiều trang thay vì khởi động một trình duyệt mới mỗi lần. Đóng trang và context khi xong, để bộ nhớ không tăng qua một lần chạy dài.
def block_heavy_assets(route):
if route.request.resource_type in ("image", "font", "media"):
route.abort()
else:
route.continue_()
page.route("**/*", block_heavy_assets)
Bước 6: khi trang đã render hiển thị captcha
Đôi khi trang đã render trình bày một captcha trước khi hiển thị bất kỳ nội dung nào. Nếu captcha đó là PopularCaptcha, loại captcha đang hoạt động tại Scarnox, bạn đọc sitekey từ trang, yêu cầu một token, và đặt token vào trường form mà trang mong đợi. Hướng dẫn Python cho PopularCaptcha đi qua bước đó. Các widget khác chưa hoạt động, nên hãy kiểm tra các loại captcha được giải thích trước khi lên kế hoạch dựa vào một loại nào đó.
Hãy cẩn thận với chính trình duyệt. Một headless browser trông rõ ràng là tự động hóa có thể kích hoạt thêm kiểm tra, không phải ít hơn. Đọc phát hiện headless browser để biết các tín hiệu cần chú ý, và ưu tiên tải trang chậm hơn, ít lần hơn.
Scrape có trách nhiệm
- Kiểm tra
robots.txtvà điều khoản của trang trước khi render hoặc gọi endpoint của họ. - Giữ giới hạn tốc độ thấp, và cache kết quả để bạn tải mỗi trang một lần.
- Không scrape dữ liệu cá nhân khi không có cơ sở pháp lý.
- Nếu một trang hạn chế truy cập, hãy dừng lại. Đừng cố vượt qua hạn chế.
Bước tiếp theo
- Scrape mà không bị chặn
- Phát hiện headless browser
- Scrape trang được Cloudflare bảo vệ
- Xử lý captcha với Playwright và Puppeteer
Bắt đầu với tín dụng dùng thử miễn phí, không cần thẻ: tạo tài khoản và thử luồng PopularCaptcha trên một trang bạn sở hữu. Xem giá hiện tại tại trang giá.