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.

Lầ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:

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

Nguồn tham khảo