Adaptive selectors thường bị gán nhầm cho rất nhiều công cụ khác nhau. Trong không ít bài so sánh scraper tôi đọc được, tính năng “sống sót sau khi website redesign” lại bị gắn cho một crawler AI lớn nào đó, dù thực tế nó chẳng làm được như vậy. Thư viện Python đặt tính năng này lên hàng đầu là Scrapling, một dự án đang tăng trưởng nhanh với khoảng 68.7k sao GitHub tính đến 2026-07-09.
Vì vậy, tôi đã chạy đúng bài test quan trọng nhất cho một lời quảng bá như thế này. Tôi dựng một trang fixture, lưu một selector, rồi đổi tên class của phần tử mục tiêu — đúng kiểu thay đổi âm thầm khiến scraper “chết lặng” ngay sáng hôm sau khi website tung ra bản redesign. Một selector thông thường trả về rỗng. Còn cơ chế adaptive match của Scrapling vẫn tìm lại được phần tử đó. Phần này là thật, và tôi sẽ cho bạn xem số liệu. Điều hầu như không ai đo đếm là khả năng “hồi phục” dừng lại ở đâu, và ranh giới đó hóa ra lại là toàn bộ trọng tâm của bài đánh giá này.
Scrapling thực sự là gì

Scrapling tự mô tả mình là một framework web scraping thích ứng, xử lý được “mọi thứ từ một request đơn lẻ đến một cuộc crawl quy mô lớn.” Bóc lớp slogan ra thì nó có hai thành phần xếp chồng lên nhau: một Fetcher dùng HTTP để tải trang và một Selector dựa trên lxml để phân tích nội dung, hỗ trợ CSS/XPath chuẩn cùng các pseudo-selector tiện lợi như ::text và ::attr(). Dự án được cấp phép BSD-3-Clause, thuộc nhóm giấy phép rất thoáng trong thế giới open source. Tôi đã test phiên bản 0.4.10, là bản phát hành hiện tại vào thời điểm đó — nên không có chuyện “bạn benchmark trên bản cũ rồi” để phải lo.
Lớp thú vị nằm ở phần adaptive được chồng lên parser đó. Hình dung selector bình thường như một địa chỉ nhà cố định: “Lấy phần tử có class product-name.” Chỉ cần đánh lại số nhà — đổi class — là địa chỉ đó chỉ vào một khoảng trống. Scrapling thì có thể lưu dấu vân tay của một phần tử ở lần chạy trước, rồi ở lần chạy sau, khi markup đã thay đổi, tìm lại phần tử đó bằng fingerprint thay vì địa chỉ cũ đã chết. Theo tài liệu adaptive scraping của Scrapling, bước đối sánh sẽ chấm điểm độ tương đồng dựa trên tag, văn bản, thuộc tính, phần tử anh em và vị trí của phần tử — không có model nào tham gia, chỉ là so khớp cấu trúc với dữ liệu đã lưu.
Cũng nên nói rõ nguồn gốc của tính năng này, vì điều đó quyết định cách bạn nhìn nhận nó. Việc tự động tìm lại phần tử là một khả năng có thật và đã được tài liệu hóa, không phải thứ tôi tình cờ phát hiện ra — tài liệu của nhà phát triển giải thích đầy đủ cơ chế lưu vào SQLite và đối sánh theo độ tương đồng, và các bài viết độc lập bên thứ ba cũng đã đi qua nó. Khái niệm “self-healing selectors” cũng xuất hiện trước Scrapling trong thế giới test automation. Điểm khác biệt là Scrapling đưa nó thành một tính năng gốc của thư viện: các parser thuần như lxml, parsel và BeautifulSoup chỉ cho bạn selector tĩnh, không có gì tự động tự hồi vị trí. Vì vậy đây là một tính năng đặc trưng nhưng có tài liệu rõ ràng mà tôi đã tái hiện và kiểm tra sức bền — chứ không phải một năng lực “độc quyền” không ai khác có.
Bài test adaptive, chi tiết ra sao

Thiết lập như sau. Tôi dựng một catalog fixture và theo dõi một phần tử sản phẩm khi class của nó là product-name. Sau đó tôi đổi class đó thành product-title và chạy lại đúng đoạn code cũ. Một selector thuần .product-name chỉ khớp 0 phần tử — chính xác là kết quả rỗng mà bạn sẽ mong đợi khi selector đang trỏ vào một class không còn tồn tại nữa. Còn cơ chế adaptive re-matching của Scrapling thì vẫn phục hồi được phần tử đã theo dõi nhờ fingerprint được lưu từ phiên bản trước. Kết quả thô nằm trong repo benchmark tại local_adaptive_selector.json.

Dùng thử Thunderbit để trích xuất dữ liệu web

Bây giờ là phần nhiều bài review hay bỏ qua. Tôi đẩy nó xa hơn bằng một bài test tổng hợp nhiều phần tử — ba phần tử được theo dõi thay vì một. Scrapling chỉ tìm lại được phần tử đầu tiên đã lưu, không phải cả ba. Điều đó không phải lỗi, cũng không phải bug; tài liệu định nghĩa auto-match là tracking theo từng phần tử, mỗi fingerprint ứng với một phần tử riêng, nên kết quả 1/3 theo mặc định là hành vi đúng như thiết kế. Nhưng điều đó cũng có nghĩa mô tả chính xác phải là “theo dõi phần tử có khả năng tự thích ứng,” chứ không phải “tự động phục hồi toàn bộ trang sau redesign.” Auto-match chỉ theo phần tử bạn bảo nó theo. Muốn xử lý nhiều phần tử hơn thì phải tự tinh chỉnh.
Sự khác biệt này quan trọng hơn vẻ ngoài của nó. “Sống sót qua thay đổi markup” nghe như một dòng tiêu đề. “Tiếp tục bám theo đúng phần tử đã fingerprint qua các thay đổi markup, phần còn lại do bạn xử lý” mới là khả năng thực tế bạn đang mua. Nếu bạn kỳ vọng vế đầu, bạn sẽ thất vọng. Nếu bạn hiểu là vế sau, nó làm việc khá gọn gàng.
Thiết lập: phần rắc rối mà ít ai cảnh báo
Đoạn này tôi mất thời gian thật, nên tôi kể trước để bạn khỏi vấp phải như tôi. pip install scrapling chỉ cài parser — và chỉ parser mà thôi. Ngay khi tôi viết from scrapling.fetchers import Fetcher, nó đứt vì chuỗi thiếu dependency: đầu tiên là curl_cffi, rồi playwright, rồi browserforge, mỗi thứ chỉ lộ ra sau khi tôi gỡ xong cái trước.
Cách xử lý là cài thêm extra: pip install "scrapling[fetchers]", hoặc chạy bước CLI scrapling install, thứ sẽ kéo cả bộ fetcher HTTP cộng browser về máy. Sau đó mọi thứ chạy bình thường. Nhưng chuỗi “cài cơ bản thì có vẻ ổn, rồi nổ ngay ở lần fetch đầu tiên” là có thật, và không có cảnh báo nào đập thẳng vào mặt bạn ngay từ đầu. Hãy tính luôn [fetchers] extra cùng đống dependency nặng của nó ngay từ lệnh đầu tiên, và bạn sẽ tránh được vòng lạc hướng đó.
Điều gì hoạt động tốt trong trích xuất HTTP thuần
Khi bộ fetcher đã sẵn sàng, đường trích xuất thông thường chạy rất ổn — recall đạt 1.0 trên toàn bộ bộ test:
| Bài test | Kết quả |
|---|---|
| Catalog tĩnh + phân trang | 12/12 sản phẩm |
| Trích xuất bài viết | tiêu đề + 3/3 đoạn văn |
| JSON API động | 8/8 mục |
| Books to Scrape (public) | 20 sản phẩm |
| Xử lý HTTP 500 | hiển thị status rõ ràng, không crash |
Nền tảng lxml thể hiện rất rõ ở đây. CSS và XPath đều hoạt động đúng như mong muốn, còn các pseudo-selector ::text / ::attr() giúp code trích xuất ngắn gọn, dễ đọc, thay vì biến nó thành một mớ lời gọi lồng nhau. Trường hợp 500 là một chi tiết nhỏ nhưng đáng chú ý — Fetcher hiển thị status code thay vì ném stack trace vào mặt tôi, và đó chính là ranh giới giữa một scraper có thể lên lịch chạy tự động với một scraper mà bạn phải ngồi canh.
Không có gì ở đây là hào nhoáng. Nó chỉ đúng, mà đúng thì rất đáng giá.
Những gì nó không làm được (và đó là chủ ý)

HTTP Fetcher không render JavaScript. Tôi thử đưa nó vào một fixture dựng bằng JS và nhận về 0 card; tương tự là 0 trên trang công khai Quotes to Scrape JS page. Đây không phải lỗi — HTTP Fetcher chỉ tải HTML, không điều khiển browser, nên nội dung được render phía client đơn giản là chưa xuất hiện ở thời điểm nó nhìn vào. Scrapling có một DynamicFetcher riêng (dựa trên browser) dành cho trang JS. Tôi chưa thử phần đó trong lần kiểm tra này, nên tôi sẽ không nói nó hoạt động ra sao. Chỉ là đừng ném đường HTTP vào một ứng dụng render phía client rồi mong thấy nội dung.
Ngoài ra còn có StealthyFetcher hướng tới chống phát hiện. Tôi xem nó như một vấn đề tuân thủ, hết. Không phải một tính năng để đem ra khoe. Bạn được phép scrape ở đâu, như thế nào phụ thuộc vào bạn và nền tảng pháp lý của bạn, còn bài review này chỉ kiểm tra năng lực trích xuất chứ không đánh giá khả năng né phát hiện. Tôi không chạy nó, nên cũng không chấm điểm nó.
Ưu và nhược điểm
Ưu điểm:
- Adaptive selectors thực sự phục hồi được một phần tử đã theo dõi sau khi đổi class, trong khi selector thường trả về 0 — lý do đặc trưng nhất để chọn Scrapling.
- Trích xuất HTTP trên trang tĩnh, bài viết và JSON API đạt recall 1.0.
- CSS/XPath dựa trên lxml gọn gàng, kèm pseudo-selector
::text/::attr()dễ đọc. - Xử lý HTTP 500 mềm mại — hiển thị status, không crash.
- Phiên bản test trùng với bản phát hành mới nhất, không lo lệch phiên bản.
- Giấy phép BSD-3-Clause thoáng, thân thiện với mục đích thương mại.
Nhược điểm:
- Auto-match chỉ theo dõi một phần tử đã lưu, không phải toàn bộ trang — bài test ba phần tử chỉ phục hồi được một.
pip install scraplingchỉ cài parser; fetcher cần[fetchers]extra cùng chuỗi dependency nặng, và tôi đã tự đụng phải điều đó.- HTTP Fetcher không render JavaScript; nội dung phía client cần
DynamicFetcherchạy bằng browser, phần này chưa được test ở đây. - Tính năng tự phục hồi được nhắc nhiều trong tiêu đề vẫn cần tinh chỉnh thủ công khi xử lý nhiều phần tử.
Ai nên dùng — và ai nên bỏ qua
Scrapling rất đáng giá nếu bạn đang duy trì scraper cho những website thay đổi giao diện thường xuyên và đã chán cảnh chỉ cần một class đổi tên là dữ liệu biến mất trong im lặng qua đêm. Nếu nỗi đau lặp lại của bạn là “selector cứ hỏng vài tuần một lần, và tôi chỉ muốn đúng phần tử quan trọng nhất luôn được tìm thấy,” thì đây nhắm thẳng vào bạn. Nó cũng là một extractor lxml gọn nhẹ, sạch sẽ cho trang tĩnh và JSON API ngay cả khi bạn không bao giờ bật lớp adaptive.
Hãy chỉnh lại kỳ vọng, hoặc tìm công cụ khác, trong hai trường hợp. Nếu bạn hy vọng adaptive selectors sẽ tự chữa lành cả một trang sau redesign — nó chỉ theo dõi phần tử, không dựng lại layout — bạn nên có một mô hình tư duy khác. Và nếu mục tiêu của bạn nặng JavaScript mà bạn không muốn dựng DynamicFetcher dựa trên browser, riêng đường HTTP sẽ không giải quyết được. Dù thế nào, khi cài thì nhớ thêm [fetchers] extra ngay từ lệnh đầu tiên.
Vị trí của một API AI scraping được quản lý
Scrapling là một thư viện open-source miễn phí, tự chạy và tự bảo trì. Bạn sở hữu code, chuỗi dependency và cả khâu tinh chỉnh — đổi lại bạn không phải trả phí theo request và giữ mọi thứ trong nhà. Đó là một lựa chọn thực tế, có thể bảo vệ được, và với nhiều team thì đó là lựa chọn đúng.
Câu hỏi đáng hỏi là ai sẽ sở hữu bài toán resilience. Scrapling trả lời rằng chính bạn: bạn tự fingerprint phần tử và tự tinh chỉnh tracking. Một managed AI scraping API trả lời khác đi — logic xử lý drift chuyển lên phía server. Đó là vị trí mà Thunderbit lấp vào cho các team kỹ thuật. POST /extract trả về JSON có cấu trúc theo JSON Schema bạn tự định nghĩa, với phần render, anti-bot và biến động markup được xử lý ở phía server; một cờ renderMode điều khiển mức độ trang được thực thi trước khi trích xuất. Thunderbit còn có MCP server cho AI agents và coding assistants — thunderbit_suggest_fields là miễn phí và chạy đầu tiên để lên kế hoạch trích xuất — cùng CLI qua npx @thunderbit/thunderbit-cli cho terminal, script và CI. Cùng một AI engine đứng sau cả ba giao diện đó.
Đánh đổi thật sự không phải là tốt hơn hay tệ hơn — mà là bạn muốn logic resilience sống ở đâu. Với Scrapling, bạn giữ nó trong code của mình, được fingerprint và tinh chỉnh bởi chính bạn, không tốn phí theo mỗi lần gọi, và chấp nhận phần bảo trì đi kèm. Với API được quản lý, bạn giao phần xử lý drift cho bên ngoài và trả tiền theo request. Quy mô nhỏ, tự host, và bạn thích tự kiểm soát việc tinh chỉnh? Quyền kiểm soát của Scrapling là câu trả lời đúng. Còn nếu bạn phải scale qua hàng trăm website và không muốn trông chừng fingerprint selector trên từng site, thì cách làm được quản lý sẽ xóa bỏ hẳn hạng mục bảo trì đó.
Nếu bạn đang so sánh các lựa chọn, full open-source scraper benchmark đặt Scrapling cạnh những công cụ khác trên cùng một bộ fixture, còn bài review Scrapy và bài review Colly bao quát thêm hai framework ưu tiên HTTP khác rất đáng xem.
Kết luận
Có nên dùng Scrapling không? Có — nếu bạn muốn một trình trích xuất Python open-source có điểm mạnh nổi bật là vẫn tìm được phần tử đã theo dõi ngay cả khi markup bên dưới nó thay đổi, và bạn hiểu rõ bản chất của “chiêu” này. Nó đã phục hồi được một phần tử mà selector lỗi không làm được, trên một lần đổi tên class vốn đủ khiến scraper thường mất dữ liệu trong im lặng. Phần trích xuất HTTP thuần sạch sẽ và đạt recall đầy đủ trên mọi fixture. Giấy phép thoáng và phiên bản tôi test là bản mới nhất.
Chỉ cần định lượng đúng khả năng của nó là bạn sẽ hài lòng. Nó theo dõi phần tử chứ không tự dựng lại cả trang — bài test ba phần tử chỉ phục hồi một. Hãy cài thêm [fetchers] extra ngay từ đầu, nếu không bạn sẽ đâm vào bức tường dependency như tôi. Và nếu trang của bạn cần JavaScript, đó là việc của fetcher chạy bằng browser, không phải đường HTTP. Trong các giới hạn đó, Scrapling làm chính xác điều mà nó nổi tiếng, và trong số các thư viện scraping Python, đây là công cụ thực sự đóng gói tính năng mà ai cũng hay gán nhầm cho nơi khác.
Dùng thử Thunderbit để trích xuất dữ liệu web Get Started Free
Câu hỏi thường gặp
Adaptive selectors của Scrapling có thật sự sống sót sau khi website redesign không?
Có — với phần tử đã được theo dõi thì nó có thể vượt qua việc đổi class, và điều này đã được kiểm chứng trong bài test. Sau khi tôi đổi product-name thành product-title, một selector thường trả về 0, còn cơ chế adaptive re-matching vẫn phục hồi được phần tử đã theo dõi. Tuy nhiên, nó theo dõi phần tử đã lưu chứ không tự dựng lại cả trang: bài test tổng hợp ba phần tử chỉ phục hồi được một. Hãy coi nó là cơ chế theo dõi phần tử có khả năng tự thích ứng, không phải phục hồi toàn bộ trang.
Vì sao pip install scrapling bị lỗi khi tôi import fetcher?
Vì bản cài cơ bản chỉ có parser. Khi import scrapling.fetchers, nó kéo theo một chuỗi dependency còn thiếu — curl_cffi, rồi playwright, rồi browserforge. Hãy chạy pip install "scrapling[fetchers]" (hoặc lệnh CLI scrapling install) để lấy trọn bộ fetcher, khi đó import sẽ hoạt động.
Scrapling có scrape được trang render bằng JavaScript không?
Không phải với HTTP Fetcher — nó trả về 0 ở cả một fixture JS và trang công khai Quotes JS, vì nó chỉ tải HTML mà không chạy browser. Scrapling có một DynamicFetcher riêng chạy bằng browser cho trang JS, nhưng phần đó chưa được test trong bài này, nên tôi chưa thể nhận xét hiệu năng của nó.
Scrapling có nhanh và chính xác cho trích xuất thông thường không? Trong quá trình test, nó rất chính xác — recall 1.0 trên catalog tĩnh, trang bài viết và JSON API, với CSS/XPath dựa trên lxml rõ ràng, gọn gàng. Nó cũng xử lý HTTP 500 bằng cách hiển thị status thay vì crash. Nếu bạn không dùng lớp adaptive, nó vẫn là một extractor nhẹ, tốt cho nội dung tĩnh.
Scrapling có miễn phí cho mục đích thương mại không? Có, nó dùng BSD-3-Clause, một giấy phép khá thoáng và thân thiện với thương mại. Như mọi khi, hãy kiểm tra lại license hiện tại trên repo trước khi build sản phẩm dựa trên nó.


