{"id":"2db9ece1-b18f-429e-925e-78f23bcc79d3","entityType":"agent","slug":"clawhub-ajayhao-article-fetcher","name":"Article Fetcher（文章抓取+Notion/Obsidian知识库存档）","canonicalUrl":"https://www.xpersona.co/agent/clawhub-ajayhao-article-fetcher","canonicalPath":"/agent/clawhub-ajayhao-article-fetcher","generatedAt":"2026-10-10T10:43:38.567Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:35:15.362Z","emptyReason":null},"description":"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.6K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17145mdmb9rthykrxn51j1yrx849jek:article-fetcher","sourceUrl":"https://clawhub.ai/ajayhao/article-fetcher","homepage":"https://clawhub.ai/ajayhao/skills/article-fetcher","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/ajayhao/article-fetcher","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/ajayhao/skills/article-fetcher","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":64,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Article Fetcher（文章抓取+Notion/Obsidian知识库存档） technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:35:15.362Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:35:15.362Z","emptyReason":null},"stars":null,"forks":null,"downloads":1615,"packageName":null,"latestVersion":"1.3.6","tractionLabel":"1.6K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:35:15.327Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T06:35:15.362Z","lastCrawledAt":"2026-10-10T06:35:15.327Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T06:35:15.327Z","lastVerifiedAt":null,"highlights":[{"version":"1.3.6","createdAt":"2026-08-08T13:16:14.670Z","changelog":"## v1.3.6 — 安全修复（基于 ClawHub SkillSpector 审计） - P0(High): lxml 6.0.4→6.1.0 修复 CVE-2026-41066 XXE - P1(Medium): 校正 README/SKILL 误导性「零网络外发/完全本地」声明 - P2(Medium): 补强 LLM 文本外发声明（可选/来自配置/截断 12000 字符） - P3: 删除 references/tirith-blocking.md 绕过拦截笔记","fileCount":27,"zipByteSize":52127},{"version":"1.3.5","createdAt":"2026-08-08T12:29:02.791Z","changelog":"- 支持离线输入：可直接解析 HTML/MHTML 文件并归档，自动处理微信懒加载图片、字符集乱序等问题。 - 新增 fetchers/offline_parser.py，实现 HTML/MHTML 解析与陷阱修复。 - Obsidian 默认存档目录由「1-收件箱」改为「收件箱」，本地命名已同步适配。 - 删除 skill-card.md，补充 tirith-blocking.md 作为参考资料。 - 增加对 stdin HTML 内容的处理选项，归档流程与 URL 模式完全一致。","fileCount":30,"zipByteSize":53732},{"version":"1.3.4","createdAt":"2026-07-19T01:44:49.723Z","changelog":"- Updated skill version to 1.3.4. - No code or documentation content changes detected; only the version number was incremented in metadata.","fileCount":26,"zipByteSize":45423},{"version":"1.3.3","createdAt":"2026-07-19T01:22:17.231Z","changelog":"## v1.3.3 (2026-07-06) ### 🔒 Tirith 安全扫描兼容 - **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄 - **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选 - **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报","fileCount":26,"zipByteSize":45452},{"version":"1.3.2","createdAt":"2026-07-19T00:58:08.499Z","changelog":"## v1.3.2 (2026-07-06) ### 🐛 修复 - **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序 - **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer --- ## v1.3.1 (2026-07-06) ### 🔒 安全审计修复 - **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE) - **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]` - **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为 - **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示 --- ## v1.3.0 (2026-07-06) ### 🆕 微信 Playwright 回退 + 空内容检测 - **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退 - **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成 - **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN ### 🐛 修复 - **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致","fileCount":21,"zipByteSize":44363},{"version":"1.3.1","createdAt":"2026-07-19T00:36:42.614Z","changelog":"## v1.3.1 (2026-07-06) ### 🔒 安全审计修复 - **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE) - **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]` - **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为 - **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示 --- ## v1.3.0 (2026-07-06) ### 🆕 微信 Playwright 回退 + 空内容检测 - **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退 - **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成 - **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN ### 🐛 修复 - **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致","fileCount":26,"zipByteSize":44818},{"version":"1.3.0","createdAt":"2026-07-19T00:15:51.577Z","changelog":"## v1.3.0 (2026-07-06) ### 🆕 微信 Playwright 回退 + 空内容检测 - **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退 - **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成 - **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN ### 🐛 修复 - **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致","fileCount":26,"zipByteSize":44361},{"version":"1.2.0","createdAt":"2026-07-05T17:36:43.124Z","changelog":"- 增加 Playwright 浏览器支持，实现知乎 403 时自动回退浏览器抓取（Playwright 未安装则跳过，无需强依赖）。 - 快速配置引导增加 Playwright 安装说明。 - SKILL 元信息修正 homepage 链接地址。 - 关键说明中新增知乎浏览器回退与 Playwright 安装提示。","fileCount":21,"zipByteSize":42324}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17145mdmb9rthykrxn51j1yrx849jek:article-fetcher","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17145mdmb9rthykrxn51j1yrx849jek:article-fetcher` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/ajayhao/article-fetcher before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T10:43:38.563Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-ajayhao-article-fetcher/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T06:35:15.362Z","emptyReason":null},"readme":"Skill: Article Fetcher（文章抓取+Notion/Obsidian知识库存档）\n\nOwner: ajayhao\n\nSummary: 抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\n\nTags: latest:1.3.6\n\nVersion history:\n\nv1.3.6 | 2026-08-08T13:16:14.670Z | user\n\n## v1.3.6 — 安全修复（基于 ClawHub SkillSpector 审计）\n- P0(High): lxml 6.0.4→6.1.0 修复 CVE-2026-41066 XXE\n- P1(Medium): 校正 README/SKILL 误导性「零网络外发/完全本地」声明\n- P2(Medium): 补强 LLM 文本外发声明（可选/来自配置/截断 12000 字符）\n- P3: 删除 references/tirith-blocking.md 绕过拦截笔记\n\nv1.3.5 | 2026-08-08T12:29:02.791Z | user\n\n- 支持离线输入：可直接解析 HTML/MHTML 文件并归档，自动处理微信懒加载图片、字符集乱序等问题。\n- 新增 fetchers/offline_parser.py，实现 HTML/MHTML 解析与陷阱修复。\n- Obsidian 默认存档目录由「1-收件箱」改为「收件箱」，本地命名已同步适配。\n- 删除 skill-card.md，补充 tirith-blocking.md 作为参考资料。\n- 增加对 stdin HTML 内容的处理选项，归档流程与 URL 模式完全一致。\n\nv1.3.4 | 2026-07-19T01:44:49.723Z | user\n\n- Updated skill version to 1.3.4.\n- No code or documentation content changes detected; only the version number was incremented in metadata.\n\nv1.3.3 | 2026-07-19T01:22:17.231Z | user\n\n## v1.3.3 (2026-07-06)\n\n### 🔒 Tirith 安全扫描兼容\n\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\n\nv1.3.2 | 2026-07-19T00:58:08.499Z | user\n\n## v1.3.2 (2026-07-06)\n\n### 🐛 修复\n\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\n\n---\n\n## v1.3.1 (2026-07-06)\n\n### 🔒 安全审计修复\n\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\n\n---\n\n## v1.3.0 (2026-07-06)\n\n### 🆕 微信 Playwright 回退 + 空内容检测\n\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\n\n### 🐛 修复\n\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\n\nv1.3.1 | 2026-07-19T00:36:42.614Z | user\n\n## v1.3.1 (2026-07-06)\n\n### 🔒 安全审计修复\n\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\n\n---\n\n## v1.3.0 (2026-07-06)\n\n### 🆕 微信 Playwright 回退 + 空内容检测\n\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\n\n### 🐛 修复\n\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\n\nv1.3.0 | 2026-07-19T00:15:51.577Z | user\n\n## v1.3.0 (2026-07-06)\n\n### 🆕 微信 Playwright 回退 + 空内容检测\n\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\n\n### 🐛 修复\n\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\n\nv1.2.0 | 2026-07-05T17:36:43.124Z | user\n\n- 增加 Playwright 浏览器支持，实现知乎 403 时自动回退浏览器抓取（Playwright 未安装则跳过，无需强依赖）。\n- 快速配置引导增加 Playwright 安装说明。\n- SKILL 元信息修正 homepage 链接地址。\n- 关键说明中新增知乎浏览器回退与 Playwright 安装提示。\n\nv1.1.1 | 2026-07-05T14:03:49.120Z | user\n\nNo code or documentation changes detected in this version.\n\n- No updates or modifications since the previous release.\n- Skill details and functionality remain unchanged for version 1.1.0.\n- Note \"Obsidian\" support in Display name of the skill\n\nv1.1.0 | 2026-07-05T08:42:46.210Z | user\n\nArticle Fetcher v1.1.0 introduces local Obsidian vault archive support and enhances flexible output modes:\n\n- Added Obsidian 本地知识库存档能力，支持 YAML frontmatter 和 markdown 格式。\n- 存档模式升级，支持 Obsidian/Notion/双写/仅终端输出四种场景。\n- 代码结构划分更细，模块化引入 archiver、detector、fetchers、processors、utils 等目录。\n- 环境变量和配置方式兼容 Hermes/OpenClaw agent，文档更清晰。\n- 补充 markdownify/pyyaml 等依赖，并完善安全、隐私、扩展性说明。\n- 原 OSS 图片上传、LLM 关键词提取等已稳定，整体体验更贴近个人知识库场景。\n\nv1.0.2 | 2026-05-10T08:08:07.730Z | user\n\n## v1.0.2 (2026-05-10)\n\n### 🏗️ LLM 多平台抽象\n\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置，移除 `DASHSCOPE_API_KEY` 等平台专属 Key，统一使用 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 三个环境变量\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\n\nv1.0.1 | 2026-05-07T13:43:59.595Z | user\n\n## v1.0.1 (2026-05-07)\n\n### 🔒 安全修复（ClawScan 扫描）\n\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\n\n### 📝 文档\n\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\n\nArchive index:\n\nArchive v1.3.6: 27 files, 52127 bytes\n\nFiles: archiver/__init__.py (0b), archiver/notion_archiver.py (15546b), archiver/obsidian_archiver.py (4805b), CHANGELOG.md (11735b), config.py (3159b), detector/__init__.py (0b), detector/platform_detector.py (1161b), fetchers/__init__.py (0b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/offline_parser.py (6606b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10912b), main.py (11575b), processors/__init__.py (0b), processors/image_processor.py (3943b), README.md (8857b), requirements.txt (184b), skill-card.md (2744b), SKILL.md (9336b), utils/__init__.py (0b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (10067b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.6:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.6\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"NOTION_API_KEY\", \"LLM_API_KEY\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"allowedEnv\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"OBSIDIAN_VAULT_PATH\", \"NOTION_API_KEY\", \"NOTION_ARTICLE_DATABASE_ID\", \"LLM_API_KEY\", \"LLM_BASE_URL\", \"LLM_MODEL\", \"WECHAT_COOKIES_FILE\", \"ZHIHU_COOKIES_FILE\"], \"securityNote\": \"OSS 凭证用于图片上传存储（图片外发至阿里云 OSS 为必需）；Notion 和 LLM 为可选集成，不配置则跳过对应功能；配置 LLM 时文章文本（前 12000 字符）会发送至你配置的 LLM API 端点\", \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.6\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 离线输入（HTML / MHTML）\r\n\r\n除 URL 外，支持直接喂入 HTML 文本或 `.mhtml` 文件（源自 wechat-article-capture 技能的三级策略），由 article-fetcher 负责图片上传 OSS、替换 URL 与归档。解析层已内置三处陷阱修复：微信懒加载 `data-src`→`src` 并删除 `data-src`、MHTML 编码乱码（charset 探测）、`data:image/svg+xml` 占位符过滤。\r\n\r\n```bash\r\n# 三级：MHTML 文件\r\npython3 main.py --mhtml page.mhtml --url https://mp.weixin.qq.com/s/xxx 标签1\r\n\r\n# 二级：粘贴 HTML（stdin）\r\ncat page.html | python3 main.py --html - --url https://mp.weixin.qq.com/s/xxx\r\n\r\n# 一级 / 原路径（不变）\r\npython3 main.py \"https://mp.weixin.qq.com/s/xxx\" 标签1\r\n```\r\n\r\n- `--platform` 默认 `wechat`（直传 HTML/MHTML 无 URL，无法自动探测平台）\r\n- `--url` 可选：作为图片下载 Referer 与归档 link；不传时回退平台默认 Referer（微信 `mp.weixin.qq.com`）\r\n- 解析产出标准 `article_data`，后半段管线（OSS 上传 → 替换 → 打标 → 字数 → 归档）与 URL 模式完全复用\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/收件箱/`，命名规则 `{YYYY-MM-DD}_{title}.md`。\r\n\r\n> 💡 **本地适配**：Vault 去序号后，存档目录从 `1-收件箱/` 改为 `收件箱/`（`obsidian_archiver.py` 已同步修正）。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：执行 `playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 本地落盘 + 图片 OSS 上传**：`.md` 笔记写入本地磁盘，但文章图片会上传至阿里云 OSS 图床（必需）后再嵌入，并非纯本地；LLM 关键词提取为可选功能，启用时文章文本（前 12000 字符）会发送至配置的 API 端点\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容（前 12000 字符）会发送至配置的 API 端点（仅用于关键词提取）。不配置则不发送\r\n- **图片下载**：自动下载原文图片并上传 OSS，过程中会请求第三方图片服务器，请确保有权抓取目标文章\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 本地落盘**：`.md` 文件写入本地磁盘；但文章图片需上传阿里云 OSS（必需），因此并非「零网络外发」\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.6:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.6 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 离线输入（HTML / MHTML）\r\n\r\n除 URL 抓取外，也支持直接传入 HTML 文本或 `.mhtml` 文件（微信文章三级采集策略），由本工具完成图片 OSS 上传、URL 替换与 Obsidian/Notion 归档。\r\n\r\n```bash\r\n# 三级：MHTML 文件\r\npython3 main.py --mhtml page.mhtml --url https://mp.weixin.qq.com/s/xxx 标签1\r\n\r\n# 二级：粘贴 HTML（stdin）\r\ncat page.html | python3 main.py --html - --url https://mp.weixin.qq.com/s/xxx\r\n\r\n# 一级 / 原路径（不变）\r\npython3 main.py \"https://mp.weixin.qq.com/s/xxx\" 标签1\r\n```\r\n\r\n- `--platform` 默认 `wechat`（直传 HTML/MHTML 无 URL，无法自动探测平台）\r\n- `--url` 可选：图片下载 Referer 与归档 link；不传时回退平台默认 Referer（微信 `mp.weixin.qq.com`）\r\n- 已修复微信懒加载（`data-src`→`src` 并删除）、MHTML 编码乱码、`data:image/svg+xml` 占位符过滤\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 本地落盘 + 图片云存储**: `.md` 笔记写入本地磁盘（Obsidian 部分纯本地），但文章图片会先上传至阿里云 OSS 图床（必需）后再嵌入笔记，**并非「零网络外发」**\r\n- **LLM 关键词提取（可选）**: 配置 `LLM_API_KEY` 时，文章文本（前 12000 字符）会发送至你配置的 LLM API 端点；不配置则仅使用本地词频方案，无任何文本外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.6:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.6\",\n  \"publishedAt\": 1786194974670\n}\n\nFile v1.3.6:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.6 (2026-08-08)\r\n\r\n### 🔒 安全修复（基于 ClawHub SkillSpector 审计）\r\n\r\n- **P0 — lxml XXE 漏洞（High）**：`requirements.txt` 中 `lxml==6.0.4` → `lxml==6.1.0`，修复 CVE-2026-41066（`iterparse` / `ETCompatXMLParser` 默认配置 XXE）。经核查代码未使用 lxml 解析不可信 XML（MHTML 走 `email` 库、HTML 走 `BeautifulSoup('html.parser')`），升级即消除风险面。\r\n- **P1 — 校正误导性「零网络外发」声明（Medium, Intent-Code Divergence）**：删除 README / SKILL 中「Obsidian 数据完全本地 / 零网络外发 / 不经过网络」等绝对化表述，统一为「Obsidian `.md` 本地落盘 + 文章图片上传阿里云 OSS（必需）」的准确描述。\r\n- **P2 — LLM 外发声明补强（Medium, External Transmission）**：在 `utils/tag_extractor.py` 的 LLM 调用处补充安全注释（可选触发 / endpoint 来自 `LLM_BASE_URL` 配置 / 仅外发前 12000 字符）；README 安全说明新增 LLM 文本外发条目，SKILL `securityNote` 同步声明。\r\n- **P3 — 删除「绕过安全拦截」笔记**：移除 `references/tirith-blocking.md`（审计报告指出的 bypass-prior-blocking 笔记），并确认无其它文件引用。\r\n\r\n## v1.3.5 (2026-08-08)\r\n\r\n### ✨ 离线输入（HTML / MHTML）\r\n\r\n- **新增 `fetchers/offline_parser.py`**：将 HTML 文本 / `.mhtml` 文件解析为标准 `article_data`，复用既有后半段管线（OSS 上传 → 替换 URL → 打标 → 字数 → Obsidian/Notion 归档）\r\n- **`main.py` 抽出 `process_and_archive(article_data, platform, url, tags)`**：URL 抓取与离线输入共用同一归档流程，行为零变化\r\n- **新增离线入口**：`archive_from_html()` / `archive_from_mhtml()`\r\n- **CLI 扩展（`sys.argv` 手动解析，不使用 argparse，遵循安全合规约定）**：`--html`（支持 `-` 读 stdin）/`--mhtml`/`--platform`（默认 wechat）/`--url`（可选）\r\n- **三陷阱修复（源自 wechat-article-capture 技能沉淀）**：\r\n  - 微信懒加载：`data-src`→`src` 并 `del data-src`，根治 markdownify 读占位符\r\n  - MHTML 编码：`get_payload(decode=True)` 取 bytes 后按 `get_content_charset()` 或 `utf-8` 解码，避免乱码\r\n  - `data:image/svg+xml` 占位符过滤，图片列表归一化（去 query、去重）确保 URL 替换可命中\r\n\r\n### 🔧 本地适配\r\n\r\n- **Vault 去序号**：存档目录从 `1-收件箱/` 改为 `收件箱/`（知衍库 2026-07 架构调整）\r\n- **移除 `distilled: false`**：蒸馏状态判定已改为文件存在性模型，不再依赖 frontmatter 标记\r\n- **裁撤 `wechat-article-capture` 技能**：v1.3.5 的 MHTML/HTML 离线模式已覆盖其全部功能，无需单独维护\r\n\r\n### 🐛 已知问题\r\n\r\n- ~~**Notion 转换**：`### | title` 格式标题会被 Notion API 误判为表格分隔符~~ **已修复 (v1.3.5-local)**：根因为 `_parse_inline_html` 将内联 `<img>` 当作 image block 塞入 `rich_text`，导致 `text` 字段缺失。修复为内联图片转文本链接 `[alt](url)`。\r\n\r\n---\r\n\r\n## v1.3.4 (2026-07-19)\r\n\r\n### 🔒 Tirith `requires.env` 补全\r\n\r\n- **`NOTION_API_KEY` / `LLM_API_KEY` 加入 `requires.env`**：代码实际读取的可选凭证必须在元数据声明，消除 CRITICAL exfiltration 误报\r\n\r\n---\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.6:skill-card.md\n\n## Description:\n\nFetches articles from WeChat, Xiaohongshu, Douban, and Zhihu, uploads article images to Aliyun OSS, extracts keywords with optional LLM support, and archives the result to Obsidian Markdown with optional Notion sync.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ajayhao](https://clawhub.ai/user/ajayhao)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and knowledge workers use this skill to turn supported social and article URLs, HTML, or MHTML captures into archived knowledge-base entries. It is suited for workflows that need Markdown notes, optional Notion records, OSS-hosted images, and generated tags.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Untrusted HTML or MHTML can contain image URLs that the runtime fetches and uploads to OSS before image URL validation is added.\n\nMitigation: Process only trusted article captures and URLs, and avoid untrusted HTML/MHTML inputs until image URL validation is available.\n\nRisk: Article images are uploaded to a configured Aliyun OSS bucket, so image content leaves the local environment.\n\nMitigation: Use a dedicated least-privilege OSS bucket and credentials limited to the needed object operations.\n\nRisk: When LLM keyword extraction is configured, article text is sent to the configured LLM endpoint and the endpoint is not forced to HTTPS by the skill.\n\nMitigation: Configure LLM_BASE_URL with HTTPS only, or leave LLM settings unset to use local keyword extraction.\n\nRisk: The release has a suspicious security verdict and should be reviewed before deployment.\n\nMitigation: Prefer the pinned requirements.txt installation path and review the configured cloud accounts and input sources before execution.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/ajayhao/skills/article-fetcher)\n- [Project Homepage](https://github.com/AjayHao/article-fetcher)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration]\n\n**Output Format:** [Markdown files with YAML frontmatter, optional Notion pages, terminal status text, and Python dictionary results when used as a module]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires environment configuration for Aliyun OSS; Obsidian, Notion, cookies, and LLM keyword extraction are optional configuration-dependent outputs.]\n\n## Skill Version(s):\n\n1.3.6 (source: server release evidence, metadata, and changelog)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.3.6:requirements.txt\n\nrequests==2.33.0\r\nbeautifulsoup4==4.14.3\r\noss2==2.19.1\r\nnotion-client==3.0.0\r\npython-dotenv==1.2.2\r\nlxml==6.1.0\r\nmarkdownify==1.1.0\r\nplaywright==1.54.0\r\npyyaml==6.0.2\r\nurllib3==2.7.0\n\nArchive v1.3.5: 30 files, 53732 bytes\n\nFiles: archiver/__init__.py (0b), archiver/notion_archiver.py (15546b), archiver/obsidian_archiver.py (4805b), CHANGELOG.md (10498b), config.py (3159b), detector/__init__.py (0b), detector/platform_detector.py (1161b), fetchers/__init__.py (0b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/offline_parser.py (6606b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10912b), main.py (11575b), processors/__init__.py (0b), processors/image_processor.py (3943b), README.md (8509b), references/tirith-blocking.md (1979b), requirements.txt (184b), skill-card.md (2547b), SKILL.md (9022b), utils/__init__.py (0b), utils/__pycache__/__init__.cpython-311.pyc (153b), utils/__pycache__/logger.cpython-311.pyc (1560b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.5:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.5\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"NOTION_API_KEY\", \"LLM_API_KEY\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"allowedEnv\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"OBSIDIAN_VAULT_PATH\", \"NOTION_API_KEY\", \"NOTION_ARTICLE_DATABASE_ID\", \"LLM_API_KEY\", \"LLM_BASE_URL\", \"LLM_MODEL\", \"WECHAT_COOKIES_FILE\", \"ZHIHU_COOKIES_FILE\"], \"securityNote\": \"OSS 凭证用于图片上传存储；Notion 和 LLM 为可选集成，不配置则跳过对应功能\", \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.5\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 离线输入（HTML / MHTML）\r\n\r\n除 URL 外，支持直接喂入 HTML 文本或 `.mhtml` 文件（源自 wechat-article-capture 技能的三级策略），由 article-fetcher 负责图片上传 OSS、替换 URL 与归档。解析层已内置三处陷阱修复：微信懒加载 `data-src`→`src` 并删除 `data-src`、MHTML 编码乱码（charset 探测）、`data:image/svg+xml` 占位符过滤。\r\n\r\n```bash\r\n# 三级：MHTML 文件\r\npython3 main.py --mhtml page.mhtml --url https://mp.weixin.qq.com/s/xxx 标签1\r\n\r\n# 二级：粘贴 HTML（stdin）\r\ncat page.html | python3 main.py --html - --url https://mp.weixin.qq.com/s/xxx\r\n\r\n# 一级 / 原路径（不变）\r\npython3 main.py \"https://mp.weixin.qq.com/s/xxx\" 标签1\r\n```\r\n\r\n- `--platform` 默认 `wechat`（直传 HTML/MHTML 无 URL，无法自动探测平台）\r\n- `--url` 可选：作为图片下载 Referer 与归档 link；不传时回退平台默认 Referer（微信 `mp.weixin.qq.com`）\r\n- 解析产出标准 `article_data`，后半段管线（OSS 上传 → 替换 → 打标 → 字数 → 归档）与 URL 模式完全复用\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/收件箱/`，命名规则 `{YYYY-MM-DD}_{title}.md`。\r\n\r\n> 💡 **本地适配**：Vault 去序号后，存档目录从 `1-收件箱/` 改为 `收件箱/`（`obsidian_archiver.py` 已同步修正）。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：执行 `playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 数据本地存储**：`.md` 文件写入本地磁盘，不经过网络。LLM 关键词提取为可选功能，启用时文章摘要会发送至配置的 API 端点\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容（前 12000 字符）会发送至配置的 API 端点（仅用于关键词提取）。不配置则不发送\r\n- **图片下载**：自动下载原文图片并上传 OSS，过程中会请求第三方图片服务器，请确保有权抓取目标文章\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 数据本地**：`.md` 文件写入本地磁盘，零网络外发\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.5:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.5 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 离线输入（HTML / MHTML）\r\n\r\n除 URL 抓取外，也支持直接传入 HTML 文本或 `.mhtml` 文件（微信文章三级采集策略），由本工具完成图片 OSS 上传、URL 替换与 Obsidian/Notion 归档。\r\n\r\n```bash\r\n# 三级：MHTML 文件\r\npython3 main.py --mhtml page.mhtml --url https://mp.weixin.qq.com/s/xxx 标签1\r\n\r\n# 二级：粘贴 HTML（stdin）\r\ncat page.html | python3 main.py --html - --url https://mp.weixin.qq.com/s/xxx\r\n\r\n# 一级 / 原路径（不变）\r\npython3 main.py \"https://mp.weixin.qq.com/s/xxx\" 标签1\r\n```\r\n\r\n- `--platform` 默认 `wechat`（直传 HTML/MHTML 无 URL，无法自动探测平台）\r\n- `--url` 可选：图片下载 Referer 与归档 link；不传时回退平台默认 Referer（微信 `mp.weixin.qq.com`）\r\n- 已修复微信懒加载（`data-src`→`src` 并删除）、MHTML 编码乱码、`data:image/svg+xml` 占位符过滤\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 数据完全本地**: `.md` 文件写入本地磁盘，零网络外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.5:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.5\",\n  \"publishedAt\": 1786192142791\n}\n\nFile v1.3.5:references/tirith-blocking.md\n\n# Tirith 安全扫描阻塞：ClawHub 安装限制\r\n\r\n## 问题\r\n\r\narticle-fetcher（Trust: community）通过 ClawHub 安装时被 Tirith 硬阻止：\r\n\r\n```\r\nCRITICAL exfiltration   config.py:34   \"self.notion_api_key = os.getenv('NOTION_API_KEY')\"\r\nCRITICAL exfiltration   config.py:38   \"self.llm_api_key = os.getenv('LLM_API_KEY', '').strip()\"\r\nDecision: BLOCKED — Blocked (community source + dangerous verdict, 2 findings)\r\n```\r\n\r\n## 已尝试的绕过方法（全部失败）\r\n\r\n| 方法 | 结果 |\r\n|------|------|\r\n| `hermes skills install clawhub:ajayhao/article-fetcher` | BLOCKED |\r\n| `hermes skills install https://github.com/AjayHao/article-fetcher` | BLOCKED（同样 scanned as community） |\r\n| `hermes config set security.tirith_enabled false` | 不影响安装时扫描 |\r\n| `--force` 参数 | \"--force does not override a dangerous verdict\" |\r\n| `tirith pending resolve` | No pending decisions |\r\n| SKILL.md 中声明 `requires.env` 含 NOTION_API_KEY + LLM_API_KEY | 不生效（v1.3.4 已声明仍被拦） |\r\n| SKILL.md 中加 `securityNote` | 不生效 |\r\n\r\n## 根因\r\n\r\nTirith 对 **community 源**的技能有独立规则：任何读取 API-key 类环境变量的代码直接判 CRITICAL exfiltration，**不参考 metadata 声明**。这是 Trust 级别决定的，不是代码或配置问题。\r\n\r\n## 当前解决方案\r\n\r\n**本地安装**（绕过安全扫描）：\r\n\r\n```bash\r\ngit clone https://github.com/AjayHao/article-fetcher ~/.hermes/skills/article-fetcher\r\n# 或更新\r\ncd ~/.hermes/skills/article-fetcher && git pull\r\n```\r\n\r\n模式显示为 `local`，无自动更新。\r\n\r\n## 根本解决方案\r\n\r\n在 ClawHub 上将技能 **Trust 从 community 升级到 verified**。verified 源的技能 Tirith 会放行 env var 读取。\r\n\r\n## 已验证受影响的技能\r\n\r\n| 技能 | ClawHub 状态 | Trust |\r\n|------|:--:|:--:|\r\n| article-fetcher | 有 | community |\r\n| video-summarizer | 有 | community |\r\n| knowledge-distill | 有 | community |\n\nFile v1.3.5:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.5 (2026-08-08)\r\n\r\n### ✨ 离线输入（HTML / MHTML）\r\n\r\n- **新增 `fetchers/offline_parser.py`**：将 HTML 文本 / `.mhtml` 文件解析为标准 `article_data`，复用既有后半段管线（OSS 上传 → 替换 URL → 打标 → 字数 → Obsidian/Notion 归档）\r\n- **`main.py` 抽出 `process_and_archive(article_data, platform, url, tags)`**：URL 抓取与离线输入共用同一归档流程，行为零变化\r\n- **新增离线入口**：`archive_from_html()` / `archive_from_mhtml()`\r\n- **CLI 扩展（`sys.argv` 手动解析，不使用 argparse，遵循安全合规约定）**：`--html`（支持 `-` 读 stdin）/`--mhtml`/`--platform`（默认 wechat）/`--url`（可选）\r\n- **三陷阱修复（源自 wechat-article-capture 技能沉淀）**：\r\n  - 微信懒加载：`data-src`→`src` 并 `del data-src`，根治 markdownify 读占位符\r\n  - MHTML 编码：`get_payload(decode=True)` 取 bytes 后按 `get_content_charset()` 或 `utf-8` 解码，避免乱码\r\n  - `data:image/svg+xml` 占位符过滤，图片列表归一化（去 query、去重）确保 URL 替换可命中\r\n\r\n### 🔧 本地适配\r\n\r\n- **Vault 去序号**：存档目录从 `1-收件箱/` 改为 `收件箱/`（知衍库 2026-07 架构调整）\r\n- **移除 `distilled: false`**：蒸馏状态判定已改为文件存在性模型，不再依赖 frontmatter 标记\r\n- **裁撤 `wechat-article-capture` 技能**：v1.3.5 的 MHTML/HTML 离线模式已覆盖其全部功能，无需单独维护\r\n\r\n### 🐛 已知问题\r\n\r\n- ~~**Notion 转换**：`### | title` 格式标题会被 Notion API 误判为表格分隔符~~ **已修复 (v1.3.5-local)**：根因为 `_parse_inline_html` 将内联 `<img>` 当作 image block 塞入 `rich_text`，导致 `text` 字段缺失。修复为内联图片转文本链接 `[alt](url)`。\r\n\r\n---\r\n\r\n## v1.3.4 (2026-07-19)\r\n\r\n### 🔒 Tirith `requires.env` 补全\r\n\r\n- **`NOTION_API_KEY` / `LLM_API_KEY` 加入 `requires.env`**：代码实际读取的可选凭证必须在元数据声明，消除 CRITICAL exfiltration 误报\r\n\r\n---\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.5:skill-card.md\n\n## Description:\n\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）。\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ajayhao](https://clawhub.ai/user/ajayhao)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and knowledge workers use this skill to fetch articles from supported Chinese content platforms or offline HTML/MHTML input, preserve article text and images, and archive the result into Obsidian or optionally Notion.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Article images are uploaded to Alibaba OSS during normal processing.\n\nMitigation: Install only when this upload behavior is acceptable and use tightly scoped OSS credentials.\n\nRisk: Article text can be sent to the configured LLM provider when LLM keyword extraction is enabled.\n\nMitigation: Avoid sensitive or private articles unless external processing is acceptable, or leave LLM credentials unconfigured.\n\nRisk: Notion pages can be created when Notion credentials are present.\n\nMitigation: Use a Notion integration scoped only to the intended database and omit Notion credentials when cloud archiving is not needed.\n\nRisk: The security scan reports a vulnerable lxml dependency pin.\n\nMitigation: Upgrade lxml to 6.1.0 or newer before deployment.\n\nRisk: A bundled note discusses bypassing a prior local-install security block.\n\nMitigation: Do not treat that note as a security recommendation; review and scan the skill before deployment.\n\n## Reference(s):\n\n- [Project homepage](https://github.com/AjayHao/article-fetcher)\n- [ClawHub skill page](https://clawhub.ai/ajayhao/skills/article-fetcher)\n- [Tirith blocking note](references/tirith-blocking.md)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown files, Notion pages, or terminal text output]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [May upload article images to Alibaba OSS and create Notion pages when the corresponding credentials are configured.]\n\n## Skill Version(s):\n\n1.3.5 (source: server release metadata, Hermes metadata, and changelog)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nFile v1.3.5:requirements.txt\n\nrequests==2.33.0\r\nbeautifulsoup4==4.14.3\r\noss2==2.19.1\r\nnotion-client==3.0.0\r\npython-dotenv==1.2.2\r\nlxml==6.0.4\r\nmarkdownify==1.1.0\r\nplaywright==1.54.0\r\npyyaml==6.0.2\r\nurllib3==2.7.0\n\nArchive v1.3.4: 26 files, 45423 bytes\n\nFiles: archiver/__init__.py (0b), archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4807b), CHANGELOG.md (8665b), config.py (3159b), detector/__init__.py (0b), detector/platform_detector.py (1161b), fetchers/__init__.py (0b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10912b), main.py (8396b), processors/__init__.py (0b), processors/image_processor.py (3943b), README.md (7609b), requirements.txt (184b), skill-card.md (2508b), SKILL.md (7771b), utils/__init__.py (0b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.4:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.4\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"NOTION_API_KEY\", \"LLM_API_KEY\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"allowedEnv\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"OBSIDIAN_VAULT_PATH\", \"NOTION_API_KEY\", \"NOTION_ARTICLE_DATABASE_ID\", \"LLM_API_KEY\", \"LLM_BASE_URL\", \"LLM_MODEL\", \"WECHAT_COOKIES_FILE\", \"ZHIHU_COOKIES_FILE\"], \"securityNote\": \"OSS 凭证用于图片上传存储；Notion 和 LLM 为可选集成，不配置则跳过对应功能\", \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.4\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-收件箱/`，命名规则 `{YYYY-MM-DD}_{title}.md`。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：执行 `playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 数据本地存储**：`.md` 文件写入本地磁盘，不经过网络。LLM 关键词提取为可选功能，启用时文章摘要会发送至配置的 API 端点\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容（前 12000 字符）会发送至配置的 API 端点（仅用于关键词提取）。不配置则不发送\r\n- **图片下载**：自动下载原文图片并上传 OSS，过程中会请求第三方图片服务器，请确保有权抓取目标文章\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 数据本地**：`.md` 文件写入本地磁盘，零网络外发\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.4:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.4 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 数据完全本地**: `.md` 文件写入本地磁盘，零网络外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.4:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.4\",\n  \"publishedAt\": 1784425489723\n}\n\nFile v1.3.4:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.4 (2026-07-19)\r\n\r\n### 🔒 Tirith `requires.env` 补全\r\n\r\n- **`NOTION_API_KEY` / `LLM_API_KEY` 加入 `requires.env`**：代码实际读取的可选凭证必须在元数据声明，消除 CRITICAL exfiltration 误报\r\n\r\n---\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.4:skill-card.md\n\n## Description: <br>\nFetches articles from WeChat, Xiaohongshu, Douban, and Zhihu, uploads article images to Aliyun OSS, extracts tags with an optional LLM, and archives results to Obsidian or optional Notion. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ajayhao](https://clawhub.ai/user/ajayhao) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nExternal users and developers use this skill to capture supported Chinese content-platform articles and save structured knowledge-base records in Obsidian, Notion, or terminal preview workflows. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill requires Aliyun OSS credentials and can use Notion and LLM API credentials when those integrations are enabled. <br>\nMitigation: Use limited-scope OSS and Notion credentials, configure only needed integrations, and keep API keys in the local agent environment. <br>\nRisk: When LLM tag extraction is enabled, article text is sent to the configured LLM endpoint. <br>\nMitigation: Disable the LLM integration for sensitive content or avoid sending private, regulated, or confidential articles to the configured endpoint. <br>\nRisk: Platform cookies may be used for WeChat or Zhihu fallback fetching. <br>\nMitigation: Use low-risk platform cookies and rotate or revoke them if they are no longer needed. <br>\nRisk: The security guidance recommends upgrading the pinned lxml dependency before routine use. <br>\nMitigation: Review and update the pinned dependency set during deployment maintenance. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ajayhao/skills/article-fetcher) <br>\n- [Project homepage](https://github.com/AjayHao/article-fetcher) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [Text, Markdown, Files] <br>\n**Output Format:** [Markdown files, Notion pages, and terminal text] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [May upload article images to Aliyun OSS and optionally create Notion pages when configured.] <br>\n\n## Skill Version(s): <br>\n1.3.4 (source: server release metadata, SKILL.md metadata, CHANGELOG v1.3.4 released 2026-07-19) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.3.4:requirements.txt\n\nrequests==2.33.0\r\nbeautifulsoup4==4.14.3\r\noss2==2.19.1\r\nnotion-client==3.0.0\r\npython-dotenv==1.2.2\r\nlxml==6.0.4\r\nmarkdownify==1.1.0\r\nplaywright==1.54.0\r\npyyaml==6.0.2\r\nurllib3==2.7.0\n\nArchive v1.3.3: 26 files, 45452 bytes\n\nFiles: archiver/__init__.py (0b), archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4807b), CHANGELOG.md (8429b), config.py (3159b), detector/__init__.py (0b), detector/platform_detector.py (1161b), fetchers/__init__.py (0b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10912b), main.py (8396b), processors/__init__.py (0b), processors/image_processor.py (3943b), README.md (7609b), requirements.txt (184b), skill-card.md (2775b), SKILL.md (7738b), utils/__init__.py (0b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.3:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.3\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"allowedEnv\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"OBSIDIAN_VAULT_PATH\", \"NOTION_API_KEY\", \"NOTION_ARTICLE_DATABASE_ID\", \"LLM_API_KEY\", \"LLM_BASE_URL\", \"LLM_MODEL\", \"WECHAT_COOKIES_FILE\", \"ZHIHU_COOKIES_FILE\"], \"securityNote\": \"OSS 凭证用于图片上传存储；Notion 和 LLM 为可选集成，不配置则跳过对应功能\", \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.3\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-收件箱/`，命名规则 `{YYYY-MM-DD}_{title}.md`。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：执行 `playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 数据本地存储**：`.md` 文件写入本地磁盘，不经过网络。LLM 关键词提取为可选功能，启用时文章摘要会发送至配置的 API 端点\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容（前 12000 字符）会发送至配置的 API 端点（仅用于关键词提取）。不配置则不发送\r\n- **图片下载**：自动下载原文图片并上传 OSS，过程中会请求第三方图片服务器，请确保有权抓取目标文章\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 数据本地**：`.md` 文件写入本地磁盘，零网络外发\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.3:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.3 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 数据完全本地**: `.md` 文件写入本地磁盘，零网络外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.3:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.3\",\n  \"publishedAt\": 1784424137231\n}\n\nFile v1.3.3:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.3:skill-card.md\n\n## Description: <br>\nFetches articles from WeChat Official Accounts, Xiaohongshu, Douban, and Zhihu, uploads images to Alibaba OSS, extracts keywords with an optional LLM, and archives the result to an Obsidian vault with optional Notion sync. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ajayhao](https://clawhub.ai/user/ajayhao) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nAgents and knowledge-management users use this skill to collect supported Chinese web articles, normalize their content and images, and store the result in Obsidian or Notion for later retrieval. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Article images are uploaded to the user's Alibaba OSS bucket. <br>\nMitigation: Use a dedicated bucket or path with least-privilege OSS credentials and confirm the user has rights to fetch and store target article images. <br>\nRisk: When optional LLM keyword extraction is configured, article text is sent to the configured LLM provider. <br>\nMitigation: Leave LLM credentials unset for local keyword fallback, or use an approved provider and endpoint for the article data being processed. <br>\nRisk: Optional Notion archiving sends article content to Notion. <br>\nMitigation: Grant the Notion integration access only to the target database and avoid archiving sensitive content unless Notion storage is approved. <br>\nRisk: Cookie files may contain platform login state. <br>\nMitigation: Use platform-specific cookie export files, keep them outside shared workspaces, and rotate or remove them when no longer needed. <br>\nRisk: The security summary calls out a vulnerable pinned dependency. <br>\nMitigation: Update lxml before processing untrusted XML-heavy content. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ajayhao/skills/article-fetcher) <br>\n- [Project homepage](https://github.com/AjayHao/article-fetcher) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, configuration, guidance] <br>\n**Output Format:** [Markdown archive files, optional Notion pages, and terminal status text] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [May upload article images to Alibaba OSS and may send article text to a configured LLM endpoint for keyword extraction.] <br>\n\n## Skill Version(s): <br>\n1.3.3 (source: server release metadata, SKILL.md metadata, and changelog, released 2026-07-06) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.3.3:requirements.txt\n\nrequests==2.33.0\r\nbeautifulsoup4==4.14.3\r\noss2==2.19.1\r\nnotion-client==3.0.0\r\npython-dotenv==1.2.2\r\nlxml==6.0.4\r\nmarkdownify==1.1.0\r\nplaywright==1.54.0\r\npyyaml==6.0.2\r\nurllib3==2.7.0\n\nArchive v1.3.2: 21 files, 44363 bytes\n\nFiles: archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4807b), CHANGELOG.md (7978b), config.py (3159b), detector/platform_detector.py (1161b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10934b), main.py (8396b), processors/image_processor.py (3943b), README.md (7609b), requirements.txt (184b), skill-card.md (2566b), SKILL.md (7387b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.2:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.2\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.2\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-收件箱/`，命名规则 `{YYYY-MM-DD}_{title}.md`。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：安装 `pip install playwright && playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 数据本地存储**：`.md` 文件写入本地磁盘，不经过网络。LLM 关键词提取为可选功能，启用时文章摘要会发送至配置的 API 端点\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容（前 12000 字符）会发送至配置的 API 端点（仅用于关键词提取）。不配置则不发送\r\n- **图片下载**：自动下载原文图片并上传 OSS，过程中会请求第三方图片服务器，请确保有权抓取目标文章\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 数据本地**：`.md` 文件写入本地磁盘，零网络外发\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.2:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.2 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 数据完全本地**: `.md` 文件写入本地磁盘，零网络外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.2:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.2\",\n  \"publishedAt\": 1784422688499\n}\n\nFile v1.3.2:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.2:skill-card.md\n\n## Description: <br>\nFetches articles from WeChat, Xiaohongshu, Douban, and Zhihu, uploads images to Aliyun OSS, extracts keywords with an optional LLM, and archives results to Obsidian or Notion. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ajayhao](https://clawhub.ai/user/ajayhao) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nExternal users and developers use this skill to capture supported social and article URLs, preserve article text and metadata, upload article images to OSS, and archive the result into a local Obsidian vault or an optional Notion database. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Aliyun OSS, Notion, cookie, and optional LLM credentials may grant access to sensitive services or content. <br>\nMitigation: Use least-privilege OSS and Notion credentials, store cookie files only for the intended platform, and avoid enabling optional services that are not needed. <br>\nRisk: When LLM keyword extraction is enabled, article text is sent to the configured LLM provider. <br>\nMitigation: Disable LLM configuration for local-only keyword extraction, or use an approved provider and avoid processing content that should not leave the environment. <br>\nRisk: The skill downloads article images from third-party servers and uploads them to OSS. <br>\nMitigation: Fetch only content the user has rights to archive, and consider stricter image and redirect validation before broad deployment. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ajayhao/skills/article-fetcher) <br>\n- [Project homepage](https://github.com/AjayHao/article-fetcher) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, files, API calls, configuration] <br>\n**Output Format:** [Markdown files with YAML frontmatter, Notion database entries, OSS image URLs, and terminal status text] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Requires Aliyun OSS credentials; Obsidian, Notion, cookies, Playwright, and LLM keyword extraction are optional depending on configuration.] <br>\n\n## Skill Version(s): <br>\n1.3.2 (source: server release metadata, SKILL metadata, README, CHANGELOG released 2026-07-06) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.3.2:requirements.txt\n\nrequests==2.33.0\r\nbeautifulsoup4==4.14.3\r\noss2==2.19.1\r\nnotion-client==3.0.0\r\npython-dotenv==1.2.2\r\nlxml==6.0.4\r\nmarkdownify==1.1.0\r\nplaywright==1.54.0\r\npyyaml==6.0.2\r\nurllib3==2.7.0\n\nArchive v1.3.1: 26 files, 44818 bytes\n\nFiles: archiver/__init__.py (0b), archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4750b), CHANGELOG.md (7665b), config.py (3159b), detector/__init__.py (0b), detector/platform_detector.py (1161b), fetchers/__init__.py (0b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10934b), main.py (8396b), processors/__init__.py (0b), processors/image_processor.py (3205b), README.md (7609b), requirements.txt (184b), skill-card.md (2498b), SKILL.md (7374b), utils/__init__.py (0b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.1:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.1\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.1\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-收件箱/`，命名规则 `{title}.md`。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：安装 `pip install playwright && playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 数据本地存储**：`.md` 文件写入本地磁盘，不经过网络。LLM 关键词提取为可选功能，启用时文章摘要会发送至配置的 API 端点\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容（前 12000 字符）会发送至配置的 API 端点（仅用于关键词提取）。不配置则不发送\r\n- **图片下载**：自动下载原文图片并上传 OSS，过程中会请求第三方图片服务器，请确保有权抓取目标文章\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 数据本地**：`.md` 文件写入本地磁盘，零网络外发\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.1:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.1 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 数据完全本地**: `.md` 文件写入本地磁盘，零网络外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.1:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.1\",\n  \"publishedAt\": 1784421402614\n}\n\nFile v1.3.1:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.1:skill-card.md\n\n## Description: <br>\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion） <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ajayhao](https://clawhub.ai/user/ajayhao) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers, researchers, and knowledge workers use this skill to fetch supported Chinese-platform articles, process images and tags, and archive the result into an Obsidian vault, a Notion database, or terminal preview output. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: Pinned dependency requires review before installation. <br>\nMitigation: Update lxml to 6.1.0 or newer before installing the skill. <br>\nRisk: The skill uses OSS, Notion, and platform cookie credentials. <br>\nMitigation: Use minimum-scope OSS and Notion credentials, and keep Zhihu/WeChat cookie files narrow and platform-specific. <br>\nRisk: Optional LLM keyword extraction can send fetched article text and titles to the configured provider. <br>\nMitigation: Enable the LLM endpoint only when that provider is acceptable for the article content being processed. <br>\nRisk: Article and image fetching makes outbound requests to source platforms and third-party image servers. <br>\nMitigation: Fetch and archive only content the operator is authorized to access and store. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ajayhao/skills/article-fetcher) <br>\n- [Project homepage](https://github.com/AjayHao/article-fetcher) <br>\n- [Publisher profile](https://clawhub.ai/user/ajayhao) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [text, markdown, files, API calls] <br>\n**Output Format:** [Markdown files with YAML frontmatter, optional Notion pages, and terminal status text] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [May write local Obsidian files, upload article images to OSS, and create Notion database entries when configured.] <br>\n\n## Skill Version(s): <br>\n1.3.1 (source: server release metadata, SKILL.md metadata, CHANGELOG) <br>\n\n## Ethical Considerations: <br>\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment. <br>\n\nFile v1.3.1:requirements.txt\n\nrequests==2.33.0\r\nbeautifulsoup4==4.14.3\r\noss2==2.19.1\r\nnotion-client==3.0.0\r\npython-dotenv==1.2.2\r\nlxml==6.0.4\r\nmarkdownify==1.1.0\r\nplaywright==1.54.0\r\npyyaml==6.0.2\r\nurllib3==2.7.0\n\nArchive v1.3.0: 26 files, 44361 bytes\n\nFiles: archiver/__init__.py (0b), archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4750b), CHANGELOG.md (7197b), config.py (3159b), detector/__init__.py (0b), detector/platform_detector.py (1161b), fetchers/__init__.py (0b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (6792b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10934b), main.py (8396b), processors/__init__.py (0b), processors/image_processor.py (3205b), README.md (7609b), requirements.txt (184b), skill-card.md (2496b), SKILL.md (7035b), utils/__init__.py (0b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nFile v1.3.0:SKILL.md\n\n---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.0\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.0\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 处理流程\r\n\r\n```\r\nURL → 平台识别 → 内容抓取 → 图片上传 OSS → 关键词提取 (LLM → 词频降级)\r\n                                              │\r\n                         ┌────────────────────┴────────────────────┐\r\n                         ▼                                         ▼\r\n                  OBSIDIAN_VAULT_PATH?                     NOTION_API_KEY?\r\n                    ✅ → Obsidian .md                       ✅ → Notion 页面\r\n                    都不配 → 终端输出\r\n```\r\n\r\n## Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-收件箱/`，命名规则 `{title}.md`。\r\n\r\n### 文件示例\r\n\r\n```markdown\r\n---\r\ntitle: \"AI Agent 技术全景解析\"\r\nsource: wechat\r\nauthor: \"张三\"\r\nlink: \"https://mp.weixin.qq.com/s/xxx\"\r\ntags:\r\n  - AI Agent\r\n  - LLM\r\n  - 技术架构\r\npub_date: \"2026-05-10 14:30:00\"\r\nwords: 3500\r\nfetched: \"2026-06-22 20:15:00\"\r\narticle_id: \"uuid\"\r\n---\r\n\r\n# AI Agent 技术全景解析\r\n\r\n正文内容（HTML → Markdown 转换，OSS 图片已内嵌）...\r\n```\r\n\r\n### 设计要点\r\n\r\n- **Frontmatter**：YAML 格式，Obsidian 原生兼容，tag 列表可直接被 Dataview 查询\r\n- **文件命名**：标题即文件名，非法字符自动替换为 `-`\r\n- **HTML→Markdown**：通过 `markdownify` 转换，保留链接、图片、粗斜体等格式\r\n- **非阻塞**：Obsidian 写入失败不影响 Notion 存档（如果配置了双写）\r\n\r\n## Notion 数据库字段（可选）\r\n\r\n配置 `NOTION_API_KEY` + `NOTION_ARTICLE_DATABASE_ID` 后启用双写。\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| Title | title | 文章标题（≤200 字符） |\r\n| Source | rich_text | 来源平台 |\r\n| Author | rich_text | 作者 |\r\n| Link | url | 原文链接 |\r\n| Tags | multi_select | 自动提取关键词 + 手动标签 |\r\n| PubDate | date | 发布时间 |\r\n| Words | number | 字数统计（剔除 HTML） |\r\n| ts | date | 存档时间（东八区） |\r\n\r\n## 关键说明\r\n\r\n- **Cookies**：知乎/微信反爬需配置（Netscape 格式），小红书/豆瓣无需登录\r\n- **知乎 / 微信二级回退**：HTTP (Cookies) → Playwright 浏览器 → 失败放弃。自动检测反爬页面（环境异常/验证码）并回退\r\n- **空内容检测**：正文为空或含反爬关键词时视为抓取失败，不生成垃圾文件\r\n- **Playwright**：安装 `pip install playwright && playwright install chromium` 后自动生效，未安装时跳过浏览器回退\r\n- **关键词**：LLM 优先（OpenAI 兼容接口），未配置或失败自动降级本地词频\r\n- **图片**：上传失败不阻断，成功多少记录多少\r\n- **时间**：统一 `YYYY-MM-DD HH:MM:SS`，缺失时留空（不伪造）\r\n- **模块**：`main.py` 可作 Python 模块调用：`from main import fetch_and_archive_article`\r\n- **Obsidian 优先**：本地知识库零外部依赖，符合\"数据私有化\"理念\r\n\r\n## 安全与隐私\r\n\r\n- **URL 校验**：严格白名单匹配 hostname，拒绝路径拼接攻击\r\n- **Cookie 隔离**：Netscape Cookies 按域名过滤，仅附加到匹配的请求\r\n- **LLM 数据外发**：配置 `LLM_API_KEY` 时，文章内容会发送至对应 API（仅用于关键词提取）\r\n- **敏感信息**：AK/SK/Key 等仅存储于本地，skill 不会外泄\r\n- **Obsidian 数据完全本地**：`.md` 文件写入本地磁盘，零网络外发\r\n- **权限最小化**：OSS Bucket 建议仅授予 PutObject/GetObject；Notion Integration 仅授予目标数据库读写权限\r\n- **依赖锁定**：requirements.txt 使用精确版本号，避免供应链风险\r\n\r\n## 扩展平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 的 `ALLOWED_HOSTS` 添加平台域名\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\n\nFile v1.3.0:README.md\n\n# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.0 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `source` / `author` / `link` / `tags` / `pub_date` / `words` / `fetched` / `article_id`\r\n- **正文**: HTML → Markdown 转换（`markdownify`），OSS 图片已内嵌\r\n\r\n### Notion 数据库字段（可选）\r\n\r\n| 字段 | 类型 | 说明 |\r\n|------|------|------|\r\n| **Title** | title | 文章标题（≤200 字符） |\r\n| **Source** | rich_text | 来源平台 |\r\n| **Author** | rich_text | 作者 |\r\n| **Link** | url | 原文链接 |\r\n| **Tags** | multi_select | 关键词（自动提取 + 手动传入） |\r\n| **PubDate** | date | 发布日期 |\r\n| **Words** | number | 字数统计 |\r\n| **ts** | date | 存档时间（东八区） |\r\n\r\n### OSS 路径规范\r\n\r\n图片: `articles/<平台码>/<article_id>/article-001.jpg`\r\n\r\n## 📋 使用方法\r\n\r\n### 命令行\r\n\r\n```bash\r\ncd D:\\GitRepo\\article-fetcher\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n### Python 模块\r\n\r\n```python\r\nfrom main import fetch_and_archive_article\r\n\r\nresult = fetch_and_archive_article(\r\n    url=\"https://example.com/article\",\r\n    tags=[\"tag1\", \"tag2\"]\r\n)\r\n\r\nif result['success']:\r\n    print(f\"✅ {result['message']}\")\r\n    print(f\"📁 存档目标：{result.get('archived_to', [])}\")\r\nelse:\r\n    print(f\"❌ {result['message']} ({result['error_code']})\")\r\n```\r\n\r\n### Cookies 配置\r\n\r\n知乎和部分微信公众号需要登录态才能抓取完整内容，否则返回 403。\r\n\r\n**知乎/微信二级回退策略**：Cookies HTTP 请求 → Playwright headless 浏览器 → 失败放弃（自动检测反爬页面）。\r\n\r\n**获取步骤**：\r\n1. 浏览器登录对应平台\r\n2. 安装 Cookie-Editor 插件\r\n3. 导出 Cookies 为 Netscape 格式\r\n4. 保存到 `~/.cookies/<platform>_cookies.txt`\r\n\r\n| 平台 | 默认路径 | 必需？ |\r\n|------|----------|--------|\r\n| 知乎 | `~/.cookies/zhihu_cookies.txt` | ✅ 必需 |\r\n| 微信 | `~/.cookies/wechat_cookies.txt` | ❌ 可选 |\r\n| 小红书 / 豆瓣 | — | 不需要 |\r\n\r\n## 🔌 扩展新平台\r\n\r\n1. `fetchers/` 下创建 `xxx_fetcher.py`，继承 `BaseFetcher` 实现 `fetch_article()`\r\n2. `detector/platform_detector.py` 添加 URL 正则\r\n3. `main.py` 的 `FETCHER_REGISTRY` 注册\r\n\r\n## 🔐 安全说明\r\n\r\n- **Obsidian 数据完全本地**: `.md` 文件写入本地磁盘，零网络外发\r\n- Cookies 等敏感信息存储在本地，skill 不会上传或外泄\r\n- OSS Bucket 建议配置最小权限（仅 PutObject/GetObject）\r\n- Notion Integration 仅授予目标数据库读写权限\r\n- 阿里云 Coding Plan Key（`sk-sp-` 前缀）禁止用于自动化脚本\r\n\r\n## 🔧 故障排查\r\n\r\n| 问题 | 原因 | 解决方案 |\r\n|------|------|----------|\r\n| 知乎 403 | Cookies 未配置或过期 | 更新 Cookies 或 Playwright 自动回退 |\r\n| 微信公众号 403 | 反爬拦截 | Playwright 自动回退或配置 Cookies |\r\n| 图片上传失败 | OSS 配置错误 | 检查 AK/SK/Bucket/Endpoint |\r\n| Obsidian 写入失败 | 路径不存在或权限不足 | 检查 `OBSIDIAN_VAULT_PATH` |\r\n| Notion 推送失败 | API Key 过期或权限不足 | 更新 Key，确认数据库权限 |\r\n| 关键词质量差 | LLM 未配置/失败 | 配置 `LLM_API_KEY` 提升质量 |\r\n| 未存档（仅终端输出） | 存档目标均未配置 | 配置 `OBSIDIAN_VAULT_PATH` 或 Notion |\r\n\r\n查看详细日志：\r\n```bash\r\nDEBUG=true python3 main.py \"URL\" 2>&1 | tee fetch.log\r\ncat logs/article-fetcher.log\r\n```\n\nFile v1.3.0:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.0\",\n  \"publishedAt\": 1784420151577\n}\n\nFile v1.3.0:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_processor.py` endpoint 前置 `lstrip` 处理\r\n- **小红书 JSON 提取**：`xhs_fetcher.py` 改用 `html.unescape()` + `get_text()` 防 BS4 转义破坏 JSON\r\n- **HTTP Session 复用**：`http_client.py` 改为模块级单例 + 连接池\r\n- **微信图片空白**：`main.py` 新增 `data-src` → `src` 修复，解决懒加载图片在 Markdown 中丢失\r\n- **`__init__.py` 恢复**：5 个子包补回 `__init__.py`，解决包导入失败\r\n\r\n### 🔧 优化\r\n\r\n- **`.env` 加载策略**：`$AGENT_HOME` > `$HERMES_HOME` > 当前目录，agent 无关通用方案\r\n- **标签 Obsidian 兼容**：`tag_extractor.py` LLM prompt 约束 + 统一清洗空格→`-`\r\n- **跨平台路径示例**：文档中 `OBSIDIAN_VAULT_PATH` 展示 Windows/macOS/Linux 三平台\r\n\r\n### 📝 文档\r\n\r\n- **SKILL.md / README.md 全面重写**：4 场景存档表、Obsidian Frontmatter 规范、Notion 降级为可选\r\n- `openclaw` metadata → `hermes`，路径 `~/.hermes` → `$AGENT_HOME`\r\n\r\n---\r\n\r\n## v1.0.2 (2026-05-10)\r\n\r\n### 🏗️ LLM 多平台抽象\r\n\r\n- **统一 LLM 配置**：`config.py` 移除 DashScope 专属字段（`dashscope_api_key`/`dashscope_base_url`/`dashscope_model`），改为 `LLM_API_KEY` + `LLM_BASE_URL` + `LLM_MODEL` 通用配置\r\n- **复用 video-summarizer 配置**：LLM 三个环境变量与 video-summarizer 共享 `.env`，无需重复设置\r\n- **重构 `tag_extractor.py`**：`extract_tags_llm()` 改为 OpenAI 兼容接口，支持 DeepSeek / DashScope / OpenAI / Groq 等任意平台\r\n- **LLM 可用性标记**：config 新增 `llm_available` 属性，一次判断即可决定是否启用 LLM\r\n- **升级日志**：LLM 调用增加模型名称打印（如 `deepseek-v4-pro`），便于排错\r\n\r\n---\r\n\r\n## v1.0.1 (2026-05-07)\r\n\r\n### 🔒 安全修复（ClawScan 扫描）\r\n\r\n- **Cookie 域隔离**: `base_fetcher.py` 重构 `_load_cookies()` 保留 domain 字段，新增 `_apply_cookies_for_url(url)` 按目标域名过滤，防止登录态泄露到非目标站点\r\n- **URL 严格校验**: `platform_detector.py` 改用 `urllib.parse.urlparse` + 白名单匹配 hostname，拒绝路径拼接攻击（如 `https://evil.com/mp.weixin.qq.com/...`）\r\n- **依赖版本锁定**: `requirements.txt` `>=` → `==` 精确版本，降低供应链风险\r\n\r\n### 📝 文档\r\n\r\n- **安全说明**: SKILL.md 新增「安全与隐私」章节，披露 LLM 数据外发、Cookie 隔离、权限最小化等安全边界\r\n- **扩展指南**: 更新平台扩展步骤（`ALLOWED_HOSTS` 替换旧正则描述）\r\n\r\n---\r\n\r\n## v1.0.0 (2026-05-07)\r\n\r\n### 🎯 正式发布\r\n\r\n- **文档精简**: SKILL.md 重写为精简扼要格式，层次清晰，面向用户与 Agent\r\n- **版本标注**: 统一版本号为 v1.0.0（SKILL.md metadata、README、CHANGELOG）\r\n- **清理冗余**: 移除 `tests/` 目录、`__init__.py`、`__pycache__/` 缓存文件\r\n- **安全加固**: 消除 `config.py` 中 `DEBUG` 变量暴露到全局模块命名空间\r\n- **SKILL.md**: 新增 Notion 数据库字段说明、扩展平台指南、模块调用示例\r\n\r\n---\r\n\r\n## v0.2.0 (2026-05-06)\r\n\r\n### ✨ 关键词提取优化\r\n\r\n- **LLM 优先策略**: 关键词提取优先调用 DashScope LLM 理解文章核心内容\r\n- **智能降级**: LLM 失败或未配置 `DASHSCOPE_API_KEY` 时自动降级为本地词频分析\r\n- **标题上下文**: 传递文章标题给 LLM，提升关键词提取准确度\r\n- **超时重试**: 3 次重试机制（60s → 90s → 120s），兼容 429/5xx 错误\r\n- **新增配置**: `.env.example` 添加 `DASHSCOPE_API_KEY`/`DASHSCOPE_BASE_URL`/`DASHSCOPE_MODEL` 说明\r\n\r\n### 📝 文档更新\r\n\r\n- SKILL.md 更新关键词提取描述（纯本地 → LLM 优先 + 降级方案）\r\n\r\n---\r\n\r\n## v0.1.1 (2026-05-01)\r\n\r\n### 🐛 Bug 修复\r\n\r\n- **关键词提取**: 移除 LLM API 依赖，改为纯本地词频方案，解决 Coding Plan Key 不能用于后端脚本的合规问题\r\n- **知乎抓取**: 修复 `#HttpOnly` 前缀导致 Cookies 解析失败（base_fetcher.py）\r\n- **知乎清理**: 移除知乎 HTML 中 64K+ CSS 噪音（内嵌 `<style>` 标签、动态 class、JS 属性）\r\n- **知乎图片提取**: 修复图片选择器不匹配问题，提取 `_r.jpg` + `_720w.jpg` 双变体 URL\r\n- **Notion 存档**: 修复单段文本超过 2000 字符导致 400 错误，段落/引用/代码块自动拆分\r\n- **Notion 图片**: 修复知乎图片 SVG 占位符未被跳过的问题，增加 `data-original` 优先级\r\n\r\n### 🔧 优化\r\n\r\n- **Cookies 路径**: 默认路径改为 `~/.cookies/<platform>_cookies.txt`，可通过环境变量覆盖\r\n- **安全文档**: SKILL.md 新增完整的「安全与隐私说明」章节\r\n- **SKILL.md**: 更新关键词提取描述（LLM → 纯本地词频）\r\n\r\n---\r\n\r\n## v0.1.0 (2026-04-29)\r\n\r\n### ✨ 初始版本\r\n\r\n- 多平台支持：微信公众号、小红书、豆瓣、知乎\r\n- 图片上传阿里云 OSS\r\n- 关键词提取（LLM + 词频降级方案）\r\n- 一键存档到 Notion\n\nFile v1.3.0:skill-card.md\n\n## Description: <br>\nFetches articles from WeChat, Xiaohongshu, Douban, and Zhihu, uploads images to Aliyun OSS, extracts keywords, and archives results to Obsidian with optional Notion sync. <br>\n\nThis skill is ready for commercial/non-commercial use. <br>\n\n## Publisher: <br>\n[ajayhao](https://clawhub.ai/user/ajayhao) <br>\n\n### License/Terms of Use: <br>\nMIT-0 <br>\n\n\n## Use Case: <br>\nDevelopers and knowledge-management users can use this skill to collect supported Chinese web articles, normalize them into markdown-style knowledge records, and save them locally in Obsidian or optionally to Notion. It is suited for article archiving workflows that require OSS-hosted images and keyword extraction. <br>\n\n### Deployment Geography for Use: <br>\nGlobal <br>\n\n## Known Risks and Mitigations: <br>\nRisk: The skill uses Aliyun OSS credentials and may use Notion, LLM, and site-cookie secrets. <br>\nMitigation: Use narrowly scoped credentials and cookies, avoid high-privilege accounts, and limit OSS and Notion permissions to the required bucket or database. <br>\nRisk: ClawScan reports material risk from pinned network/parser dependencies and redirect handling. <br>\nMitigation: Review or update dependencies before installation and inspect redirect behavior before processing untrusted URLs. <br>\nRisk: Article content may be sent to a configured LLM provider for keyword extraction. <br>\nMitigation: Disable LLM configuration or use an approved provider when articles contain private, regulated, or confidential content. <br>\n\n\n## Reference(s): <br>\n- [ClawHub skill page](https://clawhub.ai/ajayhao/skills/article-fetcher) <br>\n- [Project homepage](https://github.com/AjayHao/article-fetcher) <br>\n\n\n## Skill Output: <br>\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance] <br>\n**Output Format:** [Markdown files, terminal status messages, Python result dictionaries, and setup commands] <br>\n**Output Parameters:** [1D] <br>\n**Other Properties Related to Output:** [Can write Obsidian markdown files, create Notion pages when configured, and upload article images to Aliyun OSS.] <br>\n\n## Skill Version(s): <br>\n1.3.0 (source: \n\nArchive v1.2.0: 21 files, 42324 bytes\n\nFiles: archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4750b), CHANGELOG.md (6644b), config.py (3159b), detector/platform_detector.py (1161b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (3848b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (10934b), main.py (8379b), processors/image_processor.py (2935b), README.md (7570b), requirements.txt (184b), skill-card.md (2472b), SKILL.md (6891b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nArchive v1.1.1: 21 files, 40646 bytes\n\nFiles: archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4725b), CHANGELOG.md (5517b), config.py (3159b), detector/platform_detector.py (1161b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (3848b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (6900b), main.py (8379b), processors/image_processor.py (2935b), README.md (7457b), requirements.txt (164b), skill-card.md (2585b), SKILL.md (6535b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)\n\nArchive v1.1.0: 21 files, 40580 bytes\n\nFiles: archiver/notion_archiver.py (15412b), archiver/obsidian_archiver.py (4725b), CHANGELOG.md (5517b), config.py (3159b), detector/platform_detector.py (1161b), fetchers/base_fetcher.py (3834b), fetchers/douban_fetcher.py (5950b), fetchers/wechat_fetcher.py (3848b), fetchers/xhs_fetcher.py (4159b), fetchers/zhihu_fetcher.py (6900b), main.py (8379b), processors/image_processor.py (2935b), README.md (7457b), requirements.txt (164b), skill-card.md (2300b), SKILL.md (6535b), utils/http_client.py (3047b), utils/logger.py (748b), utils/tag_extractor.py (9700b), utils/word_counter.py (449b), _meta.json (134b)","readmeExcerpt":"Skill: Article Fetcher（文章抓取+Notion/Obsidian知识库存档） Owner: ajayhao Summary: 抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion） Tags: latest:1.3.6 Version history: v1.3.6 | 2026-08-08T13:16:14.670Z | user v1.3.6 — 安全修复（基于 ClawHub SkillSpector 审计） - P0(High): lxml 6.0.4→6.1.0 修复 CVE-2026-41066 XXE - P1(Medium): 校正 README/SKILL 误导性「零网络外发/完全本地」声明 - P2(Medium): 补强 LLM 文本外发声明（可选/来自配置/截断 12000 字符） - P","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"File v1.3.6:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.6\",\n  \"publishedAt\": 1786194974670\n}\n\nFile v1.3.6:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.6 (2026-08-08)\r\n\r\n### 🔒 安全修复（基于 ClawHub SkillSpector 审计）\r\n\r\n- **P0 — lxml XXE 漏洞（High）**：`requirements.txt` 中 `lxml==6.0.4` → `lxml==6.1.0`，修复 CVE-2026-41066（`iterparse` / `ETCompatXMLParser` 默认配置 XXE）。经核查代码未使用 lxml 解析不可信 XML（MHTML 走 `email` 库、HTML 走 `BeautifulSoup('html.parser')`），升级即消除风险面。\r\n- **P1 — 校正误导性「零网络外发」声明（Medium, Intent-Code Divergence）**：删除 README / SKILL 中「Obsidian 数据完全本地 / 零网络外发 / 不经过网络」等绝对化表述，统一为「Obsidian `.md` 本地落盘 + 文章图片上传阿里云 OSS（必需）」的准确描述。\r\n- **P2 — LLM 外发声明补强（Medium, External Transmission）**：在 `utils/tag_extractor.py` 的 LLM 调用处补充安全注释（可选触发 / endpoint 来自 `LLM_BASE_URL` 配置 / 仅外发前 12000 字符）；README 安全说明新增 LLM 文本外发条目，SKILL `securityNote` 同步声明。\r\n- **P3 — 删除「绕过安全拦截」笔记**：移除 `references/tirith-blocking.md`（审计报告指出的 bypass-prior-blocking 笔记），并确认无其它文件引用。\r\n\r\n## v1.3.5 (2026-08-08)\r\n\r\n### ✨ 离线输入（HTML / MHTML）\r\n\r\n- **新增 `fetchers/offline_parser.py`**：将 HTML 文本 / `.mhtml` 文件解析为标准 `article_data`，复用既有后半段管线（OSS 上传 → 替换 URL → 打标 → 字数 → Obsidian/Notion 归档）\r\n- **`main.py` 抽出 `process_and_archive(article_data, platform, url, tags)`**：URL 抓取与离线输入共用同一归档流程，行为零变化\r\n- **新增离线入口**：`archive_from_html()` / `archive_from_mhtml()`\r\n- **CLI 扩展（`sys.argv` 手动解析，不使用 argparse，遵循安全合规约定）**：`--html`（支持 `-` 读 stdin）/`--mhtml`/`--platform`（默认 wechat）/`--url`（可选）\r\n- **三陷阱修复（源自 wechat-article-capture 技能沉淀）**：\r\n  - 微信懒加载：`data-src`→`src` 并 `del data-src`，根治 markdownify 读占位符\r\n  - MHTML 编码：`get_payload(decode=True)` 取 bytes 后按 `get_content_charset()` 或 `utf-8` 解码，避免乱码\r\n  - `data:image/svg+xml` 占位符过滤，图片列表归一化（去 query、去重）确保 URL 替换可命中\r\n\r\n### 🔧 本地适配\r\n\r\n- **Vault 去序号**：存档目录从 `1-收件箱/` 改为 `收件箱/`（知衍库 2026-07 架构调整）\r\n- **移除 `distilled: false`**：蒸馏状态判定已改为文件存在性模型，不再依赖 frontmatter 标记\r\n- **裁撤 `wechat-article-capture` 技能**：v1.3.5 的 MHTML/HTML 离线模式已覆盖其全部功能，无需单独维护\r\n\r\n### 🐛 已知问题\r\n\r\n- ~~**Notion 转换**：`### | t"},{"language":"text","snippet":"File v1.3.5:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.5\",\n  \"publishedAt\": 1786192142791\n}\n\nFile v1.3.5:references/tirith-blocking.md\n\n# Tirith 安全扫描阻塞：ClawHub 安装限制\r\n\r\n## 问题\r\n\r\narticle-fetcher（Trust: community）通过 ClawHub 安装时被 Tirith 硬阻止："},{"language":"text","snippet":"File v1.3.4:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.4\",\n  \"publishedAt\": 1784425489723\n}\n\nFile v1.3.4:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.4 (2026-07-19)\r\n\r\n### 🔒 Tirith `requires.env` 补全\r\n\r\n- **`NOTION_API_KEY` / `LLM_API_KEY` 加入 `requires.env`**：代码实际读取的可选凭证必须在元数据声明，消除 CRITICAL exfiltration 误报\r\n\r\n---\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）"},{"language":"text","snippet":"File v1.3.3:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.3\",\n  \"publishedAt\": 1784424137231\n}\n\nFile v1.3.3:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS "},{"language":"text","snippet":"File v1.3.2:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.2\",\n  \"publishedAt\": 1784422688499\n}\n\nFile v1.3.2:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档"},{"language":"text","snippet":"File v1.3.1:_meta.json\n\n{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.1\",\n  \"publishedAt\": 1784421402614\n}\n\nFile v1.3.1:CHANGELOG.md\n\n# Changelog\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r\n### 🆕 微信 Playwright 回退 + 空内容检测\r\n\r\n- **微信二级回退**：`wechat_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 反爬时自动 Playwright 回退\r\n- **空内容检测**：`_is_valid()` 检测正文为空/反爬关键词，阻止垃圾文件生成\r\n- **图片 Referer 动态化**：`upload_images()` 新增 `article_url` 参数，从原文链接提取 Referer，兼容第三方 CDN\r\n\r\n### 🐛 修复\r\n\r\n- **文档文件名修正**：SKILL.md 改为 `{title}.md`，与实际输出一致\r\n\r\n---\r\n\r\n## v1.2.0 (2026-07-05)\r\n\r\n### 🆕 知乎 403 二级回退\r\n\r\n- **Playwright 浏览器回退**：`zhihu_fetcher.py` 新增 `_fetch_with_playwright()`，HTTP 403 时自动启动 headless Chromium 抓取\r\n- **Cookies 注入**：Netscape Cookies 自动注入到 Playwright 浏览器上下文\r\n- **二级策略**：HTTP (Cookies) → Playwright → 失败放弃（不生成垃圾文件）\r\n- **`_parse()` 提取**：HTTP 和 Playwright 两条路径复用同一解析逻辑\r\n- **Playwright 渲染等待**：`page.goto()` 后新增 `wait_for_timeout(3000)`，解决页面未渲染完就取内容报错\r\n- **Pin 类型支持**：新增 `_extract_pin()` 方法，支持知乎想法（`/pin/`）解析，含图片懒加载修复\r\n\r\n### 🐛 修复\r\n\r\n- **ObsidianArchiver 无参构造**：`__init__` 改为可选 `vault_path` 参数，兼容各种调用场景\r\n- **图片懒加载**：_extract_pin 中将 `data-original`/`data-actualsrc` 回填到 `src`，避免 OSS 替换时失配\r\n\r\n### 📝 文档\r\n\r\n- SKILL.md / README.md 新增知乎 403 回退策略说明\r\n- metadata 新增 `playwright install chromium` 安装步骤\r\n\r\n---\r\n\r\n## v1.1.0 (2026-06-22)\r\n\r\n### 🆕 Obsidian 本地存档\r\n\r\n- **新增 `obsidian_archiver.py`**：HTML → Markdown 转换（`markdownify`），YAML Frontmatter，存入 `1-输入-收件箱/文章收藏/`\r\n- **4 场景存档调度**：Obsidian 优先 / Notion 可选 / 双写 / 仅预览\r\n- **`config.py` 重构**：Notion 移出必需校验，新增 `obsidian_available` / `notion_available` / `archive_available` 属性\r\n- **文件名简化**：仅保留标题，去除日期前缀\r\n\r\n### 🐛 修复\r\n\r\n- **OSS URL 双协议头防御**：`image_pro"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\r\nname: article-fetcher\r\ndescription: \"抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图片，LLM 智能提取关键词，一键存档到 Obsidian 本地知识库（可选 Notion）\"\r\nhomepage: https://github.com/AjayHao/article-fetcher\r\nmetadata:\r\n  { \"hermes\": { \"emoji\": \"📰\", \"version\": \"1.3.6\", \"requires\": { \"bins\": [\"python3\"], \"env\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"NOTION_API_KEY\", \"LLM_API_KEY\"] }, \"primaryEnv\": \"OBSIDIAN_VAULT_PATH\", \"permissions\": [\"env:read\", \"net:outbound\", \"fs:write\"], \"allowedEnv\": [\"ALIYUN_OSS_AK\", \"ALIYUN_OSS_SK\", \"ALIYUN_OSS_BUCKET_ID\", \"ALIYUN_OSS_ENDPOINT\", \"OBSIDIAN_VAULT_PATH\", \"NOTION_API_KEY\", \"NOTION_ARTICLE_DATABASE_ID\", \"LLM_API_KEY\", \"LLM_BASE_URL\", \"LLM_MODEL\", \"WECHAT_COOKIES_FILE\", \"ZHIHU_COOKIES_FILE\"], \"securityNote\": \"OSS 凭证用于图片上传存储（图片外发至阿里云 OSS 为必需）；Notion 和 LLM 为可选集成，不配置则跳过对应功能；配置 LLM 时文章文本（前 12000 字符）会发送至你配置的 LLM API 端点\", \"install\": [{ \"id\": \"pip\", \"kind\": \"pip\", \"packages\": \"requests oss2 python-dotenv beautifulsoup4 lxml notion-client markdownify pyyaml\", \"label\": \"Install Python dependencies\" }, { \"id\": \"playwright\", \"kind\": \"shell\", \"command\": \"playwright install chromium\", \"label\": \"Install Playwright Chromium browser\" }] } }\r\n---\r\n\r\n# Article Fetcher v1.3.6\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎文章，自动上传 OSS 图床，LLM 智能关键词提取，默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n## 存档模式\r\n\r\n四种场景按需灵活切换，不存档时仅终端输出抓取结果：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 快速开始\r\n\r\n### 1. 安装依赖\r\n\r\n```bash\r\npip install -r requirements.txt\r\n```\r\n\r\n### 2. 配置环境变量\r\n\r\nskill 通过 `$AGENT_HOME/.env` 加载配置（自动兼容 Hermes / OpenClaw 等 agent）。设置方式：\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，只需设置一次：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n环境变量清单（写入 `$AGENT_HOME/.env` 或系统环境变量）：\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_ak\r\nALIYUN_OSS_SK=your_sk\r\nALIYUN_OSS_BUCKET_ID=your_bucket\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档 ==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=database_id\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=***\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬，Netscape 格式）==========\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\n```\r\n\r\n### 3. 使用\r\n\r\n```bash\r\ncd <skill-dir>\r\npython3 main.py \"文章 URL\" [标签1] [标签2]\r\n```\r\n\r\n**支持平台**：微信公众号 (`mp.weixin.qq.com`)、小红书 (`xiaohongshu.com` / `xhslink.com`)、豆瓣 (`douban.com`)、知乎 (`zhihu.com`)\r\n\r\n## 离线输入（HTML / MHTML）\r\n\r\n除 URL 外，支持直接喂入 HTML 文本或 `.mhtml` 文件（源自 wechat-article-capture "},{"path":"README.md","content":"# Article Fetcher — Hermes Skill\r\n\r\n抓取微信公众号、小红书、豆瓣、知乎等平台文章，自动处理图片上传至阿里云 OSS，\r\nLLM 智能提取关键词（本地词频降级），默认存档到 Obsidian 本地知识库（可选 Notion 双写）。\r\n\r\n**版本**: 1.3.6 | **许可**: MIT | **作者**: Ajay Hao\r\n\r\n---\r\n\r\n## 🎯 核心能力\r\n\r\n- **多平台支持**: 微信公众号、小红书、豆瓣、知乎\r\n- **智能识别**: 自动识别文章来源平台\r\n- **内容提取**: 标题、作者、发布时间、正文（HTML）、图片\r\n- **图床集成**: 阿里云 OSS 自动上传，按 `article-001.jpg` 格式命名\r\n- **智能关键词**: LLM 优先理解文章核心内容，本地词频分析降级兜底\r\n- **灵活存档**: 默认 Obsidian 本地知识库（推荐），可选 Notion 双写，都不配则仅终端输出\r\n\r\n## 🏗️ 存档模式\r\n\r\n四种场景按需灵活切换：\r\n\r\n| 场景 | Obsidian | Notion | 行为 |\r\n|------|:---:|:---:|------|\r\n| 🏠 **本地优先** | ✅ | ❌ | 存档到 Obsidian 本地知识库 |\r\n| ☁️ **纯云端** | ❌ | ✅ | 存档到 Notion 数据库 |\r\n| 🏠+☁️ **双写** | ✅ | ✅ | Obsidian + Notion 双存档 |\r\n| 🔍 **预览** | ❌ | ❌ | 仅终端输出，不存档 |\r\n\r\n## 🏗️ 处理流程\r\n\r\n```\r\nURL → 平台识别 (detector/) → 内容抓取 (fetchers/) → 图片上传 OSS (processors/)\r\n   → 关键词提取 (LLM 优先 → 词频降级) → 字数统计 → Obsidian / Notion 存档 (archiver/)\r\n```\r\n\r\n## 📂 模块结构\r\n\r\n```\r\narticle-fetcher/\r\n├── main.py                      # 主入口 + 抓取器注册表 + 存档调度\r\n├── config.py                    # 配置管理（环境变量）\r\n├── detector/\r\n│   └── platform_detector.py     # URL → 平台识别\r\n├── fetchers/\r\n│   ├── base_fetcher.py          # 基础抓取器（HTTP + Cookies）\r\n│   ├── wechat_fetcher.py        # 微信公众号\r\n│   ├── xhs_fetcher.py           # 小红书\r\n│   ├── douban_fetcher.py        # 豆瓣\r\n│   └── zhihu_fetcher.py         # 知乎\r\n├── processors/\r\n│   └── image_processor.py       # 图片上传 OSS\r\n├── archiver/\r\n│   ├── obsidian_archiver.py     # 🆕 HTML → Obsidian Markdown\r\n│   └── notion_archiver.py       # HTML → Notion 结构化块（可选）\r\n└── utils/\r\n    ├── http_client.py           # HTTP 客户端（Session 复用）\r\n    ├── logger.py                # 日志配置\r\n    ├── word_counter.py          # 字数统计\r\n    └── tag_extractor.py         # 关键词提取（LLM + 词频降级）\r\n```\r\n\r\n## ⚙️ 配置\r\n\r\n### 环境变量\r\n\r\nskill 读取 `$AGENT_HOME/.env`（通用，兼容 Hermes / OpenClaw 等任意 agent），回退 `$HERMES_HOME/.env`，最终回退当前目录 `.env`。\r\n\r\n```bash\r\n# 当前 agent 为 Hermes，设置一次即可：\r\nexport AGENT_HOME=$HERMES_HOME\r\n```\r\n\r\n```bash\r\n# ========== 必需：OSS 图床 ==========\r\nALIYUN_OSS_AK=your_access_key_id\r\nALIYUN_OSS_SK=your_access_key_secret\r\nALIYUN_OSS_BUCKET_ID=your_bucket_name\r\nALIYUN_OSS_ENDPOINT=oss-cn-shanghai.aliyuncs.com\r\n\r\n# ========== 推荐：Obsidian 本地存档 ==========\r\n# Windows 示例：\r\nOBSIDIAN_VAULT_PATH=D:\\GitRepo\\AjayObsidianVault\r\n# macOS 示例：\r\n# OBSIDIAN_VAULT_PATH=/Users/yourname/ObsidianVault\r\n# Linux / 云服务器示例：\r\n# OBSIDIAN_VAULT_PATH=/home/yourname/ObsidianVault\r\n\r\n# ========== 可选：Notion 云端存档（双写时配置）==========\r\nNOTION_API_KEY=secret_xxx\r\nNOTION_ARTICLE_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r\n\r\n# ========== 可选：LLM 关键词提取（OpenAI 兼容接口，与 video-summarizer 共用配置）==========\r\nLLM_API_KEY=sk-xxx\r\nLLM_BASE_URL=https://api.deepseek.com\r\nLLM_MODEL=deepseek-v4-pro\r\n\r\n# ========== 可选：Cookies（反爬）==========\r\nZHIHU_COOKIES_FILE=~/.cookies/zhihu_cookies.txt\r\nWECHAT_COOKIES_FILE=~/.cookies/wechat_cookies.txt\r\n```\r\n\r\n### Obsidian 存档格式\r\n\r\n文章存入 `{OBSIDIAN_VAULT_PATH}/1-输入-收件箱/文章收藏/`。\r\n\r\n- **文件命名**: `{YYYY-MM-DD}_{platform}_{title}.md`\r\n- **Frontmatter**: YAML 格式，包含 `title` / `sourc"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn73qb7xbge03wr1vsyg56snts837gqq\",\n  \"slug\": \"article-fetcher\",\n  \"version\": \"1.3.6\",\n  \"publishedAt\": 1786194974670\n}"},{"path":"CHANGELOG.md","content":"# Changelog\r\n\r\n## v1.3.6 (2026-08-08)\r\n\r\n### 🔒 安全修复（基于 ClawHub SkillSpector 审计）\r\n\r\n- **P0 — lxml XXE 漏洞（High）**：`requirements.txt` 中 `lxml==6.0.4` → `lxml==6.1.0`，修复 CVE-2026-41066（`iterparse` / `ETCompatXMLParser` 默认配置 XXE）。经核查代码未使用 lxml 解析不可信 XML（MHTML 走 `email` 库、HTML 走 `BeautifulSoup('html.parser')`），升级即消除风险面。\r\n- **P1 — 校正误导性「零网络外发」声明（Medium, Intent-Code Divergence）**：删除 README / SKILL 中「Obsidian 数据完全本地 / 零网络外发 / 不经过网络」等绝对化表述，统一为「Obsidian `.md` 本地落盘 + 文章图片上传阿里云 OSS（必需）」的准确描述。\r\n- **P2 — LLM 外发声明补强（Medium, External Transmission）**：在 `utils/tag_extractor.py` 的 LLM 调用处补充安全注释（可选触发 / endpoint 来自 `LLM_BASE_URL` 配置 / 仅外发前 12000 字符）；README 安全说明新增 LLM 文本外发条目，SKILL `securityNote` 同步声明。\r\n- **P3 — 删除「绕过安全拦截」笔记**：移除 `references/tirith-blocking.md`（审计报告指出的 bypass-prior-blocking 笔记），并确认无其它文件引用。\r\n\r\n## v1.3.5 (2026-08-08)\r\n\r\n### ✨ 离线输入（HTML / MHTML）\r\n\r\n- **新增 `fetchers/offline_parser.py`**：将 HTML 文本 / `.mhtml` 文件解析为标准 `article_data`，复用既有后半段管线（OSS 上传 → 替换 URL → 打标 → 字数 → Obsidian/Notion 归档）\r\n- **`main.py` 抽出 `process_and_archive(article_data, platform, url, tags)`**：URL 抓取与离线输入共用同一归档流程，行为零变化\r\n- **新增离线入口**：`archive_from_html()` / `archive_from_mhtml()`\r\n- **CLI 扩展（`sys.argv` 手动解析，不使用 argparse，遵循安全合规约定）**：`--html`（支持 `-` 读 stdin）/`--mhtml`/`--platform`（默认 wechat）/`--url`（可选）\r\n- **三陷阱修复（源自 wechat-article-capture 技能沉淀）**：\r\n  - 微信懒加载：`data-src`→`src` 并 `del data-src`，根治 markdownify 读占位符\r\n  - MHTML 编码：`get_payload(decode=True)` 取 bytes 后按 `get_content_charset()` 或 `utf-8` 解码，避免乱码\r\n  - `data:image/svg+xml` 占位符过滤，图片列表归一化（去 query、去重）确保 URL 替换可命中\r\n\r\n### 🔧 本地适配\r\n\r\n- **Vault 去序号**：存档目录从 `1-收件箱/` 改为 `收件箱/`（知衍库 2026-07 架构调整）\r\n- **移除 `distilled: false`**：蒸馏状态判定已改为文件存在性模型，不再依赖 frontmatter 标记\r\n- **裁撤 `wechat-article-capture` 技能**：v1.3.5 的 MHTML/HTML 离线模式已覆盖其全部功能，无需单独维护\r\n\r\n### 🐛 已知问题\r\n\r\n- ~~**Notion 转换**：`### | title` 格式标题会被 Notion API 误判为表格分隔符~~ **已修复 (v1.3.5-local)**：根因为 `_parse_inline_html` 将内联 `<img>` 当作 image block 塞入 `rich_text`，导致 `text` 字段缺失。修复为内联图片转文本链接 `[alt](url)`。\r\n\r\n---\r\n\r\n## v1.3.4 (2026-07-19)\r\n\r\n### 🔒 Tirith `requires.env` 补全\r\n\r\n- **`NOTION_API_KEY` / `LLM_API_KEY` 加入 `requires.env`**：代码实际读取的可选凭证必须在元数据声明，消除 CRITICAL exfiltration 误报\r\n\r\n---\r\n\r\n## v1.3.3 (2026-07-06)\r\n\r\n### 🔒 Tirith 安全扫描兼容\r\n\r\n- **元数据 `allowedEnv` 声明**：显式列出所有读取的环境变量及用途，避免 `NOTION_API_KEY`/`LLM_API_KEY` 被误判为外泄\r\n- **元数据 `securityNote`**：说明凭证用途，OSS 必需，Notion/LLM 可选\r\n- **移除 `pip install` 文本**：代码日志和文档中的 `pip install` 语句替换为 `requirements.txt` 引用，消除供应链误报\r\n\r\n---\r\n\r\n## v1.3.2 (2026-07-06)\r\n\r\n### 🐛 修复\r\n\r\n- **文件名补日期前缀**：`_build_filename` 恢复 `{YYYY-MM-DD}_{title}.md` 格式，Obsidian 内按时间线排序\r\n- **图片 Referer 空优先 + 403 回退**：新增 `_download_image()`，先空 Referer 请求，403 时自动回退平台 Referer\r\n\r\n---\r\n\r\n## v1.3.1 (2026-07-06)\r\n\r\n### 🔒 安全审计修复\r\n\r\n- **依赖升级**：`lxml` 6.0.2→6.0.4 (CVE-2026-41066 XXE)、`urllib3` 2.0.7→2.7.0 (多项 CVE)\r\n- **权限声明**：SKILL.md metadata 新增 `permissions: [env:read, net:outbound, fs:write]`\r\n- **隐私声明修正**：移除\"数据完全本地\"误导性表述，明确 LLM 外发范围和图片下载行为\r\n- **安全章节补充**：新增 LLM 外发范围说明、图片下载风险提示\r\n\r\n---\r\n\r\n## v1.3.0 (2026-07-06)\r\n\r"},{"path":"skill-card.md","content":"## Description:\n\nFetches articles from WeChat, Xiaohongshu, Douban, and Zhihu, uploads article images to Aliyun OSS, extracts keywords with optional LLM support, and archives the result to Obsidian Markdown with optional Notion sync.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[ajayhao](https://clawhub.ai/user/ajayhao)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and knowledge workers use this skill to turn supported social and article URLs, HTML, or MHTML captures into archived knowledge-base entries. It is suited for workflows that need Markdown notes, optional Notion records, OSS-hosted images, and generated tags.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Untrusted HTML or MHTML can contain image URLs that the runtime fetches and uploads to OSS before image URL validation is added.\n\nMitigation: Process only trusted article captures and URLs, and avoid untrusted HTML/MHTML inputs until image URL validation is available.\n\nRisk: Article images are uploaded to a configured Aliyun OSS bucket, so image content leaves the local environment.\n\nMitigation: Use a dedicated least-privilege OSS bucket and credentials limited to the needed object operations.\n\nRisk: When LLM keyword extraction is configured, article text is sent to the configured LLM endpoint and the endpoint is not forced to HTTPS by the skill.\n\nMitigation: Configure LLM_BASE_URL with HTTPS only, or leave LLM settings unset to use local keyword extraction.\n\nRisk: The release has a suspicious security verdict and should be reviewed before deployment.\n\nMitigation: Prefer the pinned requirements.txt installation path and review the configured cloud accounts and input sources before execution.\n\n## Reference(s):\n\n- [ClawHub Skill Page](https://clawhub.ai/ajayhao/skills/article-fetcher)\n- [Project Homepage](https://github.com/AjayHao/article-fetcher)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration]\n\n**Output Format:** [Markdown files with YAML frontmatter, optional Notion pages, terminal status text, and Python dictionary results when used as a module]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires environment configuration for Aliyun OSS; Obsidian, Notion, cookies, and LLM keyword extraction are optional configuration-dependent outputs.]\n\n## Skill Version(s):\n\n1.3.6 (source: server release evidence, metadata, and changelog)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1455,"uniquenessScore":41,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T06:35:15.362Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T06:35:15.362Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T10:43:38.567Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}