微信文章收藏——Week1 脚本设计文档

目标:一个命令,URL 进,Markdown 出,存入 Obsidian vault。


使用方式(目标体验)

# 从微信复制文章链接后,终端执行:
python wechat_save.py "https://mp.weixin.qq.com/s/xxxx"

# 输出:
# ✅ 抓取成功:《为什么说微信是知识黑洞》
# ✅ AI 摘要完成(3句话 + 5个标签)
# ✅ 已保存至:~/0PostNote/shine-box/wiki/raw/微信/为什么说微信是知识黑洞.md

文件结构

wechat_save.py          ← 主脚本
config.py               ← 配置(vault路径、Claude API Key)
requirements.txt        ← 依赖包
output/                 ← 暂存(正式落入 vault 前检查用)

输出 Markdown 格式

---
title: 为什么说微信是知识黑洞
source: https://mp.weixin.qq.com/s/xxxx
author: 公众号名称
date_saved: 2026-08-20
date_published: 2026-08-18
tags:
  - 微信
  - 知识管理
  - 信息焦虑
  - 产品设计
  - 内容平台
category: 科技/产品
status: unread
---

# 为什么说微信是知识黑洞

> **AI 摘要:** 微信公众号虽然是中国最大的内容平台,但其收藏功能极为简陋,缺乏搜索和分类能力。大量用户每天阅读但无法沉淀知识,形成"读了即忘"的恶性循环。作者认为知识管理工具与内容消费平台的本质矛盾是根本原因,并提出了解决方案。

> **关键词:** `微信` `知识管理` `信息焦虑` `产品设计` `内容平台`

---

[原文内容]
...

脚本代码结构

# wechat_save.py

import requests
import re
import json
from bs4 import BeautifulSoup
from pathlib import Path
from datetime import datetime
import anthropic  # Claude API

# ── 配置 ──────────────────────────────────────────
VAULT_PATH = Path("~/0PostNote/shine-box/wiki/raw/微信").expanduser()
CLAUDE_API_KEY = "your_key_here"

# ── Step 1:抓取文章 ───────────────────────────────
def fetch_article(url: str) -> dict:
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"
    }
    resp = requests.get(url, headers=headers, timeout=10)
    resp.raise_for_status()
    soup = BeautifulSoup(resp.text, "html.parser")

    title   = soup.find("h1", class_="rich_media_title")
    author  = soup.find("span", class_="rich_media_meta_nickname")
    content = soup.find("div", id="js_content")
    pub_time = soup.find("em", id="publish_time")

    return {
        "title":    title.text.strip()   if title   else "无标题",
        "author":   author.text.strip()  if author  else "未知",
        "content":  content.get_text("\n", strip=True) if content else "",
        "pub_time": pub_time.text.strip() if pub_time else "",
        "url":      url,
    }

# ── Step 2:Claude AI 摘要 ─────────────────────────
def ai_summarize(article: dict) -> dict:
    client = anthropic.Anthropic(api_key=CLAUDE_API_KEY)

    prompt = build_summarize_prompt(article["title"], article["content"])

    message = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}]
    )

    raw = message.content[0].text
    return parse_ai_response(raw)

# ── Step 3:写入 Markdown ─────────────────────────
def save_to_vault(article: dict, ai: dict):
    VAULT_PATH.mkdir(parents=True, exist_ok=True)

    safe_title = re.sub(r'[\\/:*?"<>|]', '', article["title"])[:50]
    filename = f"{datetime.now().strftime('%Y%m%d')}-{safe_title}.md"
    filepath = VAULT_PATH / filename

    content = build_markdown(article, ai)
    filepath.write_text(content, encoding="utf-8")

    print(f"✅ 已保存至:{filepath}")
    return filepath

# ── 入口 ─────────────────────────────────────────
if __name__ == "__main__":
    import sys
    url = sys.argv[1]

    print(f"⏳ 抓取中:{url}")
    article = fetch_article(url)
    print(f"✅ 抓取成功:《{article['title']}》")

    print("⏳ AI 摘要中...")
    ai = ai_summarize(article)
    print(f"✅ 摘要完成:{ai['summary'][:50]}...")

    save_to_vault(article, ai)

核心:Claude Prompt 设计

System Prompt

你是一个专业的知识管理助手,负责处理微信公众号文章。
你的任务是:提取关键信息,生成结构化摘要,方便用户日后回顾。
输出必须是合法的 JSON,不要包含任何额外文字。

User Prompt 模板

def build_summarize_prompt(title: str, content: str) -> str:
    # 内容过长时截断,避免超出 token 限制
    truncated = content[:3000] if len(content) > 3000 else content

    return f"""
请分析以下微信公众号文章,返回 JSON 格式的结构化信息。

文章标题:{title}

文章内容:
{truncated}

请返回如下 JSON(严格格式,不要有多余文字):
{{
  "summary": "用3句话概括文章核心观点,每句不超过30字",
  "tags": ["标签1", "标签2", "标签3", "标签4", "标签5"],
  "category": "从以下选择最合适的一个:投资/科技/产品/个人成长/生活/育儿/历史/其他",
  "key_points": [
    "核心要点1(一句话)",
    "核心要点2(一句话)",
    "核心要点3(一句话)"
  ],
  "worth_keeping": true,
  "reason": "一句话说明为什么值得保存(或不值得)"
}}
"""

AI 响应解析

def parse_ai_response(raw: str) -> dict:
    try:
        # 提取 JSON(防止 Claude 返回多余文字)
        match = re.search(r'\{.*\}', raw, re.DOTALL)
        if match:
            return json.loads(match.group())
    except Exception:
        pass

    # 降级:返回空结构
    return {
        "summary": "(AI 摘要失败,请手动填写)",
        "tags": [],
        "category": "其他",
        "key_points": [],
        "worth_keeping": True,
        "reason": ""
    }

最终 Markdown 生成

def build_markdown(article: dict, ai: dict) -> str:
    tags_yaml = "\n".join(f"  - {t}" for t in ai.get("tags", []))
    key_points = "\n".join(f"- {p}" for p in ai.get("key_points", []))

    return f"""---
title: {article['title']}
source: {article['url']}
author: {article['author']}
date_saved: {datetime.now().strftime('%Y-%m-%d')}
date_published: {article['pub_time']}
tags:
{tags_yaml}
category: {ai.get('category', '其他')}
worth_keeping: {str(ai.get('worth_keeping', True)).lower()}
status: unread
---

# {article['title']}

> **AI 摘要:** {ai.get('summary', '')}

**关键要点:**
{key_points}

**来源:** [{article['author']}]({article['url']})

---

{article['content']}
"""

依赖安装

pip install requests beautifulsoup4 anthropic

已知边界情况

情况 处理方式
需关注才能查看的文章 报错提示"文章需要关注公众号"
文章已删除 requests 返回 200 但内容为空,检测后提示
内容过长(>10000字) 截取前 3000 字给 AI,全文仍保存
标题含特殊字符 正则清洗后作为文件名
网络超时 timeout=10,失败后提示重试

Week1 验收标准


See Also

Updated: 2026-08-20