Botasaurus là gì
Botasaurus là một framework web scraping bằng Python của Omkar Cloud, được giới thiệu như bộ công cụ “tất cả trong một” để xây dựng scraper. Bạn chỉ cần viết một hàm bình thường, gắn @browser, @request hoặc @task, rồi framework sẽ lo luôn phần driver trình duyệt, HTTP client kiểu trình duyệt, cache, xử lý song song và xuất dữ liệu đa định dạng quanh nó. Đây là một meta-package, và đó mới là điểm đáng nói: pip install botasaurus không chỉ cài một thư viện vào môi trường của bạn, mà là kéo cả một cụm wheel chính chủ cùng một cây phụ thuộc khá rộng xuống. Chính chi tiết cơ học đó lại là thứ tôi đo được một cách trung thực và thú vị nhất.
Botasaurus quảng bá khá mạnh về khả năng chống phát hiện, nhưng đó lại là trục mà bài review này không chạm tới. Tôi kiểm kê framework — cài gì, import gì, có những method nào, nặng bao nhiêu, được cấp phép ra sao — thay vì đem nó đi đối đầu với bất kỳ lớp phòng vệ thực nào. Tất cả số liệu về footprint và import bên dưới đều đến từ pip, từ python -c "import ...", và từ việc soi các class đã được khởi tạo nhưng chưa bao giờ được yêu cầu tải trang; không có browser nào được mở chỉ để tạo ra các số đó. Về sau tôi có mở browser, nhưng chỉ đến những trang do chính tôi viết và phục vụ trên 127.0.0.1, để xem driver tự khai báo gì về mình và liệu nó có đọc được nội dung do JavaScript dựng lên hay không. Không hề có website thật nào được dùng, không có anti-bot service nào được gọi tới hay đo kiểm, và cũng không đụng tới CAPTCHA nào. Khả năng vượt qua các website thực tế nằm ngoài phạm vi theo thiết kế, và tôi thà nói rõ điều đó ngay từ đầu còn hơn vô tình ám chỉ một benchmark mà tôi chưa chạy.
Khi đã đặt ranh giới như vậy, phát hiện chính là câu chuyện về footprint, và đây là một câu chuyện khá nhẹ nhàng. Một bản cài sạch tạo ra thư mục site-packages nặng 122,3 MB với 44 package, trên một máy mà browser driver nằm ở trung tâm chỉ khoảng 4 MB. Framework không nặng vì driver nặng; nó nặng vì “all-in-one” nghĩa là nó kéo thêm numpy, lxml, gevent và hơn chục thứ khác vào một tác vụ vốn chỉ là lấy HTML. Và nửa sau của phát hiện là một con số mà nhiều người sẽ trích như một ưu điểm nhưng thực ra không nên tin quá: import botasaurus chỉ mất 0,08 ms, nghe như một framework cực nhẹ, nhưng thật ra chỉ là một cánh cửa trước gần như rỗng.
Botasaurus thực chất là gì
Botasaurus — omkarcloud/botasaurus trên GitHub, lúc tôi lấy metadata vào ngày 14/07/2026 thì có 5.561 sao, 486 fork và 58 issue đang mở — là một framework Python, không phải một thư viện đơn nhiệm. Phiên bản tôi thử là botasaurus 4.0.97 cho meta-package và botasaurus-driver 4.0.92 cho phần engine bên dưới. Meta-package khai báo requires-python >=3.7 (driver là >=3.5), còn classifier trên PyPI chỉ ghi hỗ trợ đến 3.11. Trên máy của tôi, nó cài được và vượt qua bài smoke test import trên Python 3.14.2. Đó là bằng chứng cho bản cài này, không phải cam kết tương thích cho mọi tính năng.
Nhãn phân loại rất đáng để chốt lại, vì nó quyết định thế nào mới được xem là “tốt”. Botasaurus nằm ở phía framework của phổ, cùng khu với Scrapy và Crawlee — bạn chấp nhận cấu trúc, decorator và quy ước của nó, đổi lại framework lo phần plumbing. Đó là một đề bài khác hẳn so với một driver chuyên dụng như nodriver, vốn chỉ đưa bạn một kết nối Chrome DevTools Protocol rồi lùi ra. Botasaurus có gói driver (chính là botasaurus-driver) nhưng bọc nó trong task runner, lớp cache, bộ serializer xuất dữ liệu và request client. Bạn không mua một driver đơn lẻ; bạn mua một workflow có quan điểm rõ ràng và bên trong có driver.
Ba decorator là toàn bộ thiết kế ở dạng thu nhỏ, và cả ba entry point đều tồn tại thật — tôi đã xác nhận botasaurus.browser.browser, botasaurus.request.request và botasaurus.task.task đều có thể import. @browser chạy hàm của bạn trên browser driver được “nhân hoá”. @request chạy nó trên một HTTP client nhẹ, được thiết kế để trông giống trình duyệt. @task là wrapper tổng quát cho mọi thứ không rõ ràng thuộc hai nhóm đầu. Chỉ cần gắn decorator, Botasaurus sẽ cung cấp toàn bộ phần còn lại: chạy song song, tái sử dụng driver, cache kết quả và ghi ra JSON, CSV, Excel, HTML. Ý tưởng này rất nhất quán. Còn bạn có muốn framework “ôm” scraper của mình đến mức đó hay không thì lại là chuyện khác — chuyện về gu sử dụng, không phải lỗi kỹ thuật.
Kết luận ngắn và ranh giới của nó
Botasaurus là một công cụ làm việc được, thiết kế ổn và làm đúng điều mà framework nên làm: rút ngắn phần việc quen thuộc. Mô hình decorator rất gọn. License MIT thực sự rộng rãi. Bản cài chạy lên không ồn ào. Nếu tôi chấm điểm độ thuận tay của API surface, nó sẽ được điểm khá tốt.
Điều tôi cứ suy nghĩ mãi là chính lời hứa nổi bật của framework — anti-detection — lại là thứ mà một bài review có trách nhiệm không thể đánh giá nếu không đem nó chĩa vào cơ chế phòng vệ trong môi trường production của ai đó. Driver đúng là có một số method được đặt tên theo hướng chống phát hiện; tôi xác nhận chúng tồn tại, nhưng không hề kiểm tra hành vi của chúng trên bất kỳ mục tiêu nào. Đó là tất cả những gì tôi dám khẳng định. Tôi không đưa nó vào một site được bảo vệ, không đo tỷ lệ thành công, không reverse-engineer cơ chế nào, và cũng sẽ không ám chỉ ba thứ đó bằng cách diễn đạt. Các method đó có thật trong class. Còn chúng làm gì ngoài thực tế thì là một bài review khác, không phải bài này.
Phần tiếp theo là một bản kiểm kê về năng lực, cài đặt, tài nguyên và license, cộng với những gì driver thể hiện trên một trang do tôi kiểm soát — một phạm vi hẹp hơn hầu hết các bài review về công cụ này, và chính sự hẹp đó là mục đích.
Nó tự khai báo gì về bản thân

Có một câu hỏi bạn có thể trả lời mà không cần đụng tới bất kỳ lớp phòng vệ thực nào: khi Botasaurus điều khiển một browser, browser đó tự tiết lộ gì về bản thân với trang mà nó đang mở? Tôi viết một trang đọc các tín hiệu rất hiển nhiên — navigator.webdriver, user-agent, platform, languages, số plugin và phần cứng, hình dạng của window.chrome, thông tin từ Permissions API, kích thước cửa sổ và màn hình — rồi phục vụ nó trên 127.0.0.1 và chạy bốn stack vào đó: Botasaurus, nodriver, cùng Playwright và Puppeteer nguyên bản làm đối chứng. Cả bốn đều điều khiển cùng một bản Chrome (Chrome for Testing 151.0.7922.10), nên mọi khác biệt nếu có là do thư viện chứ không phải do browser. Mỗi chế độ headless và headed được chạy ba lần. Mọi giá trị bên dưới đều giữ nguyên trong cả ba lượt.
| Stack | Chế độ | navigator.webdriver | Token user-agent | navigator.languages |
|---|---|---|---|---|
| Botasaurus 4.0.92 | headless | false | HeadlessChrome/151.0.0.0 | ["en-US"] |
| Botasaurus 4.0.92 | headed | false | Chrome/151.0.0.0 | ["en-US"] |
| nodriver 0.50.3 | headless / headed | false | HeadlessChrome/151 / Chrome/151 | ["en-US"] |
| Playwright 1.56.0 | headless / headed | true | HeadlessChrome/151 / Chrome/151 | ["en-US","en"] |
| Puppeteer 24.16.0 | headless / headed | true | HeadlessChrome/151 / Chrome/151 | ["en-US"] |
Những khác biệt phụ thuộc vào control bạn dùng để so sánh. So với Puppeteer nguyên bản, Botasaurus thay đổi navigator.webdriver; danh sách ngôn ngữ và geometry cửa sổ bằng 0 khớp nhau, còn user-agent chỉ khác về cách hiển thị version. So với Playwright nguyên bản, các khác biệt quan sát được còn bao gồm cả danh sách ngôn ngữ và geometry cửa sổ. So với nodriver, giá trị boolean và languages trùng nhau, còn user-agent chỉ khác về định dạng trường như đã thấy ở đây. Đây là các quan sát về thông tin mặc định được lộ ra, không phải một điểm số anti-detection.
Có hai chi tiết khiến nó đáng chú ý hơn một giá trị false đơn thuần. Thứ nhất là cách giá trị đó xuất hiện. Trong cả bốn stack, property này vẫn là native getter của chính browser trên Navigator.prototype — function get webdriver() { [native code] } — chứ không phải một own-property được ghim lên instance, và cũng không phải một function bị thay thế. Nghĩa là Botasaurus không vá property này sau khi page tải xong; giá trị được quyết định ngay lúc browser khởi chạy, còn bản thân property thì vẫn nguyên vẹn.
Chi tiết thứ hai là phần làm câu chuyện marketing bớt đẹp. Ở chế độ headless, user-agent của Botasaurus vẫn tự khai báo HeadlessChrome/151.0.0.0 — y hệt Puppeteer nguyên bản, y hệt Playwright nguyên bản. Chạy headed thì nó đổi thành Chrome/151.0.0.0, cũng giống hệt. Constructor Driver có tham số user_agent, nên bạn có thể gán bằng một keyword argument, nhưng cấu hình mặc định thì không hề che đi chuỗi tự nhận diện nổi tiếng nhất trong browser automation.
Gần như mọi thứ khác giữa bốn stack đều giống nhau, và điều đó nên được nói thật rõ vì nó thu hẹp câu chuyện — mọi property dưới đây đều đọc giống hệt nhau trên Botasaurus, nodriver, Playwright và Puppeteer:
| Thuộc tính | Giá trị giống nhau trên cả bốn stack |
|---|---|
platform | MacIntel |
vendor | Google Inc. |
| Plugins | năm |
| MIME types | hai |
pdfViewerEnabled | true |
| Logical cores | mười hai |
| Device memory báo cáo | 16 GB |
| Touch points | zero |
window.chrome | có, với app/csi/loadTimes và không có runtime |
| Chuỗi WebGL renderer | giống nhau trên cả bốn |
Cái “kinh điển” nơi Permissions API và Notification.permission mâu thuẫn với nhau không xuất hiện ở đâu cả — cả bốn đều báo default và prompt nhất quán. Tôi cũng rà document và window để tìm các dấu vết cdc_ kiểu cũ từng nổi tiếng ở các stack WebDriver đời trước: không có gì trong cả bốn.
Một điều cuối đáng biết trước khi bạn triển khai: dù nặng 122 MB, Botasaurus không đi kèm hay tự tải browser. find_chrome_executable() sẽ trỏ tới bản Chrome sẵn có trên máy bạn — trên máy của tôi là /Applications/Google Chrome.app, phiên bản 150.0.7871.187 — và đó cũng chính là version mà user-agent sẽ tiết lộ. Cụm máy của bạn sẽ tự khai báo đúng bản Chrome mà cụm đó đang cài, và với một framework có nhiều quan điểm như thế này, đây lại là một mặc định khá ít áp đặt.
Hãy nói đúng nó là gì và không phải gì. Đây là bản ghi về những gì một stack tự động tiết lộ khi không ai yêu cầu nó che giấu — hữu ích nếu bạn đứng về phía phòng thủ, hữu ích nếu bạn muốn biết chính công cụ của mình đang phát tín hiệu gì. Nó không phải là thước đo xem những tín hiệu đó có quan trọng với một dịch vụ cụ thể hay không. Tôi không test điều đó, và không có dòng nào bên trên nên được hiểu là ám chỉ một kết quả như vậy.
Một cách đọc mặc định “dễ tha thứ” hơn trên fixture này
Tự nhận diện là một chuyện; trả về đúng HTML mới là việc chính. Tôi chạy Botasaurus trên cùng một fixture ba lớp nội dung mà phần còn lại của repo benchmark này đang dùng, để số liệu có thể so sánh với mọi công cụ khác đã đo ở đây. Trang có ba thứ: A, một liên kết tĩnh với marker là literal có sẵn trong bytes được phục vụ; B, một node được dựng bởi inline script trong quá trình parse, với marker và URL được ghép từ các mảnh nên chỉ khi thực thi JavaScript mới thấy được; và C, một node được chèn vào 800 ms sau sự kiện load, cũng được ghép theo cách tương tự. Class C là phần “đối kháng” — một lần đọc ở thời điểm load event sẽ không thể thấy nó.
| Stack | Đọc mặc định | Với wait rõ ràng |
|---|---|---|
| Botasaurus 4.0.92 | 2 trên 3 (A + B, thiếu C) | 3 trên 3 |
| nodriver 0.50.3 | 2 trên 3 | 3 trên 3 |
| Playwright 1.56.0 | 2 trên 3 | 3 trên 3 |
| Puppeteer 24.16.0 | 2 trên 3 | 3 trên 3 |
Botasaurus đứng cùng nhóm với các công cụ nặng. driver.get() rồi đọc thẳng driver.page_html là một ảnh chụp tại thời điểm load: nó render JavaScript đúng — class B chứng minh điều đó, vì class B không hề tồn tại trong bytes được phục vụ — nhưng với độ trễ 800 ms thì nó bỏ lỡ class C. Thêm driver.wait_for_element("#delayed-injected") là bạn sẽ có cả ba. Kết quả ổn định qua ba lần lặp và ba lần chạy toàn bộ suite, không hề có flake.
Điều thú vị xuất hiện khi bạn quét độ trễ chèn để tìm điểm mà phép đọc mặc định của từng stack bắt đầu hụt:
| Class-C được chèn sau | Botasaurus | nodriver | Playwright | Puppeteer |
|---|---|---|---|---|
| 0 ms | tìm thấy | tìm thấy | tìm thấy | tìm thấy |
| 100 ms | tìm thấy | — | — | — |
| 200 ms | tìm thấy | — | — | — |
| 300 ms | tìm thấy | — | — | — |
| 400 ms trở lên | — | — | — | — |
Tất cả các stack còn lại mất class C ngay khi nội dung được chèn muộn hơn 100 ms sau load. Botasaurus vẫn bắt được tới 300 ms, và chỉ bỏ cuộc ở mốc 400. Đây là hệ quả của việc Botasaurus đúng nghĩa là một framework, và nguyên nhân nằm ngay trong constructor: wait_for_complete_page_load=True là mặc định, nên get() trả về muộn hơn đáng kể so với một load event trần. Cụ thể, lần đọc mặc định của nó tốn khoảng 401–431 ms wall-clock, trong khi nodriver là 119–129 ms và Puppeteer là 125–171 ms.
Trên fixture chèn trễ cục bộ này, cái giá phải trả vào khoảng 250 ms cho mỗi lần điều hướng để đổi lấy một snapshot mặc định muộn hơn. Nó bắt được nội dung được chèn muộn tới 300 ms sau load trong các lần chạy này; điều đó không chứng minh Botasaurus chính xác hơn trên mọi website. Nếu bạn viết scraper nhanh mà không đặt điều kiện wait cụ thể, khoảng đệm này có thể giúp tránh bỏ sót node đến muộn. Nhưng ở tần suất điều hướng cao, hoặc khi bạn đã đợi đúng một điều kiện rõ ràng rồi, nó chỉ còn là overhead.
Thêm hai số timing để thấy quy mô. Việc khởi động browser của Botasaurus gần như ngang với nodriver và Puppeteer, nhưng chậm hơn Playwright khá nhiều:
| Stack | Thời gian khởi chạy browser, qua các lần chạy |
|---|---|
| Botasaurus 4.0.92 | 986–1151 ms |
| nodriver 0.50.3 | 910–1583 ms |
| Puppeteer 24.16.0 | 969–1008 ms |
| Playwright 1.56.0 | 282–365 ms |
Và wait_for_element() gần như không phát sinh thêm chi phí cho tới độ trễ 300 ms — vì get() đã tự chờ đủ lâu để qua mốc chèn — sau đó tăng lên khoảng 1,42 s ở 400–800 ms và 2,43 s ở 1500 ms.
Câu hỏi 122 MB: một meta-package thực sự cài gì

Hãy nhìn vào phép tính, vì đây là thứ hữu ích nhất tôi có thể đưa cho bạn. Một lệnh pip install botasaurus sạch trong một virtual environment mới tạo ra cây site-packages nặng 122,3 MB với 44 package dist-info. Nếu bỏ riêng pip ra (10,9 MB, tức là overhead của venv chứ không phải thứ Botasaurus yêu cầu), bạn còn khoảng 111 MB framework và các phụ thuộc. Browser driver — thành phần làm phần tự động hóa browser thực sự — chỉ khoảng 4 MB trong đó. Vậy là khoảng 107 MB còn lại là mọi thứ khác mà meta-package cho rằng bạn cần.
Nó đi vào đâu? Chỉ riêng năm phụ thuộc transitive nặng nhất đã chiếm phần lớn dung lượng rồi (mỗi dòng là một entry install_footprint.heaviest_deps_mb trong artifacts/raw/runs/resource_baseline.run1.json; tổng là phép cộng của tôi, không phải field có sẵn trong file):
| Package | Dung lượng trên đĩa |
|---|---|
| numpy | 30,9 MB |
| lxml | 19,2 MB |
| botasaurus_requests | 12,6 MB |
| gevent | 11,3 MB |
| pygments | 8,4 MB |
| Năm package cộng lại | 82,4 MB (30,9 + 19,2 + 12,6 + 11,3 + 8,4) |
numpy là món lớn nhất, và chính nó khiến tôi nhướn mày — đây là một thư viện đại số tuyến tính lại nằm trong một công cụ có nhiệm vụ lấy và phân tích trang web. Không phải là sai, nhưng framework thì hay tích dần các phụ thuộc tiện ích, và có vẻ như một phần trong cây phụ thuộc này cần tới toán học mảng. Chỉ là hơi nhiều máy móc cho một việc vặt.
Để so sánh quy mô, driver tập trung như nodriver chỉ nặng khoảng 17,2 MB với 6 package trên cùng một máy — tức là nhẹ hơn khoảng 7 lần. (Con số này không đến từ bản cài ở đây: đó là install_footprint.site_packages_total_mb trong artifacts/raw/runs/resource_baseline.run1.json của nodriver, đo trong một lần chạy tách biệt trên cùng host, và 122,3 ÷ 17,2 = 7,1.) Cả hai con số đều không phải lỗi; đây chỉ là chênh lệch chi phí cơ học giữa một framework “đóng gói sẵn mọi thứ” và một driver chuyên dụng. Trong container, footprint site-packages được đo sẽ cộng thẳng vào lớp ứng dụng. Nó không phải toàn bộ kích thước image, và thử nghiệm này cũng không đo thời gian build hay cold-deploy.
Có thêm một chi tiết footprint nữa: trong lúc introspection, lần đầu gọi from botasaurus.request import request đã kích hoạt một lần tải xuống khoảng 12,8 MB. Lần chạy được ghi nhận không xác định đủ rõ artifact và đích đến để xem con số đó là một phần tăng footprint cài đặt ổn định. Tuy vậy, nó cho thấy đường code này có thể cần truy cập mạng ở lần dùng đầu tiên, điều đáng kiểm tra lại trong image của bạn trước khi triển khai trong môi trường air-gapped.
Con số import nói dối
Timing import lúc cold-start là nơi một cách đọc số liệu hời hợt dễ đi sai. Đo trên bảy subprocess import mới hoàn toàn, import botasaurus ở cấp top-level chỉ mất median 0,08 ms. Nếu trích riêng con số đó, bạn sẽ tưởng đây là framework nhẹ nhất trong nhóm.
Không phải vậy. Nó nhanh vì gần như không có gì ở đó cả. Package botasaurus cấp top-level không có __version__ và namespace public gần như rỗng — import nó gần như chẳng làm gì, vì nó gần như chẳng chứa gì. Con số thực sự quan trọng với một CLI tool hay serverless cold start là import engine: from botasaurus_driver import Driver mất khoảng 135 ms, ổn định trong phạm vi vài mili-giây qua các lần chạy. Đó mới là chi phí cố định thật sự bạn phải trả trước khi tải được bất kỳ trang nào. Và sau khi module driver đã được import, bộ nhớ cư trú vào khoảng 29–30 MB — vẫn là trước khi có bất kỳ tiến trình Chrome nào tồn tại. Khi launch một browser thực sự, con số này tăng đáng kể; tôi không đo memory khi browser đang chạy, nên sẽ không gán số cụ thể ở đây.
Bài học nhỏ nhưng sắc: import botasaurus có vẻ tức thì không phải vì framework rẻ, mà vì top-level package rỗng. Nếu bạn đang tính cold start, hãy đo đúng import mà bạn sẽ phụ thuộc.
Hình dạng API: 99 method sau cánh cửa trước gần như rỗng

Class Driver của engine có 99 method public — một bề mặt rất rộng, bao trùm navigation, truy vấn element, cookies và local storage, thao tác chuột và bàn phím, chụp ảnh màn hình, quản lý tab, passthrough CDP và upload file. Constructor nhận 18 tham số, đó là bản đồ khá đầy đủ của bề mặt có thể tinh chỉnh: headless, proxy, profile, tiny_profile, block_images, block_images_and_css, wait_for_complete_page_load, chrome_executable_path, extensions, arguments, user_agent, window_size, lang và vài tham số nữa. Ở cấp API khởi tạo, nó bao quát các điều khiển thường thấy của một browser wrapper.
Điểm cần lưu ý nằm ở top-level, và nó vô hại nhưng có thật. import botasaurus cho bạn một namespace gần như trống — không có __version__, hầu như không có public name nào ở cấp cao nhất. Mọi thứ bạn thực sự dùng đều nằm trong submodule: from botasaurus.browser import browser, Driver, from botasaurus.request import request, from botasaurus.task import task. Nếu bạn đi tìm botasaurus.__version__ để ghi log xem mình đang chạy bản nào, bạn sẽ không thấy; bạn phải dùng importlib.metadata thay thế. Không có gì ở đây làm hỏng cả. Chỉ là đây không phải layout mà đa số dev Python sẽ phản xạ đoán ra ngay, và biết điều đó sẽ giúp bạn khỏi mất năm phút bối rối trong ngày đầu tiên.
Một quan sát về tài liệu cho các method tôi đã nhắc phía trên cũng hoàn toàn hợp lệ trong ranh giới này: trong số các method có tên mang ý chống phát hiện mà tôi đã xác định, chỉ 2 method có docstring ngay trong code. Phần còn lại chỉ tự mô tả bằng tên, còn tài liệu chi tiết nằm trên site docs bên ngoài chứ không nằm trong source đã cài. Đây là ghi chú về vị trí của tài liệu, không phải phán xét chất lượng — nhiều thư viện tốt vẫn để phần prose docs ở ngoài code — nhưng nếu quy trình của bạn là “đọc source để hiểu method”, thì phần lớn bề mặt này chỉ cho bạn cái tên và không hơn.
License: MIT từ trên xuống dưới, kể cả driver
Cả meta-package lẫn botasaurus-driver đều khai báo MIT, kèm classifier chuẩn License :: OSI Approved :: MIT License. MIT là giấy phép rất thoáng: không có nghĩa vụ copyleft, không bắt bạn mở mã nguồn riêng, và giảm ma sát đáng kể cho việc dùng trong môi trường thương mại. Đây là một khác biệt thật sự và không hề nhỏ so với nodriver, driver chống phát hiện ở gần đó, vốn phát hành theo AGPL-3.0 — một license copyleft có điều khoản về việc sử dụng qua mạng khiến nhiều bộ phận pháp chế dè chừng. Nếu license là điều kiện chặn đầu vào của bạn, thì MIT của Botasaurus là một điểm cộng thực sự.
Nhưng lại quay về hình dạng meta-package. MIT hào phóng đó chỉ áp dụng cho các wheel chính chủ do Botasaurus phát hành. Nó không tự động bảo chứng cho khoảng 40 package phụ thuộc transitive mà bản cài kéo xuống, mỗi package lại có license riêng. Tôi đã xác nhận MIT ở cấp cao nhất cho các package Omkar Cloud phát hành; tôi không audit license của từng dependency trong cây. Với dự án sở thích cá nhân, sự khác biệt này thường không quan trọng. Nhưng nếu triển khai cả cây phụ thuộc vào doanh nghiệp coi trọng software bill of materials, thì cây 40 package đó là thứ bạn nên đưa qua scanner license của riêng mình trước khi chốt — không phải vì tôi phát hiện vấn đề gì, mà vì tôi không xem, và meta-package chính là nơi một license bất ngờ rất dễ ẩn mình.
Ưu và nhược điểm
Ưu điểm:
- Thiết kế ba decorator gọn gàng (
@browser/@request/@task), cả ba entry point đều xác nhận tồn tại — framework rút ngắn phần việc phổ biến. - License MIT cho cả meta-package lẫn driver, khác biệt rõ với AGPL-3.0 của một driver tương đương. Thoáng, phù hợp thương mại, không copyleft.
- Bề mặt driver rất rộng: 99 method public và constructor 18 tham số, bao quát hầu hết nhu cầu browser automation thường gặp.
- Cài và vượt smoke test import trên Python 3.14.2 và 3.12.13, vượt ngoài danh sách classifier công bố (dừng ở 3.11); tương thích runtime thì chưa được chứng minh.
- Có cửa sổ độ chính xác của default read rộng nhất trong các stack tôi đo: vẫn bắt được nội dung được chèn sau load 300 ms, trong khi nodriver, Playwright và Puppeteer đều bỏ lỡ ở 100 ms.
wait_for_complete_page_load=Truethực sự có tác dụng. navigator.webdrivermặc định trả vềfalsethay vìtruenhư các control nguyên bản, nhưng không phải bằng cách vá property — descriptor vẫn là native getter của browser.- Mọi thứ cần thiết đều được đóng gói sẵn — cache, chạy song song, tái sử dụng driver và xuất JSON/CSV/Excel/HTML đều có sẵn trong framework.
Nhược điểm:
- Nặng trên đĩa: 122,3 MB với 44 package, khoảng 7 lần một driver tập trung, chủ yếu do các phụ thuộc như numpy (30,9 MB) và lxml (19,2 MB) chứ không phải do driver ~4 MB.
- Con số import top-level 0,08 ms nghe đẹp nhưng gây hiểu lầm; import engine mà bạn thực sự cần là khoảng 135 ms, và sau khi import thì memory cư trú khoảng 29–30 MB trước khi browser nào chạy.
- Cửa sổ default read rộng hơn đó không miễn phí: mỗi lần navigate-and-read tốn khoảng 401–431 ms, so với 119–129 ms của một driver gọn hơn trên cùng trang và cùng Chrome.
- Chạy headless vẫn tự khai báo
HeadlessChrometrong user-agent theo mặc định, giống hệt control nguyên bản; tham sốuser_agentcó tồn tại nhưng không ai set hộ bạn cả. - Dù nặng 122 MB, nó vẫn không ship browser; nó điều khiển Chrome có sẵn trên host, nên phiên bản browser được tiết lộ phụ thuộc vào máy của bạn.
- Lần đầu dùng
@requesttrong chạy này đã kích hoạt một lần tải về khoảng 12,8 MB; artifact và đích đến không được ghi nhận đủ rõ để coi đó là tăng footprint cài đặt ổn định. - Package top-level gần như trống và không có
__version__; API thật và version đều nằm ở chỗ không quá trực quan. - Phần lớn các method mang tên chống phát hiện không có docstring ngay trong code, nên đọc source chỉ biết tên chứ chưa biết hành vi.
Ngoài phạm vi mà các con số này bao phủ, và vì thế chưa được thử ở đây: mọi thứ liên quan đến hiệu quả chống bot trong thế giới thật (theo thiết kế là ngoài phạm vi), memory theo từng trang, xử lý proxy và profile, throughput ở quy mô lớn, và bất kỳ nền tảng nào ngoài macOS arm64. Các số liệu footprint và import được tạo ra mà không khởi động browser; các số về recall và disclosure đến từ browser chỉ nói chuyện với fixture trên 127.0.0.1.
Nó hợp với ai, và ai nên bỏ qua
Botasaurus hợp nếu bạn muốn một framework hơn là một mảnh rời. Nếu bạn bắt đầu một dự án scraping từ một file trống và muốn nhận luôn cấu trúc thay vì tự ráp — decorator cho entry point, cache và parallelism đã lo, bộ xuất dữ liệu đã có sẵn — thì đây là một lựa chọn hợp lý, có license MIT. MIT là thoáng, nhưng mô hình sử dụng và phân phối của bạn vẫn cần quy trình compliance bình thường. Những team đã quen tư duy kiểu Scrapy hay Crawlee sẽ thấy cách tiếp cận này rất quen thuộc.
Nên bỏ qua, hoặc ít nhất là nghĩ kỹ hơn, nếu mục tiêu triển khai của bạn nhạy cảm về dung lượng. Một bản cài 122 MB với numpy và gevent trong cây phụ thuộc là khá nhiều để đưa vào một container mỏng khi nhu cầu thực sự chỉ là “điều khiển browser và lấy vài field”. Một driver gọn sẽ cho bạn phần automation với chi phí nhẹ hơn nhiều, đổi lại bạn phải tự viết phần plumbing xung quanh. Và bỏ qua hoàn toàn nếu điều bạn muốn là một câu trả lời về hiệu quả chống phát hiện, vì đó đúng là thứ tôi cố ý không test — bạn sẽ phải tin vào một lời marketing mà tôi không xác nhận cũng không phủ định.
Phương án thay thế, và Thunderbit nằm ở đâu
Cách nói thật trước: Botasaurus là miễn phí, dùng MIT, và tự host. Bạn tự chạy fleet, tự quản cập nhật, và tự sở hữu cả cây phụ thuộc của nó — toàn bộ 44 package — bao gồm vá lỗi, tuân thủ license và bất cứ điều gì numpy quyết định làm ở bản phát hành tương lai. Với rất nhiều team, quyền sở hữu đó mới là thứ họ muốn, và không dịch vụ quản lý nào có thể thắng “một framework bạn đã có sẵn” nếu chỉ nhìn chi phí thuần.
Trong thế giới open source, so sánh hữu ích là theo hình dạng. Nếu bạn đang ở phía framework như Botasaurus, Scrapy và Crawlee là những đối thủ tự nhiên nên cân nhắc — trưởng thành, có quan điểm rõ ràng và mỗi bên đều có quy ước riêng cần chấp nhận. Nếu bạn muốn Markdown sẵn sàng cho LLM từ trang web thay vì một framework để tổ chức crawler, Crawl4AI và công cụ tập trung nội dung Trafilatura nhắm đúng việc đó. Nếu bạn thích hướng Python và anti-detect nhưng muốn thứ nhẹ hơn một meta-package, Scrapling đáng xem thử, và nếu bạn có thể dùng ngôn ngữ biên dịch, thư viện Go Colly đổi việc render JavaScript lấy tốc độ và footprint rất nhỏ. Bất kỳ lựa chọn nào điều khiển browser, kể cả Botasaurus, đều mang theo profile chi phí mà so sánh Playwright vs Puppeteer của chúng tôi đã mô tả — browser thật không hề rẻ để chạy, và đó là lý do framework bao quanh chúng mới nặng như vậy.
Khi một API được quản lý tham gia vào, đó là một điểm khác trên cùng pipeline. Botasaurus là developer stack tự host; Thunderbit bán giải pháp được quản lý, hướng tới cùng nhóm developer. Open API chỉ có hai endpoint. POST /distill (1 credit) trả về một trang dưới dạng Markdown sạch, sẵn sàng cho LLM, với rendering và anti-bot được xử lý ở phía server, nên bạn không phải provision browser hay dependency tree nào cả. POST /extract (20 credits) trả về JSON có cấu trúc theo JSON Schema do bạn định nghĩa, với renderMode đặt là none, basic hoặc full tùy trang thực sự cần browser đến đâu. Cả hai đều có bản batch cho tối đa một trăm URL cùng lúc. Có một MCP server cho agent và coding assistant — thunderbit_suggest_fields là miễn phí và sẽ cho bạn biết trang đang lộ ra những field nào trước khi bạn tốn credit — và có cả CLI qua npx @thunderbit/thunderbit-cli cho cron và CI. Với người không phải developer và không muốn đụng tới các thứ này, Chrome extension chạy cùng engine nhưng theo kiểu no-code, còn các bài hướng dẫn trên kênh YouTube của Thunderbit bao quát những workflow phổ biến.
Đánh đổi nằm ở chỗ công việc được đặt ở đâu, chứ không phải công cụ nào “tốt hơn”. Botasaurus giữ framework, browser fleet, dependencies, infrastructure và bảo trì ở phía bạn mà không tính phí vendor theo từng request; nhưng compute, bandwidth, proxy và vận hành vẫn tốn tiền. Một API được quản lý sẽ gỡ phần rendering và đầu ra có dạng schema khỏi tay bạn và tính phí theo mỗi lần gọi — bạn có thể đem so con số đó với một setup tự host trên trang pricing.
Thử Thunderbit để trích xuất dữ liệu web
Kết luận
Botasaurus là một lựa chọn hợp lý nếu bạn muốn một framework Python all-in-one và chấp nhận footprint phụ thuộc của nó. Thiết kế ba decorator rất gọn, bề mặt driver rất rộng, và nó đã vượt smoke test cài đặt/import trên các bản Python mới hơn những gì classifier công bố. Trên fixture cục bộ của tôi, snapshot mặc định của nó còn bắt được nội dung được chèn sau load 300 ms, trong khi các stack khác tôi thử đều bỏ lỡ ở 100 ms; đó là kết quả của fixture, không phải một bảng xếp hạng chung.
Nhưng hãy định cỡ các tuyên bố cho đúng. Đây là một bản cài 122 MB với 44 package, trong đó driver chỉ khoảng 4 MB còn lại là numpy, lxml, gevent và các thứ khác — tức khoảng 7 lần một driver tập trung, và trọng lượng đó chui thẳng vào container image và thời gian deploy lạnh của bạn. Con số 0,08 ms ở cấp top-level import là một cánh cửa trước rỗng, không phải một framework nhẹ; con số ~135 ms của engine import mới là thứ tính vào chi phí. Cửa sổ default read dễ dãi đó tốn khoảng 250 ms trên mỗi lần điều hướng. Và với câu hỏi về default disclosure mà tôi thực sự có thể trả lời, bức tranh còn hẹp hơn lời quảng bá: chỉ một boolean khác với Puppeteer nguyên bản, user-agent ở chế độ headless vẫn nói HeadlessChrome, và mọi thuộc tính khác tôi đo đều giống hệt giữa bốn stack. Lời hứa anti-detection bán công cụ là thứ duy nhất bài review này không chấm điểm — tôi xác nhận các method đó tồn tại, chạy driver trên một trang do chính tôi kiểm soát, rồi dừng lại ở đó, có chủ đích. Biết footprint, bỏ qua con số import đẹp mắt, và coi marketing về stealth là một câu hỏi còn để ngỏ, thì Botasaurus là một framework công bằng và làm đúng vai trò của một framework. Nếu bạn chờ một driver nhẹ như lông hồng, bạn sẽ bất ngờ lúc docker build chạy.
Thử Thunderbit để trích xuất dữ liệu web Get Started Free
Câu hỏi thường gặp
Botasaurus có miễn phí không, và nó dùng license gì?
Có, nó miễn phí và dùng license MIT — cả meta-package botasaurus lẫn engine botasaurus-driver đều mang classifier MIT được OSI chấp thuận. MIT là giấy phép thoáng nên không có nghĩa vụ copyleft và khá thân thiện với mục đích thương mại, khác hẳn với một số driver chống phát hiện tương đương đang dùng AGPL-3.0. Một lưu ý: MIT chỉ bao phủ các package chính chủ của Omkar Cloud, chứ không tự động bao trùm khoảng 40 dependency transitive mà bản cài kéo xuống, nên trước khi triển khai trong doanh nghiệp cho cả cây phụ thuộc, bạn vẫn nên chạy scanner license của riêng mình.
Bản cài Botasaurus lớn đến mức nào, và vì sao import botasaurus lại trông như tức thì?
Một lệnh pip install botasaurus sạch đã tạo ra cây site-packages nặng 122,3 MB với 44 package trên máy của tôi. Bản thân browser driver chỉ khoảng 4 MB — phần nặng đến từ việc meta-package kéo theo cả một cây phụ thuộc rộng, đứng đầu là numpy (30,9 MB), lxml (19,2 MB), botasaurus_requests (12,6 MB), gevent (11,3 MB) và pygments (8,4 MB). Nó xấp xỉ nặng gấp 7 lần một driver tập trung như nodriver trên cùng máy. Không có gì ở đây là lỗi; đó là cái giá của “batteries included”, và nó quan trọng nhất với kích thước image container. Còn thời gian import mới là nơi footprint này ẩn đi: import botasaurus đo được khoảng 0,08 ms, nhưng chỉ vì package cấp top-level gần như rỗng — không có __version__, gần như không có tên public nào, nên import nó gần như chẳng làm gì. Phần import tốn chi phí là engine, from botasaurus_driver import Driver, khoảng 135 ms, với memory cư trú vào khoảng 29–30 MB sau import và trước khi browser nào được mở. Nếu bạn đang tối ưu serverless cold start, hãy đo import của engine chứ đừng đo gói top-level rỗng đó.
Botasaurus tự tiết lộ gì về bản thân, và nó đã được thử với hệ anti-bot thật chưa?
Phần đầu đã được đo; phần sau thì tôi cố ý không làm. Trên một trang tôi phục vụ từ 127.0.0.1, khi điều khiển cùng bản Chrome như các control: navigator.webdriver trả về false, trong khi Playwright nguyên bản và Puppeteer nguyên bản đều báo true. Giá trị đó được set ngay lúc browser khởi động, chứ không phải do vá property — descriptor vẫn là native getter của Chrome. Ngoài boolean đó ra, gần như mọi thứ đều khớp với control: cùng platform string, năm plugin, mười hai core, 16 GB device memory báo cáo, cùng dạng window.chrome, không có mâu thuẫn từ Permissions API, và cũng không thấy dấu vết cdc_ kiểu cũ trên document hay window. Chạy headless vẫn tự khai báo HeadlessChrome/151.0.0.0 trong user-agent, y hệt hai control — constructor Driver có tham số user_agent, nhưng mặc định thì không ai set giúp bạn cả. Còn về hiệu quả thực tế: tôi không bao giờ đưa driver vào một site live, không gọi tới anti-bot service nào, và cũng không đụng tới CAPTCHA nào. Driver đúng là có một loạt method mang tên chống phát hiện, tôi đã xác nhận chúng tồn tại, nhưng tôi không gọi chúng, không kiểm tra hành vi của chúng trên bất kỳ mục tiêu nào, không đo tỷ lệ thành công, và cũng không mô tả cơ chế bên trong. Bảng disclosure ở trên chỉ cho bạn biết stack tự khai gì, chứ không nói ai đang lắng nghe hay họ phản ứng thế nào.
Botasaurus có xử lý đúng nội dung render bằng JavaScript không?
Có, và mặc định của nó còn dễ dãi hơn đa số. Trên một fixture có ba lớp nội dung, driver.get() + driver.page_html trả về 2 trên 3 khi độ trễ chèn là 800 ms — nó render JavaScript đúng, nhưng đọc trước khi nội dung đến quá muộn — còn driver.wait_for_element() thì trả về đủ 3 trên 3. Điểm đáng chú ý là nơi phép đọc mặc định dừng lại: Botasaurus vẫn bắt được nội dung được chèn sau load 300 ms, trong khi nodriver, Playwright và Puppeteer đều bỏ lỡ ở 100 ms. Đó là do wait_for_complete_page_load=True trong constructor, và nó khiến mỗi lần navigate tốn thêm khoảng 250 ms.
Sau khi cài xong thì import và dùng Botasaurus như thế nào?
Không giống kiểu bạn đoán đâu. Namespace botasaurus cấp top-level gần như rỗng, nên API thật nằm trong các submodule: from botasaurus.browser import browser, Driver, from botasaurus.request import request, và from botasaurus.task import task. Bạn gắn @browser, @request hoặc @task lên một hàm bình thường là framework sẽ lo phần driver, cache và xuất dữ liệu xung quanh. Vì không có botasaurus.__version__, nếu muốn ghi log build đang chạy thì hãy dùng importlib.metadata.


