Crawl4AI — bào web thành data sạch trong một buổi chiều
Hướng dẫn triển khai Crawl4AI từ cài đặt đến structured extraction — đủ để chạy pipeline crawl-to-data trong một buổi chiều.
Bụi WireLần cuối bạn scrape một trang web bằng requests + BeautifulSoup là khi nào — và bạn có nhớ mình ngồi debug selector bao lâu không?
Mình hỏi thật, vì tuần trước mình cũng y vậy. Cần lấy data từ khoảng 30 trang sản phẩm, chạy script cũ thì gặp ngay trang render bằng JavaScript, BeautifulSoup trả về HTML trống trơn. Đúng lúc đó mình thử Crawl4AI — thư viện open-source chuyên crawl web, render JS, rồi trả về markdown sạch hoặc JSON có cấu trúc. Setup xong trong đúng một buổi chiều.
Thực ra nó giải quyết cái gì?
Nói thẳng ra thì Crawl4AI là một lớp abstraction bên trên Playwright (headless browser), nhưng nó bổ sung mấy thứ mà tự build sẽ tốn cả tuần:
- Markdown generation: crawl xong, trả về markdown sạch thay vì HTML thô. Có cả "fit markdown" — lọc bỏ noise chỉ giữ nội dung chính.
- BM25 filtering: truyền vào một query, Crawl4AI chỉ giữ đoạn liên quan. Như bào phẳng mặt gỗ — gỗ thô vào, mặt nhẵn ra, dăm bào bay hết.
- CSS-based extraction: định nghĩa schema bằng CSS selector, lấy data có cấu trúc mà không cần gọi LLM.
- JS execution: chạy JavaScript trước khi extract — bấm nút "Load more", scroll page, đợi AJAX xong mới lấy.
- LLM-based extraction: kết hợp model AI để biến nội dung phi cấu trúc thành JSON gọn gàng.
- Concurrent crawling: crawl nhiều trang song song, tiết kiệm thời gian đáng kể.
Bắt tay vào — 4 bước, 20 phút
Bước 1 — Cài đặt
pip install crawl4ai
playwright install chromium
Trên Linux server, thêm dependencies cho Chromium:
apt-get install -y libnss3 libatk-bridge2.0-0 libxcomposite1
Bước 2 — Crawl cơ bản, lấy markdown
import asyncio
from crawl4ai import AsyncWebCrawler
async def basic_crawl():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(url="https://example.com")
print(result.markdown)
asyncio.run(basic_crawl())
Không cần config gì thêm, bạn đã có nội dung trang web dưới dạng markdown sạch sẽ.
Bước 3 — Extract có cấu trúc bằng CSS selector
Giả sử team bạn cần lấy danh sách bài viết từ một trang tin:
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
import json
schema = {
"name": "Articles",
"baseSelector": "article.post",
"fields": [
{"name": "title", "selector": "h2", "type": "text"},
{"name": "link", "selector": "h2 a",
"type": "attribute", "attribute": "href"},
{"name": "summary", "selector": "p.excerpt", "type": "text"}
]
}
strategy = JsonCssExtractionStrategy(schema)
async def structured_crawl():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://news-site.com",
extraction_strategy=strategy
)
data = json.loads(result.extracted_content)
print(json.dumps(data, indent=2, ensure_ascii=False))
asyncio.run(structured_crawl())
Đây là kiểu đục mộng gỗ — bạn khoét đúng chỗ cần, miếng ghép ra vừa khít, không thừa không thiếu.
Bước 4 — Trang SPA chạy JavaScript? Xử lý được.
async def js_crawl():
async with AsyncWebCrawler() as crawler:
result = await crawler.arun(
url="https://spa-site.com/products",
js_code="window.scrollTo(0, document.body.scrollHeight);",
wait_for="css:.product-card",
)
print(result.markdown)
asyncio.run(js_crawl())
Truyền js_code để chạy JavaScript trước khi extract, wait_for để đợi element xuất hiện. Trang SPA hay infinite scroll đều xử lý gọn.
Hai kịch bản rất thực cho team Việt Nam
Kịch bản 1 — Monitoring giá đối thủ
Giả sử bạn đang ở một startup e-commerce, cần theo dõi giá khoảng 200 SKU từ 3 đối thủ mỗi ngày. Trước đây team dùng Scrapy + custom parser cho từng site — mỗi lần đối thủ đổi layout là parser vỡ. Chuyển sang Crawl4AI với CSS extraction strategy, bạn chỉ cần cập nhật schema selector thay vì rewrite cả spider. Kết hợp concurrent crawling, 200 trang chạy song song trong vài phút thay vì tuần tự cả tiếng.
Kịch bản 2 — Xây RAG pipeline từ docs phân tán
Team AI của bạn cần index documentation từ nhiều nguồn — Confluence, Notion public pages, blog kỹ thuật nội bộ. Như mình đã chia sẻ trong các bài về RAG trước đây, chất lượng data đầu vào quyết định chất lượng retrieval. Crawl4AI với fit markdown + BM25 filtering cho bạn nội dung đã lọc sạch, đúng chủ đề, sẵn sàng chunk và đưa vào vector store — không cần viết thêm bước cleaning riêng.
Mấy cái bẫy mình từng dính — để bạn khỏi giẫm lại
Bẫy 1: Quên handle session. Mình crawl một trang cần login, request đầu OK nhờ cookie cũ, request thứ hai bị redirect thẳng về login page. Crawl4AI có session management — bạn truyền session_id để giữ state giữa các request. Quên cái này là trang 1 lấy ngon, trang 2 toàn HTML form đăng nhập.
Bẫy 2: Dùng LLM extraction cho mọi thứ. LLM-based extraction mạnh thật, nhưng nếu data có cấu trúc rõ ràng (bảng giá, danh sách sản phẩm, ranking), CSS selector nhanh hơn gấp nhiều lần và không tốn tiền API. Chỉ nên dùng LLM extraction khi nội dung phi cấu trúc — review, bài báo, comment. Tốn tiền mà không thêm giá trị gì thì phí.
Bẫy 3: Crawl ào ạt không rate limit. Concurrent crawling mạnh, nhưng 50 request đồng thời vào một domain thì bạn bị block nhanh lắm. Mình từng bị một trang e-commerce ban IP vì quên set max_concurrent — phải đợi 24 tiếng mới crawl lại được. Nhớ thêm delay và giới hạn concurrency per domain.
Khi nào nên — và không nên — chọn Crawl4AI
Crawl4AI hợp khi bạn cần crawl vài trăm đến vài nghìn trang, đặc biệt trang render bằng JavaScript. Nếu bạn crawl hàng triệu trang, Scrapy vẫn nhẹ hơn nhiều vì không chạy headless browser — resource tiết kiệm hơn đáng kể.
Bản chất thật sự: Crawl4AI không thay thế Scrapy, nó lấp cái khoảng trống giữa "BeautifulSoup không đủ" và "Scrapy + Splash quá phức tạp". Đúng cái chỗ mà phần lớn team nhỏ đang đứng.
Đừng tin mình, thử đi rồi biết — cài Crawl4AI, crawl một trang bạn đang cần data, so sánh với workflow cũ. Cái khoảnh khắc nhìn markdown sạch chạy ra thay vì HTML loạn xạ — khá là sướng.
---
Bụi Wire — nghiện đọc release notes lúc 2 giờ sáng