Biến trang báo thành Markdown sạch để đưa cho AI đọc
Việc cần làm là đưa tin tức cho model đọc. Project của mình có một job chạy theo lịch, lấy bài báo từ RSS feed rồi đưa phần nội dung cho model đọc. Nó đã có sẵn một bộ extract nhỏ, đọc từng đoạn văn và dừng ở 6.000 ký tự. Ngày 5/11/2026 mình chạy crawl4ai (bản 0.9.5) song song với nó trên cùng mấy trang, để xem một crawler chuyên dụng có làm tốt hơn không.
Mình đã chạy gì
Năm bài báo lấy trong ngày từ chính các feed mà job đang đọc: ABC News, Guardian Australia, Sydney Morning Herald, The Conversation và 7NEWS. Thêm hai link Google News trong cùng danh sách feed. crawl4ai chạy hai kiểu, một kiểu qua headless browser, một kiểu gọi HTTP trơn không cần browser, cả hai đều bật filter lọc nhiễu "fit Markdown" của nó (PruningContentFilter, threshold 0.48).
git clone --depth 1 https://github.com/unclecode/crawl4ai.git repo
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python ./repo # 10 s, venv ends up 610 MB
.venv/bin/python -m playwright install chromium # 21 s, 203 MB browser
Để so sánh, mình lấy mọi câu dài hơn 60 ký tự trong output của bộ extract của mình, rồi đếm xem có mấy câu cũng nằm trong output của crawl4ai.
Kết quả ra sao
Số ký tự mỗi bài, của mình (đã bỏ giới hạn) so với crawl4ai bản thô và bản đã lọc, rồi tới độ phủ:
- ABC: 7.703, so với 28.324 bản thô (207 link) và 9.269 bản lọc (4 link). Độ phủ 96%.
- Guardian: 7.808, so với 27.629 (197 link) và 12.165 (18 link). Độ phủ 100%.
- SMH: 7.330, so với 15.004 (71 link) và 10.966 (38 link). Độ phủ 98%.
- The Conversation: 3.293, so với 12.288 (75 link) và 4.345 (13 link). Độ phủ 100%.
-
7NEWS: 6.848, so với 24.745 (138 link) và 7.968 (5 link). Độ phủ 100%.
-
Cả hai đều lấy được phần thân bài. Output đã lọc giữ được 96 tới 100% những gì bộ extract của mình giữ.
- crawl4ai giữ được đoạn mà bộ của mình bỏ mất. Bài của ABC liệt kê mười hai chuyến bay bị chuyển hướng dưới dạng gạch đầu dòng. Bộ của mình chỉ đọc đoạn văn nên mất cả mười hai dòng. crawl4ai giữ lại hết, kèm tiêu đề và tiêu đề phụ.
- Nhưng nó cũng giữ nhiều rác hơn: mười tiêu đề "related stories" của Guardian, footer và link đăng ký của SMH, chú thích ảnh, chữ "Share to Facebook".
- Không dùng browser thì crawl4ai chạy nhanh ngang bộ của mình (0,31 tới 0,74 giây một trang). Bật browser thì tốn thêm khoảng 2 giây.
- Vấn đề lớn hơn lại nằm ở giới hạn của chính mình. Bốn trên năm bài dài 6.800 tới 7.800 ký tự, nên mức cắt 6.000 ký tự làm mất 12 tới 23% cuối của mỗi bài.
Chỗ nào hỏng
- Chế độ browser bị treo ở 7NEWS. Nó đợi trang load xong suốt 60 giây rồi bỏ cuộc, hai lần liền. Gọi HTTP trơn thì lấy được trang đó trong chưa tới một giây.
- Link Google News vẫn không lấy được. Mình hy vọng browser thật sẽ đi theo link tới trang báo gốc. Đợi thêm 5 giây cho network yên rồi mà nó vẫn dừng ở một trang lỗi của Chrome, và chính bộ dò của crawl4ai đánh dấu đó là bị chặn anti-bot.
- Nó nặng. Khoảng 610 MB package Python, chưa tính 203 MB browser. Nếu app của bạn không viết bằng Python thì phải chạy thêm một process nữa bên cạnh.
- Nó quảng cáo ngay lúc import. Lần import nào cũng in banner giới thiệu bản hosted của họ, trừ khi bạn set
CRAWL4AI_NO_CLOUD_NOTICE=1.
Mình giữ lại gì
Không phải cái library. Filter của nó chấm điểm từng block theo tỉ lệ chữ nằm trong link, và mình mang ý tưởng đó vào bộ extract của mình: đọc cả list item chứ không chỉ đoạn văn, và bỏ item nào có hơn nửa chữ là link. Test trên một bản copy với đúng năm trang đó, nó lấy lại được mười hai dòng chuyến bay, bỏ được hai dòng mời đăng ký newsletter ở Guardian, còn ba bài kia giữ nguyên.
Bộ dò bị chặn của crawl4ai cũng đáng đọc: một danh sách ngắn các pattern nhận ra trang chặn của Cloudflare, Akamai, Imperva và mấy bên tương tự, những trang vẫn trả status 200. Một bộ extract đơn giản không phân biệt được trang bị chặn với trang trống.
Bạn đang dùng Python và cần đọc trang render bằng JavaScript, hoặc crawl cả một site, thì chọn crawl4ai. Còn với báo phục vụ HTML thường, một bộ extract nhỏ làm được y như vậy mà ít rác hơn.
English