fetch-page
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTrang web → nội dung chính
网页 → 主要内容
Chạy thế nào
运行方式
URL ──► Chrome headless ──CDP──► tab đã chạy JS xong ──► defuddle chạy trong tab ──► md | html | jsonScript mở Chrome với cổng debug, nối websocket vào một tab và ra lệnh CDP: đổi User-Agent, đặt khung
màn hình desktop, , chờ trang ổn định. Sau đó nó inject bundle defuddle vào trang và
gọi trên DOM thật. Defuddle luôn lọc trang trước; chỉ quyết định kết quả
lọc được in ra dưới dạng nào.
Page.navigateparseAsync()--formatDefuddle chạy trong Chrome chứ không qua JSDOM: CSS thật, không phải parse lại HTML 1–2 MB, nên
phần defuddle chỉ mất dưới 1 giây.
URL ──► Chrome headless ──CDP──► 已执行完JS的标签页 ──► 在标签页内运行defuddle ──► md | html | json脚本以调试端口启动 Chrome,通过 websocket 连接到一个标签页并下达 CDP 指令:修改 User-Agent,设置桌面屏幕尺寸,,等待页面稳定。然后它将 defuddle bundle 注入页面并对真实 DOM 调用 。Defuddle 始终先过滤页面; 只决定过滤结果以哪种形式输出。
Page.navigateparseAsync()--formatDefuddle 在 Chrome 内部运行,而不是通过 JSDOM:使用真实 CSS,不需要重新解析 1–2 MB 的 HTML,因此 defuddle 部分只需不到 1 秒。
Dùng
使用
Thư mục skill: . Lần đầu (hoặc khi chưa có trong thư mục skill):
${CLAUDE_SKILL_DIR}node_modules/bash
npm install --prefix ${CLAUDE_SKILL_DIR}Mỗi lần lấy trang:
bash
node ${CLAUDE_SKILL_DIR}/fetch-page.mjs <url> -o /tmp/page.md| Flag | Tác dụng |
|---|---|
| Dạng đầu ra, mặc định |
| Ghi kết quả ra file. Không có thì in ra stdout |
| Lưu thêm HTML thô của trang (sau khi JS chạy, trước khi defuddle lọc) để soi khi kết quả lạ |
| Ra gì | Khi nào dùng |
|---|---|---|
| Frontmatter YAML + markdown, giống | Đọc, tóm tắt, lưu bài |
| HTML đã làm sạch, không kèm metadata | Cần giữ cấu trúc HTML (bảng phức tạp, render lại) |
| Cả object defuddle trả về: | Cần metadata để xử lý tiếp bằng code |
Cần Node ≥ 22 (dùng có sẵn) và Chrome. Chrome không nằm ở chỗ quen thuộc thì đặt
.
WebSocketCHROME_PATHstderr in , (sau redirect), và thời gian. Một bài báo thường mất 2–6 giây.
titleurlwordCount技能目录:。首次(或技能目录中没有 时):
${CLAUDE_SKILL_DIR}node_modules/bash
npm install --prefix ${CLAUDE_SKILL_DIR}每次抓取页面:
bash
node ${CLAUDE_SKILL_DIR}/fetch-page.mjs <url> -o /tmp/page.md| Flag | 作用 |
|---|---|
| 输出格式,默认 |
| 将结果写入文件。不指定则输出到 stdout |
| 额外保存页面原始 HTML(JS 执行后、defuddle 过滤前),用于结果异常时排查 |
| 输出内容 | 使用场景 |
|---|---|---|
| YAML frontmatter + markdown,类似 | 阅读、总结、保存文章 |
| 已清理的 HTML,不含 metadata | 需要保留 HTML 结构(复杂表格、重新渲染) |
| defuddle 返回的完整对象: | 需要 metadata 做后续代码处理 |
需要 Node ≥ 22(使用内置的 )和 Chrome。如果 Chrome 不在常规位置,请设置 。
WebSocketCHROME_PATHstderr 输出 、(重定向后)、 和时间。一篇文章通常需要 2–6 秒。
titleurlwordCountĐọc kết quả trước khi dùng
使用前先检查结果
wordCount--raw-html<p>Lỗi thì script thoát với mã 1 và in lý do ra stderr: domain sai (), vẫn
kẹt ở trang chặn bot, hoặc trang bắt giải captcha. Gặp captcha thì đừng chạy lại, kết quả sẽ y hệt.
net::ERR_NAME_NOT_RESOLVED先看 。如果文章只有几十个词,或 markdown 中途停止,说明 defuddle 丢失了部分内容。可用 重新运行,然后对比 HTML 正文中的 片段与 markdown。
wordCount--raw-html<p>出错时脚本以退出码 1 结束并在 stderr 打印原因:域名错误()、仍卡在反爬页面、或页面要求通过验证码。遇到验证码不要重试,结果会一模一样。
net::ERR_NAME_NOT_RESOLVEDVì sao script đặt những thứ này
为什么脚本要设置这些
Mỗi dòng dưới đây là một lỗi đã gặp thật. Bỏ cái nào thì lỗi đó quay lại.
| Trong script | Nếu không có |
|---|---|
| Cloudflare trả trang "Just a moment..." thay cho bài (machinelearningmastery.com) |
| Khung headless mặc định ~756px, AP News coi là mobile, gập nửa sau bài bằng |
Không chờ | Trang báo nhiều quảng cáo không bao giờ bắn |
| Plugin WordPress này vẽ code thành bảng số dòng + code, defuddle ra bảng markdown với code dồn một dòng |
| Script thoát mã 0 và in trang "Let's confirm you are human" ra như một bài 29 từ (arstechnica.com) |
| Trang có CSP chặt chặn việc inject bundle defuddle |
| Chờ Chrome thoát hẳn rồi mới xoá profile tạm | |
下面每一行都是实际遇到过的错误。去掉哪一行,对应错误就会复发。
| 脚本中的设置 | 如果缺少 |
|---|---|
| Cloudflare 会返回 "Just a moment..." 页面而不是文章(machinelearningmastery.com) |
| 默认 headless 视口约 756px,AP News 视其为移动端,用 |
不等待 | 广告很多的新闻页面永远不会触发 |
| 这个 WordPress 插件把代码渲染成“行号表格 + 代码”,defuddle 会输出代码被挤成一行的 markdown 表格 |
| 脚本以退出码 0 结束,并将 "Let's confirm you are human" 页面当作一篇 29 词的文章输出(arstechnica.com) |
| 严格 CSP 会阻止注入 defuddle bundle |
| 等待 Chrome 完全退出后才删除临时 profile | |
Khi gặp site mới lỗi
遇到新站点出错时
- Khối code ra thành bảng ⇒ site dùng một plugin highlight khác. Tìm trong HTML thô () chỗ chứa code thô (thường là
--raw-htmlẩn hoặc thuộc tínhtextarea), thêm selector vàodata-*.FIX_CODE_HIGHLIGHTERS - Mất một đoạn giữa bài ⇒ tìm trong HTML xem đoạn đó có bị ẩn (, class
display: none) không. Thường là JS của site gập bài lại; tìm điều kiện khiến nó gập (khung màn hình, cookie) thay vì tắthidden— tắt đi thì menu mobile, popup ẩn cũng lọt vào markdown.removeHiddenElements
- 代码块变成表格 ⇒ 站点使用了其他高亮插件。在原始 HTML()中找到包含原始代码的位置(通常是隐藏的
--raw-html或textarea属性),将选择器添加到data-*。FIX_CODE_HIGHLIGHTERS - 文章中间缺失一段 ⇒ 在 HTML 中检查该段落是否被隐藏(、
display: none类)。通常是站点 JS 折叠了文章;找出触发折叠的条件(视口尺寸、cookie),而不是关闭hidden——关闭后移动端菜单、隐藏弹窗也会混入 markdown。removeHiddenElements
Giới hạn
限制
- Chỉ qua được kiểu chặn bot nhìn User-Agent. Captcha, kiểm tra dấu vân tay trình duyệt, paywall thì
không qua. Captcha chỉ nhận ra được loại của AWS WAF; loại khác (reCAPTCHA, hCaptcha toàn trang…) vẫn
lọt qua thành kết quả, nên thấp bất thường thì mở markdown ra xem.
wordCount - Ngôn ngữ trong fence code là defuddle đoán; HTML không ghi thì có thể đoán sai (Python ra ).
```js - được import theo đường dẫn file trong
buildFrontmattervì package không export nó. Version defuddle ghim ởnode_modules/defuddle/dist/trong0.19.4; nâng version thì kiểm tra lại đường dẫn này.package.json
- 只能绕过基于 User-Agent 的反爬。验证码、浏览器指纹检测、付费墙无法绕过。验证码只能识别 AWS WAF 类型;其他类型(reCAPTCHA、整页 hCaptcha 等)仍会混入结果,所以 异常低时请打开 markdown 查看。
wordCount - 代码块围栏中的语言是 defuddle 猜测的;HTML 未记录时可能猜错(Python 被标成 )。
```js - 是通过
buildFrontmatter中的文件路径导入的,因为包未导出它。defuddle 版本固定在node_modules/defuddle/dist/的package.json;升级版本时请重新检查此路径。0.19.4