Skill: winsaney/daily-news-collector (version 1.0.0)

--- name: daily-news-collector description: 每天从多个媒体渠道（RSS源、网站、API）自动收集最新资讯，智能分析并生成结构化日报。适用于需要定期监控行业动态、新闻资讯、竞品信息等场景。 dependency: python: - requests==2.31.0 - feedparser==6.0.10 - beautifulsoup4==4.12.2 - lxml==4.9.3 --- # 每日资讯收集助手 ## 任务目标 - 本 Skill 用于：每天自动从多个媒体渠道收集最新资讯并生成结构化日报 - 能力包含： - 支持RSS订阅源、网页抓取等多种数据源 - 自动解析和格式化原始数据 - 智能筛选重要资讯并分类整理 - 生成可读性强的Markdown格式日报 - 触发条件：用户需要"收集资讯"、"生成日报"、"监控媒体动态"等场景 ## 前置准备 - 依赖说明： ``` requests==2.31.0 feedparser==6.0.10 beautifulsoup4==4.12.2 lxml==4.9.3 ``` - 配置媒体源：根据 [references/sources.md](references/sources.md) 创建 `sources.json` 配置文件 ## 操作步骤 ### 标准流程 1. **收集原始数据** - 从RSS源收集：调用 `scripts/collect_feeds.py` 读取配置并获取文章列表 ```bash python /workspace/projects/daily-news-collector/scripts/collect_feeds.py --config ./sources.json --output ./raw_data.json ``` - 从网页抓取：调用 `scripts/collect_webpages.py` 获取网页内容 ```bash python /workspace/projects/daily-news-collector/scripts/collect_webpages.py --url <URL> --output ./webpage_data.json ``` - 合并数据到统一的JSON文件供后续分析 2. **分析筛选资讯** - 智能体读取收集到的原始数据 - 根据用户需求筛选重要资讯（如：科技类、商业类、特定关键词） - 按主题自动分类（科技、商业、社会、政策等） - 为每条资讯生成简明摘要（1-2句话） 3. **生成日报** - 参照 [assets/template.md](assets/template.md) 的格式 - 按分类组织内容，包含标题、链接、摘要、来源 - 添加日期、统计数据（总条数、分类分布） - 输出为Markdown格式的日报文件 ### 可选分支 - 当只需要RSS数据：仅执行 `collect_feeds.py` - 当需要增量更新：检查历史数据，只收集新发布的内容 - 当需要定制分类：在分析阶段根据用户指定的分类规则 ## 资源索引 - 核心脚本： - [scripts/collect_feeds.py](scripts/collect_feeds.py) (解析RSS订阅源，获取文章列表) - [scripts/collect_webpages.py](scripts/collect_webpages.py) (抓取网页内容，提取正文) - 配置参考： - [references/sources.md](references/sources.md) (媒体源配置指南和示例) - [references/format.md](references/format.md) (日报格式规范) - 输出模板： - [assets/template.md](assets/template.md) (日报结构模板) ## 注意事项 - 脚本负责数据获取和格式化，智能体负责内容分析和总结 - 定期更新媒体源配置以保持数据新鲜度 - 遵守目标网站的robots.txt规则，合理设置抓取频率 - 生成的日报保存在当前工作目录（`.`），便于用户访问 ## 使用示例 ### 示例1：收集科技媒体资讯 ```bash # 1. 配置RSS源（如36氪、TechCrunch等） # 2. 收集数据 python /workspace/projects/daily-news-collector/scripts/collect_feeds.py --config ./tech_sources.json --output ./tech_news.json # 3. 智能体分析并生成科技日报（由智能体完成） # - 筛选科技类资讯 # - 生成分类摘要 # - 输出：daily-tech-report-2024-01-15.md ``` ### 示例2：监控竞品动态 ```bash # 1. 配置竞品官网和新闻源 # 2. 收集网页内容 python /workspace/projects/daily-news-collector/scripts/collect_webpages.py --url https://competitor.com/news --output ./competitor_news.json # 3. 智能体分析竞品动态（由智能体完成） # - 提取关键信息（产品发布、融资、合作等） # - 生成竞品监控报告 ``` ### 示例3：综合日报 ```bash # 1. 从多个源收集数据（RSS + 网页） python /workspace/projects/daily-news-collector/scripts/collect_feeds.py --config ./all_sources.json --output ./all_news.json # 2. 智能体生成综合日报（由智能体完成） # - 按主题分类 # - 生成每日摘要 # - 输出：daily-report-2024-01-15.md ```

path: assets/template.md
content: # {{主题}} 日报 - {{日期}}

**生成时间**: {{生成时间}}
**资讯总数**: {{总数}} 条
**分类数量**: {{分类数}} 个

## 今日重点摘要

- 要点1：{{重要资讯简述}}
- 要点2：{{重要资讯简述}}
- 要点3：{{重要资讯简述}}

---

## {{分类名称}}

### [{{来源}}] {{文章标题}}
- **链接**: {{URL}}
- **摘要**: {{简要内容描述}}
- **发布时间**: {{时间}}

### [{{来源}}] {{文章标题}}
- **链接**: {{URL}}
- **摘要**: {{简要内容描述}}

## {{分类名称}}

### [{{来源}}] {{文章标题}}
- **链接**: {{URL}}
- **摘要**: {{简要内容描述}}
- **发布时间**: {{时间}}

---

**数据来源**: RSS订阅源、网页抓取
**生成工具**: Daily News Collector Skill
**备注**: 本报告由AI智能体自动生成


path: scripts/collect_feeds.py
content: #!/usr/bin/env python3
"""
RSS源数据收集脚本
从配置文件读取RSS源列表，解析并获取最新文章
"""

import json
import argparse
from datetime import datetime
from feedparser import parse
from typing import List, Dict, Any


def load_config(config_path: str) -> Dict[str, Any]:
    """加载配置文件"""
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        return config
    except FileNotFoundError:
        raise ValueError(f"配置文件不存在: {config_path}")
    except json.JSONDecodeError as e:
        raise ValueError(f"配置文件格式错误: {e}")


def collect_rss_source(source: Dict[str, str], limit: int = 20) -> List[Dict[str, Any]]:
    """
    收集单个RSS源的文章

    参数:
        source: RSS源配置，包含url、name、category等字段
        limit: 每个源最多收集的文章数

    返回:
        文章列表
    """
    url = source.get('url')
    name = source.get('name', '未知来源')
    category = source.get('category', '未分类')

    if not url:
        print(f"警告: 来源 {name} 缺少URL配置")
        return []

    try:
        feed = parse(url)
        if feed.bozo:
            print(f"警告: RSS源 {name} 解析错误: {feed.bozo_exception}")
            return []

        articles = []
        for entry in feed.entries[:limit]:
            article = {
                'title': entry.get('title', '无标题'),
                'link': entry.get('link', ''),
                'published': entry.get('published', ''),
                'summary': entry.get('summary', '')[:500],  # 限制摘要长度
                'source': name,
                'category': category,
                'collected_at': datetime.now().isoformat()
            }
            articles.append(article)

        print(f"成功收集 {name}: {len(articles)} 篇文章")
        return articles

    except Exception as e:
        print(f"错误: 收集 {name} 失败: {str(e)}")
        return []


def main():
    parser = argparse.ArgumentParser(description='收集RSS源数据')
    parser.add_argument('--config', required=True, help='配置文件路径（JSON格式）')
    parser.add_argument('--output', required=True, help='输出文件路径（JSON格式）')
    parser.add_argument('--limit', type=int, default=20, help='每个源最多收集的文章数')
    args = parser.parse_args()

    # 加载配置
    config = load_config(args.config)
    sources = config.get('sources', [])

    if not sources:
        raise ValueError("配置文件中未定义任何RSS源")

    # 收集所有源的数据
    all_articles = []
    for source in sources:
        articles = collect_rss_source(source, args.limit)
        all_articles.extend(articles)

    # 输出结果
    output_data = {
        'collected_at': datetime.now().isoformat(),
        'total_articles': len(all_articles),
        'sources_count': len(sources),
        'articles': all_articles
    }

    with open(args.output, 'w', encoding='utf-8') as f:
        json.dump(output_data, f, ensure_ascii=False, indent=2)

    print(f"\n收集完成: 共 {len(all_articles)} 篇文章，来自 {len(sources)} 个RSS源")
    print(f"结果已保存到: {args.output}")


if __name__ == '__main__':
    main()


path: scripts/collect_webpages.py
content: #!/usr/bin/env python3
"""
网页内容抓取脚本
从指定URL抓取网页内容，提取正文文本
"""

import json
import argparse
from datetime import datetime
from bs4 import BeautifulSoup
from typing import Dict, Any, Optional
import re

def fetch_webpage(url: str) -> Optional[str]:
    """抓取网页HTML内容"""
    try:
        import requests
        response = requests.get(url, timeout=30, headers={
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
        response.raise_for_status()
        return response.text
    except Exception as e:
        print(f"错误: 抓取网页失败 {url}: {str(e)}")
        return None


def extract_content(html: str, url: str) -> Dict[str, Any]:
    """
    从HTML中提取关键信息

    参数:
        html: HTML内容
        url: 网页URL

    返回:
        提取的内容字典
    """
    soup = BeautifulSoup(html, 'lxml')

    # 提取标题
    title = ''
    if soup.find('h1'):
        title = soup.find('h1').get_text().strip()
    elif soup.find('title'):
        title = soup.find('title').get_text().strip()

    # 尝试找到正文内容（常见的选择器）
    content_selectors = [
        'article',
        '[class*="content"]',
        '[class*="article"]',
        '[id*="content"]',
        '[id*="article"]',
        'main',
    ]

    content_text = ''
    for selector in content_selectors:
        element = soup.select_one(selector)
        if element:
            # 移除脚本和样式标签
            for script in element(['script', 'style', 'nav', 'footer']):
                script.decompose()
            content_text = element.get_text(separator='\n', strip=True)
            if len(content_text) > 200:  # 找到足够长的内容
                break

    # 如果没有找到内容，提取所有段落
    if not content_text:
        paragraphs = soup.find_all('p')
        content_text = '\n'.join([p.get_text().strip() for p in paragraphs if p.get_text().strip()])

    # 清理文本
    content_text = re.sub(r'\n+', '\n', content_text)
    content_text = content_text[:2000]  # 限制长度

    # 提取发布时间
    published = ''
    time_elements = soup.find_all(['time', 'span'], attrs={'class': re.compile(r'time|date|publish', re.I)})
    for elem in time_elements:
        text = elem.get_text().strip()
        if text:
            published = text
            break

    return {
        'url': url,
        'title': title,
        'content': content_text,
        'published': published,
        'collected_at': datetime.now().isoformat()
    }


def collect_webpages(urls: list) -> list:
    """
    收集多个网页的内容

    参数:
        urls: URL列表

    返回:
        网页内容列表
    """
    results = []

    for url in urls:
        if not url.startswith(('http://', 'https://')):
            print(f"警告: 跳过无效URL: {url}")
            continue

        print(f"正在抓取: {url}")
        html = fetch_webpage(url)

        if html:
            content = extract_content(html, url)
            results.append(content)
            print(f"成功: {content['title'][:50]}")
        else:
            print(f"失败: {url}")

    return results


def main():
    parser = argparse.ArgumentParser(description='抓取网页内容')
    parser.add_argument('--url', help='单个网页URL')
    parser.add_argument('--url-list', help='包含URL列表的文本文件（每行一个URL）')
    parser.add_argument('--output', required=True, help='输出文件路径（JSON格式）')
    args = parser.parse_args()

    # 收集URL列表
    urls = []

    if args.url:
        urls.append(args.url)

    if args.url_list:
        try:
            with open(args.url_list, 'r', encoding='utf-8') as f:
                for line in f:
                    url = line.strip()
                    if url and not url.startswith('#'):
                        urls.append(url)
        except FileNotFoundError:
            raise ValueError(f"URL列表文件不存在: {args.url_list}")

    if not urls:
        raise ValueError("未指定有效的URL")

    # 收集网页内容
    results = collect_webpages(urls)

    # 输出结果
    output_data = {
        'collected_at': datetime.now().isoformat(),
        'total_pages': len(results),
        'webpages': results
    }

    with open(args.output, 'w', encoding='utf-8') as f:
        json.dump(output_data, f, ensure_ascii=False, indent=2)

    print(f"\n抓取完成: 共 {len(results)} 个网页")
    print(f"结果已保存到: {args.output}")


if __name__ == '__main__':
    main()


path: references/sources.md
content: # 媒体源配置指南

## 目录
- [配置文件格式](#配置文件格式)
- [RSS源配置](#rss源配置)
- [常见媒体源示例](#常见媒体源示例)
- [配置文件示例](#配置文件示例)

## 概览
本指南说明如何配置媒体源数据，支持RSS订阅源、API接口等数据源。

## 配置文件格式

媒体源配置文件使用JSON格式，命名为 `sources.json`，放在当前工作目录下。

### 完整格式结构
```json
{
  "sources": [
    {
      "name": "来源名称",
      "url": "RSS源URL或API地址",
      "category": "分类",
      "type": "rss|api",
      "enabled": true
    }
  ]
}
```

### 字段说明

| 字段 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `sources` | 数组 | 是 | 媒体源列表 |
| `name` | 字符串 | 是 | 来源名称，用于标识和显示 |
| `url` | 字符串 | 是 | RSS订阅地址或API接口地址 |
| `category` | 字符串 | 否 | 分类标签，如"科技"、"商业"等，默认"未分类" |
| `type` | 字符串 | 否 | 类型标识，默认"rss" |
| `enabled` | 布尔 | 否 | 是否启用该源，默认true |

## RSS源配置

### 如何获取RSS地址
1. 访问目标网站
2. 查找页面中的"RSS"、"订阅"、"Feed"链接
3. 复制RSS链接地址（通常以.xml或/rss结尾）

### 常见RSS路径
- `https://example.com/rss`
- `https://example.com/feed`
- `https://example.com/rss.xml`

## 常见媒体源示例

### 科技类媒体
```json
{
  "name": "36氪",
  "url": "https://36kr.com/feed",
  "category": "科技"
},
{
  "name": "TechCrunch",
  "url": "https://techcrunch.com/feed/",
  "category": "科技"
},
{
  "name": "InfoQ",
  "url": "https://www.infoq.cn/feed",
  "category": "技术"
}
```

### 商业财经
```json
{
  "name": "财新网",
  "url": "https://www.caixin.com/rss/",
  "category": "财经"
},
{
  "name": "虎嗅网",
  "url": "https://www.huxiu.com/rss/0.xml",
  "category": "商业"
}
```

### 综合新闻
```json
{
  "name": "BBC News",
  "url": "http://feeds.bbci.co.uk/news/rss.xml",
  "category": "综合"
},
{
  "name": "新华网",
  "url": "http://www.xinhuanet.com/politics/news_politics.xml",
  "category": "综合"
}
```

### 行业专业媒体
```json
{
  "name": "AI News",
  "url": "https://artificialintelligence-news.com/feed/",
  "category": "人工智能"
},
{
  "name": "The Verge",
  "url": "https://www.theverge.com/rss/index.xml",
  "category": "科技产品"
}
```

## 配置文件示例

### 示例1：简单配置（仅RSS源）
```json
{
  "sources": [
    {
      "name": "36氪",
      "url": "https://36kr.com/feed",
      "category": "科技"
    },
    {
      "name": "虎嗅网",
      "url": "https://www.huxiu.com/rss/0.xml",
      "category": "商业"
    },
    {
      "name": "InfoQ",
      "url": "https://www.infoq.cn/feed",
      "category": "技术"
    }
  ]
}
```

### 示例2：完整配置（包含禁用源）
```json
{
  "sources": [
    {
      "name": "36氪",
      "url": "https://36kr.com/feed",
      "category": "科技",
      "type": "rss",
      "enabled": true
    },
    {
      "name": "TechCrunch",
      "url": "https://techcrunch.com/feed/",
      "category": "科技",
      "type": "rss",
      "enabled": true
    },
    {
      "name": "暂不使用的源",
      "url": "https://example.com/feed",
      "category": "其他",
      "type": "rss",
      "enabled": false
    }
  ]
}
```

### 示例3：分类配置
```json
{
  "sources": [
    {
      "name": "36氪",
      "url": "https://36kr.com/feed",
      "category": "科技"
    },
    {
      "name": "财新网",
      "url": "https://www.caixin.com/rss/",
      "category": "财经"
    },
    {
      "name": "新华网",
      "url": "http://www.xinhuanet.com/politics/news_politics.xml",
      "category": "综合"
    },
    {
      "name": "AI News",
      "url": "https://artificialintelligence-news.com/feed/",
      "category": "人工智能"
    }
  ]
}
```

## 注意事项
1. 确保RSS源地址可访问且格式正确
2. 定期检查源是否失效，及时更新配置
3. 合理设置分类，便于后续智能体分析和整理
4. 避免配置过多源，影响收集效率
5. 遵守目标网站的使用条款和robots.txt规则


path: references/format.md
content: # 日报格式规范

## 目录
- [日报结构](#日报结构)
- [格式要求](#格式要求)
- [示例模板](#示例模板)
- [内容规范](#内容规范)

## 概览
本规范定义生成的每日资讯日报的格式要求，确保输出内容结构清晰、易于阅读。

## 日报结构

### 标准结构
1. **报头**
   - 日报标题
   - 日期
   - 统计数据

2. **分类板块**
   - 按主题分类的资讯列表
   - 每个板块包含相关资讯

3. **摘要**
   - 当日重点摘要（可选）

4. **报尾**
   - 数据来源说明
   - 生成时间

## 格式要求

### 1. 报头格式
```markdown
# [主题] 日报 - YYYY-MM-DD

**生成时间**: YYYY-MM-DD HH:MM:SS
**资讯总数**: XX 条
**分类数量**: XX 个
```

### 2. 分类板块格式
每个分类使用二级标题，资讯使用有序列表：

```markdown
## 分类名称

### [来源] 文章标题
- **链接**: URL
- **摘要**: 简要内容描述（1-2句话）
- **发布时间**: 时间信息（可选）

### [来源] 文章标题
- **链接**: URL
- **摘要**: 简要内容描述
```

### 3. 每日摘要格式（可选）
```markdown
## 今日重点摘要

- 要点1：重要资讯简述
- 要点2：重要资讯简述
- 要点3：重要资讯简述
```

### 4. 报尾格式
```markdown
---

**数据来源**: RSS订阅源、网页抓取
**生成工具**: Daily News Collector Skill
**备注**: 本报告由AI智能体自动生成
```

## 示例模板

### 完整示例
```markdown
# 科技日报 - 2024-01-15

**生成时间**: 2024-01-15 09:30:00
**资讯总数**: 25 条
**分类数量**: 4 个

## 今日重点摘要

- 要点1：AI领域发布新一代大模型，性能提升显著
- 要点2：多家科技公司公布季度财报，超出市场预期
- 要点3：新型电池技术取得突破，续航里程提升50%

---

## 科技

### [36氪] 某科技公司发布AI新模型
- **链接**: https://36kr.com/p/123456
- **摘要**: 该模型在多项基准测试中超越前代产品，推理速度提升3倍
- **发布时间**: 2024-01-15

### [InfoQ] 开源社区发布新框架
- **链接**: https://infoq.cn/article/123456
- **摘要**: 新框架旨在简化微服务开发，提供完整的企业级解决方案

### [TechCrunch] 创业公司获得千万融资
- **链接**: https://techcrunch.com/2024/01/15/startup-funding
- **摘要**: 该公司专注于AI医疗诊断，本轮融资将用于产品研发

## 商业

### [虎嗅网] 某电商平台发布财报
- **链接**: https://www.huxiu.com/article/123456
- **摘要**: 营收同比增长20%，净利润创历史新高

### [财新网] 市场监管新政策解读
- **链接**: https://www.caixin.com/2024-01-15/policy.html
- **摘要**: 新政策加强对互联网平台的监管，预计明年正式实施

## 人工智能

### [AI News] 新研究突破计算瓶颈
- **链接**: https://artificialintelligence-news.com/research-breakthrough
- **摘要**: 研究团队提出新的算法架构，大幅降低AI模型的计算成本

### [The Verge] AI助手集成到更多设备
- **链接**: https://www.theverge.com/ai-integration
- **摘要**: 多家硬件厂商宣布将AI助手深度集成到新一代设备中

## 综合

### [新华网] 国家发布科技发展新规划
- **链接**: http://www.xinhuanet.com/politics/2024-01-15/plan.html
- **摘要**: 新规划明确未来5年科技发展目标，重点支持前沿技术研发

---

**数据来源**: RSS订阅源、网页抓取
**生成工具**: Daily News Collector Skill
**备注**: 本报告由AI智能体自动生成
```

## 内容规范

### 1. 资讯标题
- 使用来源名称作为前缀，如 `[36氪]`
- 标题保持原文，不超过50个字
- 避免使用特殊符号和emoji

### 2. 摘要要求
- 简明扼要，1-2句话概括核心内容
- 字数控制在50-100字
- 突出关键信息（新产品、新技术、重要事件等）
- 避免重复原文中的冗长描述

### 3. 分类规则
- 按内容主题分类，常见分类：科技、商业、人工智能、综合
- 同一分类下的资讯按相关性排序
- 每个分类至少包含2条资讯，少于2条的合并到"其他"分类

### 4. 排序规则
- 每个分类内按发布时间倒序排列
- 同一时间发布的按相关性排序
- 重点摘要放在最前面

### 5. 格式一致性
- 统一使用Markdown格式
- 链接使用完整URL
- 时间格式统一为：YYYY-MM-DD 或 YYYY-MM-DD HH:MM

## 注意事项
1. 保持简洁，避免冗长描述
2. 确保链接可访问
3. 摘要要客观准确，不添加主观评价
4. 定期检查格式规范，保持一致性
5. 根据用户需求可适当调整分类和结构


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