Install
openclaw skills install @sedey999/sites-to-ima网站内容批量标记入库:全量抓取指定网站内容列表、导出xlsx/csv、建立分类索引笔记并导入知识库、生成增量更新手册。当用户说"标记网站XX""爬取XX网站入库""把XX建成分类索引""更新marksites站点XX"时触发。不适用于单篇文章阅读、非网站数据处理、纯知识库管理。
openclaw skills install @sedey999/sites-to-ima运行环境:本 skill 依赖 ima.copilot(腾讯智能工作台)平台能力(push_note / import_urls / ima_cos_util 等 OpenAPI 与沙箱命令),需在 ima.copilot 环境中使用,非独立运行工具。
网站内容批量标记入库:全量抓取指定网站的内容列表(遍历"加载更多"/分页),导出 xlsx/csv 表格,建立 ima 分类索引笔记体系并批量导入知识库,最后生成可复用的增量更新手册。
触发:用户说"标记网站 XX"、"爬取 XX 网站入库"、"把 XX 建成分类索引"、"抓取 XX 全量内容列表"、"更新 marksites 站点 XX"。
不适用:单篇文章的抓取与阅读、非网站类数据处理、纯知识库管理(用 ima-knowledge)。
| 笔记 | 命名 | 定位 |
|---|---|---|
| 独立分类索引笔记(每类 1 篇) | 【{站点}·分类索引】{分类名} | 目录指引:收录该分类全量文章索引 |
| 统一更新笔记(全站 1 篇) | 【{站点}·分类索引·更新】 | 正式导入记录:记录实际导入 KB 的 URL,多一个「类别」列,所有更新追加在此 |
| 更新手册(1 篇) | 【{站点}·更新手册】 | 下次增量更新的完整操作方法 |
为什么双轨:统一更新笔记收录的是"实际导入 KB 的文章"(初次约 180 篇,保持轻量可打开);独立分类笔记是全量目录(大站可能数千行,客户端打不开也保留为历史归档/目录指引,标题可加 (建议绕过) 前缀提示)。两者用途不同,不可合并。
每站点维护一个本地进度文件 output/{site}_progress.json,解决两个问题:①用户说"pr daily 更新"等关键词时,不依赖读取 ima 笔记即可锁定站点与进度;②会话记忆重置后,进度记录仍有本地文件兜底。
{
"site": "PR Daily",
"domain": "prdaily.com",
"keywords": ["pr daily", "prdaily"],
"last_fetch": "YYYY-MM-DD",
"notebook_folder_id": "笔记本ID",
"manual_note_id": "更新手册笔记ID",
"unified_note": {"note_id": "统一更新笔记ID", "last_seq": 0},
"categories": {
"{分类名}": {
"slug": "{WP分类slug或入口标识}",
"index_note": {"note_id": "...", "last_date": "YYYY-MM-DD", "total": 0, "last_seq": 0},
"kb_import": {"folder_id": "...", "last_date": "YYYY-MM-DD", "total": 0}
}
}
}
字段语义(双轨进度分开记录,与双轨笔记模型一一对应):
keywords:站点识别关键词(小写;含站名常用叫法与去空格变体),用户说"更新 XX"时做包含匹配index_note.last_date / total / last_seq:索引进度--该类独立索引笔记已收录的最新文章发布日期 / 累计篇数 / 最后序号(序号供追加续编)unified_note.note_id / last_seq:统一更新笔记定位与全局序号(全站一篇,跨类全局续编)kb_import.last_date / total:导入进度--知识库该类文件夹已导入的最新文章日期 / 累计篇数(全量目录与实际导入量不同,必须分开记)last_fetch:最近一次执行增量更新的日期笔记获取方式(note_id 优先,标题兜底):所有笔记(索引/统一更新/手册)一律按 progress.json 记录的 note_id 直接定位(export_note / push_note 直用);note_id 失效或文件中缺失时,按标题搜索兜底:【{站点}·分类索引】{分类名} /【{站点}·分类索引·更新】/【{站点}·更新手册】,找到后回填 progress.json。禁止每次更新都靠标题搜索定位笔记(慢且可能误中同名笔记)。
双源策略:本地 progress.json 为第一信源(每次执行后必同步);【{站点}·更新手册】笔记为兜底(本地文件丢失时按手册的 note_id/folder_id/最新抓取日期恢复重建 progress.json)。
add_notebook 创建,再继续流程;用户拒绝代建时请用户自行建好后再来。禁止在无目标笔记本的情况下把笔记散落写入默认位置。import_urls 调用前(包括可行性验证的 1-2 篇试导入),必须已向用户明确说明:①将对该站做全量内容提取 ②初次导入知识库的具体总篇数与各分类分配 ③(强防护站点)试导入可能产生需用户手动删除的残留条目。说明未发出或用户未确认,坚决不得执行任何导入行为,一条都不行。首次执行(探测完抓取通道、抓到分类清单后、任何导入动作执行前)发送给用户。此模板未发出且未获用户确认前,禁止调用 import_urls(含 1-2 篇试导入):
【ima_marksites 执行计划确认】{站点名}({域名})
■ 抓取通道:{WordPress API / RSS+sitemap / 浏览器遍历 / 平台导入+fetch读取}
■ 检测到 {n} 个分类:
{分类A}({数量}篇)|{分类B}({数量}篇)|…
■ 将对该站做**全量提取**,检测到内容共约 {总量} 篇
■ 将建立 {n+2} 篇笔记(笔记本:{目标笔记本名}):
1.【{站点}·分类索引】{分类A} 等 {n} 篇——全量目录指引(各类约{行数}行)
2.【{站点}·分类索引·更新】——正式导入记录(初始约{总导入数}行,带类别列)
3.【{站点}·更新手册】——增量更新方法
■ 知识库:{KB名} / {目标文件夹} 下新建 {n} 个分类子文件夹
**初次导入共 {总导入数} 篇**(分配:{分类A} {x}篇、{分类B} {y}篇…)
■ 强防护站点附加说明(仅适用时保留):验证通道需先试导入 1-2 篇,若平台抓取失败将产生需您手动删除的残留条目
■ 表格文件:output/{site}_full.json + 每分类 1 个 xlsx(序号/发布日期/标题/摘要/URL,序号与索引笔记一一对应)
■ 预计耗时:抓取约{X}分钟|KB 导入+复核约{Y}分钟|笔记写入约{Z}分钟|合计约{T}
■ 注意:独立目录笔记为全量收录,大站单篇可能数千行,客户端或有打不开的情况(仍保留为目录指引,助手可读)。
确认开始?(回复"确认",或直接说要调整的地方)
耗时估算基准:API 抓取每千篇约 2-5 分钟;浏览器抓取每页 3-8 秒;KB 导入每 10 篇约 3 秒调用 + 全部导完等 20 秒复核,180 篇约 5 分钟;笔记写入每千行约 5 分钟(分批追加)。
先判定可行性,再动手抓取。以下类型站点全通道不可行,应立即向用户摊牌,勿换库反复试错。
| 类型 | 判定特征 | 实测结论 |
|---|---|---|
| Cloudflare 强防护站(JS Challenge + 托管质询 + IP 信誉) | 任何请求返回 403 + 页面标题 "Just a moment..." | 本地 HTTP 库(requests/curl)、TLS 指纹模拟(curl_cffi/tls_client/cloudscraper,chrome/safari/edge 全试)、Playwright 浏览器(无头/有头+xvfb)、Patchright 反检测浏览器(无头/有头)、平台 import_urls 服务端抓取(解析永久卡死)、公共代理(jina/allorigins/codetabs)全部无效 |
| 沙箱网络不可达的兜底服务 | Google Cache、Wayback Machine(web.archive.org)、r.jina.ai 直连超时/空响应 | 无法作为降级通道 |
已知不可抓取站点黑名单:marketingweek.com(2026-08-15 全通道实测失败,含平台导入)。
curl 带 UA 测 WP API / RSS / sitemap(3 分钟内):全部 403 -> 下一步。curl_cffi impersonate=chrome 测首页(2 分钟):仍 403 且响应含 "Just a moment" -> 判定 CF 强防护,进入第 3 步;否则按正常降级流程走。import_urls 小批量导入 1-2 个文章 URL,等 5 分钟:
fetch(media_id) 可读 -> 平台能过 CF,全流程改走「平台导入 + fetch 读取」通道,随后照常发【首次执行确认模板】(含全量提取范围与具体导入数量),二次确认后才可批量导入。Phase 1 开工前必须先完成上方「第 0 步:站点可行性判定」:命中 Cloudflare 强防护(403 + "Just a moment")时按该步骤处理,不要进入本 Phase 的常规降级链。
{域名}/wp-json/wp/v2/posts?per_page=1,返回 JSON 即命中。用 /wp-json/wp/v2/categories 找分类(用户给的目录链接若含 ?cat= 或分类 slug,对应匹配)。API 只返回文章对象,天然避开导航/侧边栏/相关报道。/feed/、/sitemap.xml、/post-sitemap*.xml。feed 提供结构与字段样例,sitemap 提供全量 URL(缺日期/摘要时逐篇补抓或留空)。用户一次给多个目录链接 = 多个分类;单链接 = 单分类(此时独立分类笔记仍建,命名取链接锚文本或栏目名)。
/wp-json/wp/v2/posts?categories={id}&per_page=100&page=N 翻页至返回空。字段映射:link→URL、title.rendered→标题(去 HTML 标签与实体)、date→发布日期(取 YYYY-MM-DD)、excerpt.rendered→描述(无则留空)。output/{site}_full.json(结构:{分类名: {items: [{url,title,date,excerpt}]}}),此文件是后续一切操作的数据底座,务必落盘。output/{site}_{分类名}.xlsx,列=序号|发布日期|标题|摘要|URL(与索引笔记列序一致;摘要即站点现成的描述/摘要,缺失留空,不猜测)。格式细节(标题行/元信息块/表列/追加小节/更新记录沉底)必须先读 references/note-formats.md,按模板生成。要点:
|,直接走 API JSON body 会触发 WAF。写入路径:Markdown 写 .md 文件 → ima_cos_util -f 上传拿 cosKey → push_note(新建不传 note_id / 追加传 note_id + content_cos_key)。长笔记(>1000 行)分批追加,每批 ≤1000 行。export_note 下载原文核验行数(fetch 对长笔记会降级返回加工摘要,不可信)。**前置条件:硬性规则第 5 条(导入前置说明门)与首次执行确认门均已通过。**未向用户说明具体导入数量并获确认,不得执行本 Phase。
per = round(180/n) 取整到十(不足 10 按 10),余数按 +10 依序补给前面的分类。示例:6 类=30×6;5 类=40,40,40,30,30;4 类=50,50,40,40;3 类=60×3。每类取最新发布的 X 篇。create_folder 建子文件夹(命名=分类名),然后 import_urls 导入(≤10 URL/批,批间 1.5s)。get_knowledge_list 复核各类实际条目数;不足则从该类更早文章继续补,直到达标或候选耗尽(如实告知用户)。按 references/manual-template.md 的七章模板生成【{站点}·更新手册】,写入目标笔记本。手册必须记录:本次实际使用的抓取通道与参数(slug/cat_id 或选择器)、全部笔记 note_id、全部 KB 文件夹 folder_id、最新抓取日期。这些 ID 是下次增量更新的钥匙,缺失会导致下次无法续接。
同时写本地进度底座 output/{site}_progress.json(结构见「核心概念:本地进度底座」):keywords 取站点常用叫法(含去空格变体),每类填入 index_note(note_id/last_date/total/last_seq=初次收录数)与 kb_import(folder_id/last_date/total=初次导入数),unified_note 填统一更新笔记 note_id 与初始全局序号,顶层 last_fetch 填本次日期。
从用户消息提取站点名(如"pr daily 更新"中的"pr daily"),按顺序匹配:
output/*_progress.json,将提取词与各文件 keywords 做不区分大小写的包含匹配 -> 命中即锁定站点,直接读取其进度(各类 last_date),跳到第 3 步。last_date 之后的新文章(API 用 after={日期}T23:59:59)。output/{site}_full.json 数据底座。after 的最新 15 篇日期分布验证抓取无漏,再向用户如实汇报。【{站点名} 更新完成】本次新增:索引 {X} 篇|知识库导入 {Y} 篇
| 分类 | 索引收录至 | 索引累计 | 知识库导入至 | 导入累计 |
|---|---|---|---|---|
| {分类A} | {最新文章日期} | {N} 篇 | {最新文章日期} | {M} 篇 |
| ... | | | | |
进度回写后立即执行三项校验,任何一项失败都必须如实告知用户并说明缺了什么:
last_fetch = 本次执行日期 ②有新增的分类其 index_note.last_date / kb_import.last_date 已前移、total 已增加 ③unified_note.last_seq 等于旧值+本次新增数。任一不符 -> 重写 progress.json 再验一次,仍不符则报告。export_note 下载统一更新笔记原文,确认尾部存在本次新增行(末行序号 = unified_note.last_seq);独立索引笔记抽验至少 1 类(挑本次新增最多的类),确认末行序号 = 该类 last_seq。fetch 返回的加工摘要不可作为校验依据。get_knowledge_list 复核有导入的分类,实际条目数 >= 该类 kb_import.total(import_urls 计数回弹时以实际为准回写 total)。校验全部通过后才允许输出第 4 步的进度汇报表;汇报表末尾附一行:[OK] 进度校验通过(本地/笔记/知识库 三项)。
pip install requests openpyxl # 必装
pip install playwright # 浏览器兜底通道需要
# 浏览器下载必须走国内镜像,直连必超时:
PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ playwright install chromium
apt-get install -y --no-install-recommends xvfb # 有头模式(过 CF 必需)配合 xvfb-run
pip install patchright # playwright 反检测 fork,CF 站点备选(安装后同样走镜像装浏览器)
沙箱要点(实测):
playwright install 直连下载必超时,必须设 PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/(镜像约 30 秒装完)。chromium-headless-shell(旧版无头,指纹特征明显,CF 必拦);要装完整版 chromium(new headless)。apt 装 chromium 完整包会 OOM(exit 137),不要尝试。kill -9 <pid>; rm -f /var/lib/dpkg/lock*; dpkg --configure -a 后重试。xvfb-run -a --server-args="-screen 0 1280x800x24" <python脚本>。| 坑 | 对策 |
|---|---|
| Cloudflare 拦截返回空体 | 请求带浏览器 UA;仍失败用 curl 抓到文件再解析 |
| CF 强防护站(403 + "Just a moment") | 按「站点可行性限制」章节处理:验证平台通道,不行即摊牌,勿换库试错 |
| playwright install 下载超时 | 必设 PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright/ |
| import 卡在解析中成僵尸条目 | 新站点先小批量 1-2 篇验证解析完成再批量;残留条目只能用户客户端手删 |
| append/update 直接传表格触发 WAF(501) | 一律走 COS+push_note(content_cos_key) 路径 |
| import_urls 显示成功但计数回弹 | 等 20s 后 get_knowledge_list 复核,不足再补 |
| fetch 长笔记返回加工摘要 | 用 export_note 拿 content_url 下载原文核验 |
| fetch(type=url) 对多数 URL 返回不存在 | 它只能读平台历史缓存资源,不能当抓取通道用 |
| 表格内容含 | 换行导致错列 | 单元格内 |→/、去换行 |
| 巨笔记客户端打不开 | 属预期,保留为目录指引,标题加 (建议绕过) 前缀 |