Skill: onlyloveher/wechat-tool-v2 (version 1.0.0)

--- name: wechat-articles description: > 搜索和读取微信公众号文章的完整工具，支持关键词搜索与全文提取。 **只要用户提到以下任何场景，必须使用此 skill：** (1) 搜索公众号文章、按关键词查找微信内容 (2) 读取、摘要、分析、翻译 mp.weixin.qq.com 链接的内容 (3) 用户说"帮我找公众号文章"、"读这篇微信文章"、"搜索公众号"、"抓取公众号" (4) 用户粘贴了微信文章 URL 并要求提取或处理内容 (5) 需要从微信生态获取资讯、报告、行业信息 即使用户没有明确说"微信"，只要涉及公众号内容获取或提取，也应触发此 skill。 Triggers: 搜索公众号, 微信文章, 读这篇公众号, 抓取公众号, wechat article, mp.weixin.qq.com, 公众号内容, 微信链接 version: 1.0.0 homepage: https://github.com/johan-oilman/wechat-articles author: 油太人 / @johan-oilman license: MIT metadata: clawdbot: emoji: "📄" requires: packages: - beautifulsoup4 - requests - playwright - miku-ai binaries: - chromium install: - pip install beautifulsoup4 requests playwright miku-ai - playwright install chromium --with-deps --- # 微信公众号文章搜索与读取 (v1.0) 搜索和读取微信公众号文章的完整工具，支持 **simple**（快速）和 **playwright**（稳定）双模式 + **auto** 自动切换。 ## 快速开始 ### 搜索文章 ```bash python3 scripts/search.py "关键词" [数量] ``` 示例： ```bash python3 scripts/search.py "绿电直连政策" 10 ``` ### 读取文章 ```bash python3 scripts/read.py "微信文章URL" [--mode MODE] [--screenshot PATH] ``` 模式选择： - `--mode=simple` - 快速模式（requests + BeautifulSoup） - `--mode=playwright` - 稳定模式（真实浏览器） - `--mode=auto` - 自动切换（默认，推荐） ## Python API 使用（推荐） ```python import sys sys.path.append('scripts') from wechat_articles import search_articles, read_article # 搜索文章 articles = search_articles("绿电直连政策", top_num=5) # 读取文章（建议加错误处理） try: content = read_article(articles[0]['url'], mode='auto') print(f"标题: {content['title']}") print(f"公众号: {content['author']}") print(f"发布时间: {content['publish_time']}") # 若有 print(f"读取模式: {content['mode']}") for p in content['paragraphs'][:10]: print(p) except Exception as e: print(f"读取失败: {e}") # auto 模式失败时会抛出异常，建议捕获后降级处理或提示用户 ``` ### 返回数据结构说明 `read_article()` 返回一个字典，包含以下字段： | 字段 | 类型 | 说明 | |------|------|------| | `title` | str | 文章标题 | | `author` | str | 公众号名称 | | `publish_time` | str | 发布时间（部分文章可能为空） | | `paragraphs` | list[str] | 正文段落列表 | | `mode` | str | 实际使用的读取模式（`simple` 或 `playwright`） | `search_articles()` 返回列表，每项包含： | 字段 | 类型 | 说明 | |------|------|------| | `title` | str | 文章标题 | | `url` | str | 文章链接（有时效性，建议尽快读取） | | `author` | str | 公众号名称 | | `digest` | str | 文章摘要 | ## 模式对比 | 模式 | 速度 | 资源 | 稳定性 | 适用场景 | |------------|------------|------|--------|----------------------| | simple | 快 (0.5-1s) | 轻量 | 一般 | 简单页面，频繁调用 | | playwright | 慢 (3-5s) | 较重 | 很高 | 复杂页面，稳定优先 | | auto | 自适应 | 自适应 | 最佳 | 默认推荐 | ## 安装依赖 ### Simple 模式（默认，轻量快速） ```bash pip install beautifulsoup4 requests miku-ai ``` 这些包通常已随 agent-reach 安装。 ### Playwright 模式（可选，推荐稳定读取） ```bash pip install playwright playwright install chromium --with-deps ``` `--with-deps` 会自动安装 Linux 系统依赖（如 libnss3、libgbm 等），首次运行需几分钟。 ## 注意事项 - 搜索结果 URL 有时效性，建议尽快读取 - 避免高频请求防止触发反爬 - auto 模式优先尝试 simple，失败后自动切换 playwright；若两者均失败则抛出异常 - Playwright 首次运行需安装 Chromium（约几分钟） 欢迎反馈 & PR！GitHub: https://github.com/johan-oilman/wechat-articles

path: _meta.json
content: ownerId: kn7f7k6rh31wfyvbs58wt0me0n82btxs
slug: wechat-articles
version: 1.0.1
publishedAt: 1772700025961

path: scripts/read.py
content: 
#!/usr/bin/env python3
"""
读取微信公众号文章
用法: python read.py "微信文章URL" [--mode MODE] [--screenshot PATH]
模式: simple|playwright|auto (默认auto)
"""

import sys
import os
import argparse

# 添加脚本路径
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)

from wechat_articles import read_article, print_article_content


def main():
    parser = argparse.ArgumentParser(description="读取微信公众号文章")
    parser.add_argument("url", help="微信文章URL")
    parser.add_argument("--mode", "-m", default="auto", 
                        choices=["simple", "playwright", "auto"],
                        help="读取模式 (默认: auto)")
    parser.add_argument("--screenshot", "-s", help="Playwright模式下截图保存路径")
    parser.add_argument("--max-paragraphs", "-n", type=int, default=50,
                        help="显示的最大段落数 (默认: 50)")
    
    args = parser.parse_args()
    
    print(f"正在读取文章...")
    print(f"URL: {args.url[:80]}...")
    print(f"模式: {args.mode}")
    if args.screenshot:
        print(f"截图: {args.screenshot}")
    print()
    
    try:
        content = read_article(args.url, mode=args.mode, screenshot_path=args.screenshot)
        print_article_content(content, max_paragraphs=args.max_paragraphs)
    except Exception as e:
        print(f"错误: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/search.py
content: 
#!/usr/bin/env python3
"""
搜索微信公众号文章
用法: python search.py "关键词" [数量]
"""

import sys
import os

# 添加脚本路径
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)

from wechat_articles import search_articles, print_article_summary


def main():
    if len(sys.argv) < 2:
        print("用法: python search.py \"关键词\" [数量]")
        print("示例: python search.py \"绿电直连政策\" 10")
        sys.exit(1)
    
    query = sys.argv[1]
    top_num = int(sys.argv[2]) if len(sys.argv) > 2 else 5
    
    print(f"正在搜索: {query}")
    print("=" * 60)
    
    articles = search_articles(query, top_num)
    
    if not articles:
        print("未找到相关文章")
        return
    
    print(f"找到 {len(articles)} 篇文章:\n")
    
    for i, article in enumerate(articles, 1):
        print(f"【{i}】")
        print_article_summary(article)


if __name__ == "__main__":
    main()


path: scripts/search_and_read.py
content: 
#!/usr/bin/env python3
"""
搜索并读取微信公众号文章
用法: python search_and_read.py "关键词" [数量] [--mode MODE]
模式: simple|playwright|auto (默认auto)
"""

import sys
import os
import argparse

# 添加脚本路径
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)

from wechat_articles import search_articles, read_article, print_article_summary, print_article_content


def main():
    parser = argparse.ArgumentParser(description="搜索并读取微信公众号文章")
    parser.add_argument("query", help="搜索关键词")
    parser.add_argument("num", nargs="?", type=int, default=3, 
                        help="返回文章数量 (默认: 3)")
    parser.add_argument("--mode", "-m", default="auto",
                        choices=["simple", "playwright", "auto"],
                        help="读取模式 (默认: auto)")
    parser.add_argument("--max-paragraphs", "-n", type=int, default=30,
                        help="每篇文章显示的最大段落数 (默认: 30)")
    parser.add_argument("--no-read", action="store_true",
                        help="只搜索不读取文章内容")
    
    args = parser.parse_args()
    
    print(f"正在搜索: {args.query}")
    print("=" * 80)
    
    articles = search_articles(args.query, args.num)
    
    if not articles:
        print("未找到相关文章")
        return
    
    print(f"找到 {len(articles)} 篇文章")
    
    if args.no_read:
        print("\n文章列表:")
        print("=" * 80)
        for i, article in enumerate(articles, 1):
            print(f"\n【{i}】")
            print_article_summary(article)
        return
    
    print(f"开始读取内容 (模式: {args.mode})...\n")
    
    for i, article in enumerate(articles, 1):
        print(f"\n{'='*80}")
        print(f"【第 {i} 篇】")
        print(f"{'='*80}")
        print_article_summary(article)
        
        try:
            print(f"\n正在读取全文...")
            content = read_article(article['url'], mode=args.mode)
            print_article_content(content, max_paragraphs=args.max_paragraphs)
        except Exception as e:
            print(f"读取失败: {e}")
            import traceback
            traceback.print_exc()
        
        print(f"\n{'='*80}\n")


if __name__ == "__main__":
    main()


path: scripts/wechat_articles.py
content: 
"""
微信公众号文章搜索与读取模块
提供搜索和读取微信公众号文章的功能，支持简单模式和Playwright模式
"""

import asyncio
import miku_ai.spider
import requests
from bs4 import BeautifulSoup


def search_articles(query, top_num=5):
    """
    搜索微信公众号文章
    
    Args:
        query (str): 搜索关键词
        top_num (int): 返回结果数量，默认5篇
    
    Returns:
        list: 文章列表，每篇包含 title, snippet, url, source, date
    """
    articles = asyncio.run(miku_ai.spider.get_wexin_article(query, top_num))
    return articles


def read_article_simple(url):
    """
    使用简单模式读取微信公众号文章内容（requests + BeautifulSoup）
    
    Args:
        url (str): 微信文章URL
    
    Returns:
        dict: 包含 title, author, paragraphs 的字典
    """
    # 简化URL
    simple_url = url.split("&new=1")[0] if "&new=1" in url else url
    
    headers = {
        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "zh-CN,zh;q=0.9"
    }
    
    response = requests.get(simple_url, headers=headers, timeout=30, allow_redirects=True)
    
    if response.status_code != 200:
        raise Exception(f"请求失败，状态码: {response.status_code}")
    
    # 解析HTML
    soup = BeautifulSoup(response.content, 'html.parser')
    
    # 提取标题
    title_elem = soup.find('h1', {'class': 'rich_media_title'})
    title = title_elem.get_text().strip() if title_elem else "N/A"
    
    # 提取公众号
    author_elem = soup.find('a', {'id': 'js_name'})
    author = author_elem.get_text().strip() if author_elem else "N/A"
    
    # 提取正文
    content_div = soup.find('div', {'id': 'js_content'})
    if not content_div:
        raise Exception("未找到正文内容")
    
    # 移除脚本和样式
    for script in content_div.find_all(['script', 'style']):
        script.decompose()
    
    # 提取段落
    paragraphs = content_div.find_all(['p', 'h1', 'h2', 'h3', 'h4', 'li'])
    valid_paragraphs = []
    
    for p in paragraphs:
        text = p.get_text().strip()
        if len(text) > 5:
            valid_paragraphs.append(text)
    
    return {
        "title": title,
        "author": author,
        "paragraphs": valid_paragraphs,
        "mode": "simple"
    }


def read_article(url, mode="auto", screenshot_path=None):
    """
    读取微信公众号文章内容（支持多种模式）
    
    Args:
        url (str): 微信文章URL
        mode (str): 读取模式 - "simple"| "playwright"| "auto"
        screenshot_path (str, optional): Playwright模式下的截图保存路径
    
    Returns:
        dict: 包含 title, author, paragraphs 的字典
    """
    if mode == "simple":
        return read_article_simple(url)
    
    elif mode == "playwright":
        from wechat_articles_playwright import read_article_playwright_sync
        return read_article_playwright_sync(url, screenshot_path)
    
    elif mode == "auto":
        # 自动模式：先试简单模式，失败再试Playwright
        try:
            return read_article_simple(url)
        except Exception as e:
            print(f"简单模式失败: {e}，尝试Playwright模式...")
            from wechat_articles_playwright import read_article_playwright_sync
            return read_article_playwright_sync(url, screenshot_path)
    
    else:
        raise ValueError(f"未知模式: {mode}，可选值: simple, playwright, auto")


def print_article_summary(article):
    """
    打印文章摘要（搜索结果）
    """
    print(f"标题: {article['title']}")
    print(f"来源: {article['source']}")
    print(f"日期: {article['date']}")
    print(f"链接: {article['url']}")
    if article.get('snippet'):
        print(f"摘要: {article['snippet'][:100]}...")
    print("-" * 60)


def print_article_content(content, max_paragraphs=50):
    """
    打印文章内容
    """
    print("=" * 80)
    print(f"标题: {content['title']}")
    print(f"公众号: {content['author']}")
    print(f"模式: {content.get('mode', 'unknown')}")
    print("=" * 80)
    print()
    
    for i, p in enumerate(content['paragraphs'][:max_paragraphs]):
        print(p)
        print()
    
    if len(content['paragraphs']) > max_paragraphs:
        print(f"... (还有 {len(content['paragraphs']) - max_paragraphs} 段)")


path: scripts/wechat_articles_playwright.py
content: 
"""
微信公众号文章搜索与读取模块 - Playwright版本
提供更稳定的文章读取功能，使用真实浏览器
"""

import asyncio
from playwright.async_api import async_playwright
from bs4 import BeautifulSoup


async def read_article_playwright(url, screenshot_path=None, headless=True):
    """
    使用Playwright读取微信公众号文章内容
    
    Args:
        url (str): 微信文章URL
        screenshot_path (str, optional): 截图保存路径
        headless (bool): 是否无头模式，默认True
    
    Returns:
        dict: 包含 title, author, paragraphs 的字典
    """
    # 简化URL
    simple_url = url.split("&new=1")[0] if "&new=1" in url else url
    
    iphone_ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
    
    async with async_playwright() as p:
        # 启动浏览器
        browser = await p.chromium.launch(headless=headless)
        page = await browser.new_page(user_agent=iphone_ua)
        
        try:
            # 访问页面
            await page.goto(simple_url, wait_until="networkidle", timeout=60000)
            
            # 保存截图（如果需要）
            if screenshot_path:
                await page.screenshot(path=screenshot_path, full_page=True)
            
            # 获取页面内容
            content = await page.content()
            
            # 解析
            soup = BeautifulSoup(content, 'html.parser')
            
            # 提取标题
            title_elem = soup.find('h1', {'class': 'rich_media_title'})
            title = title_elem.get_text().strip() if title_elem else "N/A"
            
            # 提取公众号
            author_elem = soup.find('a', {'id': 'js_name'})
            author = author_elem.get_text().strip() if author_elem else "N/A"
            
            # 提取正文
            content_div = soup.find('div', {'id': 'js_content'})
            if not content_div:
                raise Exception("未找到正文内容")
            
            # 移除脚本和样式
            for script in content_div.find_all(['script', 'style']):
                script.decompose()
            
            # 提取段落
            paragraphs = content_div.find_all(['p', 'h1', 'h2', 'h3', 'h4', 'li'])
            valid_paragraphs = []
            
            for p in paragraphs:
                text = p.get_text().strip()
                if len(text) > 5:
                    valid_paragraphs.append(text)
            
            return {
                "title": title,
                "author": author,
                "paragraphs": valid_paragraphs,
                "mode": "playwright"
            }
            
        finally:
            await browser.close()


def read_article_playwright_sync(url, screenshot_path=None, headless=True):
    """
    同步版本的Playwright读取函数
    """
    return asyncio.run(read_article_playwright(url, screenshot_path, headless))


def print_article_content(content, max_paragraphs=50):
    """
    打印文章内容
    """
    print("=" * 80)
    print(f"标题: {content['title']}")
    print(f"公众号: {content['author']}")
    print(f"模式: {content.get('mode', 'unknown')}")
    print("=" * 80)
    print()
    
    for i, p in enumerate(content['paragraphs'][:max_paragraphs]):
        print(p)
        print()
    
    if len(content['paragraphs']) > max_paragraphs:
        print(f"... (还有 {len(content['paragraphs']) - max_paragraphs} 段)")


path: skill.yaml
content: name: wechat-articles
version: 1.0.0
description: 微信公众号文章搜索与读取工具。支持关键词搜索、全文提取、内容分析。
author: clawd
homepage: https://clawhub.com/skills/wechat-articles

triggers:
  - 微信文章
  - 公众号文章
  - 搜索公众号
  - 微信搜索
  - 公众号搜索
  - wechat article
  - mp.weixin

features:
  - 关键词搜索公众号文章
  - 全文提取和摘要
  - 内容分析
  - 支持mp.weixin.qq.com链接读取

compatibility:
  platforms:
    - openclaw
    - clawdbot


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)