Observed since 21 Oct, 11:31 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ai-news-collector-lib
A Python library for collecting AI-related news from multiple sources
一个用于收集AI相关新闻的Python库,支持多种搜索源和高级功能。
AI News Collector 截止最新版本(v0.1.7),已支持从 15 个不同数据源 收集 AI 相关信息,覆盖 新闻资讯、学术论文、开源项目、AI 模型、视频内容、AI 搜索 等多种维度。
| 源 | 描述 | 特点 |
|---|---|---|
| 🔥 HackerNews | 技术社区讨论 | 实时热点,开发者友好 |
| 📚 ArXiv | 学术论文预印本 | 学术质量,多学科覆盖 |
| 🦆 DuckDuckGo | 隐私搜索引擎 | 隐私保护,广泛覆盖 |
| 🐙 GitHub | 开源项目搜索 | 代码库/Star/Language,可选 Token 提高速率 |
| 🤗 HuggingFace | AI 模型搜索 | 模型/数据集/Downloads/Likes,可选 Token |
| 📰 RSS Feeds | RSS 订阅源 | 自定义订阅,灵活扩展 |
| 源 | API | 特点 | 免费额度 |
|---|---|---|---|
| 📹 YouTube | YouTube Data API v3 | 视频搜索,频道信息 | 10k units/天 |
| 🧠 Perplexity | Perplexity Sonar API | AI 智能搜索,带引用摘要 | 按量付费 |
| 📡 NewsAPI | newsapi.org | 多源新闻聚合 | 100 请求/天 |
| 🔍 Tavily | tavily.com | AI 驱动搜索 | 1000 请求/月 |
| 🌐 Google Search | googleapis.com | 精准搜索,覆盖广 | 100 请求/天 |
| ⚡ Serper | serper.dev | 高速 Google 搜索 | 100 请求/月 |
| 🦁 Brave Search | search.brave.com | 独立隐私搜索 | 100 请求/月 |
| 🛰️ MetaSota | metaso.cn (MCP) | 多模态搜索(MCP 协议) | 按量/平台策略 |
| 🧭 Baidu Search | qianfan.baidubce.com | 百度千帆网页搜索,实时中文内容覆盖 | 1000 请求/天 |
注: MetaSota Search 通过 MCP 协议支持,需要特殊配置。
⏰ 所有搜索引擎的时间过滤参数 days_back 均以 UTC 时间为准,published 字段统一输出为 ISO8601(UTC 时区)。
v0.1.7 版本更新 - 迁移
QueryEnhancer至 Google 官方全新google-genaiSDK,消除废弃警告并增强容灾测试覆盖。
QueryEnhancer 迁移至 google-genai (from google import genai),采用现代 Client 架构与 client.models.generate_content()google-generativeai 库的 FutureWarninggoogle-genai>=0.1.0,新增 Mock 生成、缓存与降级测试📋 详见: 完整指南 | v0.1.7 发布总结
v0.1.3 版本更新 - 引入了 AI 驱动的查询增强功能。
# 基础安装
pip install ai-news-collector-lib
# 安装开发/测试依赖
pip install ai-news-collector-lib[dev]
# 或从源代码安装
pip install -e .[dev]
创建 .env 文件并配置API密钥(可选,仅用于付费API):
# v0.1.5+ 新增搜索引擎配置
YOUTUBE_API_KEY=your_youtube_key
GITHUB_TOKEN=your_github_token # 可选 (提高速率限制)
HUGGINGFACE_API_KEY=your_hf_token # 可选
PERPLEXITY_API_KEY=your_perplexity_key
BAIDU_SEARCH_API_KEY=your_baidu_key
# 现有搜索引擎配置
NEWS_API_KEY=your_newsapi_key
TAVILY_API_KEY=your_tavily_key
GOOGLE_SEARCH_API_KEY=your_google_key
GOOGLE_SEARCH_ENGINE_ID=your_engine_id
SERPER_API_KEY=your_serper_key
BRAVE_SEARCH_API_KEY=your_brave_key
METASOSEARCH_API_KEY=your_metasota_key
⚠️ 重要:请勿将
.env文件提交到版本控制。参见 API密钥安全指南。
import asyncio
from ai_news_collector_lib import AINewsCollector, SearchConfig
async def main():
# 创建配置
config = SearchConfig(
enable_hackernews=True,
enable_arxiv=True,
enable_duckduckgo=True,
enable_github_search=True, # v0.1.5 新增,免费
enable_huggingface=True, # v0.1.5 新增,免费
max_articles_per_source=10,
days_back=7
)
# 创建收集器
collector = AINewsCollector(config)
# 收集新闻
result = await collector.collect_news("machine learning")
# 输出结果
print(f"收集 {result.total_articles} 篇文章(去重后 {result.unique_articles} 篇)")
for article in result.articles[:5]:
print(f"- [{article.source}] {article.title}")
return result
# 运行
asyncio.run(main())
import asyncio
from ai_news_collector_lib import AdvancedAINewsCollector, AdvancedSearchConfig
async def main():
# 创建带 LLM 查询增强的配置
config = AdvancedSearchConfig(
enable_hackernews=True,
enable_arxiv=True,
enable_tavily=True,
enable_github_search=True, # v0.1.5 新增
enable_query_enhancement=True, # ✨ 启用 LLM 查询增强
llm_provider="google-gemini",
llm_model="gemini-2.5-pro",
llm_api_key="your-google-api-key", # 从环境变量设置更安全
query_enhancement_cache_ttl=86400, # 24 小时缓存
max_articles_per_source=10
)
# 创建收集器
collector = AdvancedAINewsCollector(config)
# LLM 会自动为各个搜索引擎优化查询
result = await collector.collect_news_advanced("machine learning")
# 查看增强后的查询
if result.get('enhanced_queries'):
for engine, query in result['enhanced_queries'].items():
print(f" - {engine}: {query}")
return result
asyncio.run(main())
import asyncio
from ai_news_collector_lib import FlexibleAINewsCollector, FlexibleSearchConfig, TimeRange
async def main():
# 灵活配置 - 为每个搜索引擎独立设置参数
config = FlexibleSearchConfig()
# 为 HackerNews 设置独立的时间范围和文章数
config.set_engine_config("hackernews", enabled=True, max_articles=20, time_range=TimeRange.ONE_DAY)
# 为 GitHub 设置较长的时间范围
config.set_engine_config("github_search", enabled=True, max_articles=15, time_range=TimeRange.ONE_MONTH)
# 为 YouTube 设置 API 密钥和参数
config.set_engine_config("youtube", enabled=True, max_articles=10, api_key="your-key")
# 创建灵活收集器
collector = FlexibleAINewsCollector(config)
result = await collector.collect_news("artificial intelligence")
print(f"收集 {result.total_articles} 篇文章")
return result
asyncio.run(main())
import asyncio
from ai_news_collector_lib import AdvancedAINewsCollector, AdvancedSearchConfig
async def main():
# 创建高级配置
config = AdvancedSearchConfig(
enable_hackernews=True,
enable_arxiv=True,
enable_duckduckgo=True,
enable_github_search=True,
enable_content_extraction=True, # 自动提取内容
enable_keyword_extraction=True, # 自动提取关键词
cache_results=True, # 启用缓存
max_articles_per_source=10
)
# 创建高级收集器
collector = AdvancedAINewsCollector(config)
# 收集增强新闻
result = await collector.collect_news_advanced("artificial intelligence")
# 分析结果
total_words = sum(article.get('word_count', 0) for article in result['articles'])
print(f"总字数: {total_words}")
return result
# 运行
asyncio.run(main())
import asyncio
from ai_news_collector_lib import AdvancedAINewsCollector, AdvancedSearchConfig
async def main():
# 创建配置 - 混合使用免费和付费源
config = AdvancedSearchConfig(
enable_hackernews=True,
enable_arxiv=True,
enable_tavily=True, # 付费搜索API
enable_google_search=True, # 谷歌自定义搜索
enable_serper=True, # Serper搜索API
enable_youtube=True, # YouTube 视频搜索
enable_perplexity=True, # Perplexity AI 搜索
enable_baidu_search=True, # Baidu Search(百度千帆)
cache_results=True, # 启用缓存减少API调用
max_articles_per_source=15,
similarity_threshold=0.85
)
collector = AdvancedAINewsCollector(config)
result = await collector.collect_news_advanced("deep learning")
return result
asyncio.run(main())
from ai_news_collector_lib import AdvancedSearchConfig
config = AdvancedSearchConfig(
# 传统源
enable_hackernews=True,
enable_arxiv=True,
enable_rss_feeds=False,
# 付费搜索源
enable_tavily=False,
enable_google_search=False,
enable_serper=False,
enable_brave_search=False,
enable_metasota_search=False,
enable_baidu_search=False,
enable_newsapi=False,
# v0.1.5+ 新增搜索源
enable_youtube=False, # YouTube 视频搜索(需要 API 密钥)
enable_github_search=False, # GitHub 项目搜索(可选 API 密钥)
enable_huggingface=False, # HuggingFace 模型搜索(可选 API 密钥)
enable_perplexity=False, # Perplexity AI 搜索(需要 API 密钥)
enable_baidu_search=False, # Baidu Search(需要 API 密钥)
# 网页搜索
enable_duckduckgo=True,
# 高级功能
enable_content_extraction=False, # 自动提取文章内容
enable_keyword_extraction=False, # 自动提取关键词
cache_results=False, # 缓存结果
# 搜索参数
max_articles_per_source=10,
days_back=7,
# ⏰ 本参数对所有引擎有效;内部时间过滤使用 UTC;所有 published 输出均为 ISO8601(UTC)
similarity_threshold=0.85,
)
from ai_news_collector_lib import DailyScheduler, AdvancedAINewsCollector, AdvancedSearchConfig
async def collect_news():
config = AdvancedSearchConfig(
enable_hackernews=True,
enable_arxiv=True,
cache_results=True
)
collector = AdvancedAINewsCollector(config)
return await collector.collect_news_advanced("AI")
# 创建定时任务 - 每天上午9点
scheduler = DailyScheduler(
collector_func=collect_news,
schedule_time="09:00",
timezone="Asia/Shanghai"
)
# 启动调度器
scheduler.start()
from ai_news_collector_lib import CacheManager
# 创建缓存管理器
cache = CacheManager(cache_dir="./cache", default_ttl_hours=24)
# 获取缓存
cache_key = cache.get_cache_key("AI news", ["hackernews", "arxiv"])
cached_result = cache.get_cached_result(cache_key)
if cached_result:
print("使用缓存结果")
result = cached_result
else:
# 执行搜索
result = await collector.collect_news("AI news")
# 缓存结果
cache.cache_result(cache_key, result)
from ai_news_collector_lib import ReportGenerator
# 创建报告生成器
reporter = ReportGenerator(output_dir="./reports")
# 生成Markdown报告
report = reporter.generate_daily_report(result, format="markdown")
reporter.save_report(result, filename="daily_report.md")
# 生成CSV报告
reporter.generate_daily_report(result, format="csv")
# 运行基础测试
pytest
# 运行所有测试(包括付费API测试)
pytest -v
# 生成覆盖率报告
pytest --cov=ai_news_collector_lib --cov-report=html
项目包含预录制的VCR cassettes,允许在完全离线状态下测试所有付费API集成 - 无需真实API密钥。
# 运行付费API测试(使用cassettes,完全离线)
pytest tests/test_integration_advanced.py -v
# 查看cassette记录详情
cat tests/cassettes/advanced_ml_hn_ddg.yaml
VCR库记录真实的HTTP请求/响应,然后在测试中重放(无需真实API调用):
import pytest
from vcr import VCR
# 使用cassette进行测试
@pytest.mark.vcr
def test_with_cassette(vcr):
# 首次运行记录HTTP交互,后续测试直接重放
result = collector.search(query="AI")
assert len(result) > 0
详见: VCR Cassette详解 | 测试指南 | FAQ
项目使用GitHub Actions实现完整的自动化测试和发布:
| 工作流 | 触发条件 | 功能 |
|---|---|---|
| test-paid-apis | Push到任何分支 | 运行所有测试,生成覆盖率报告 |
| publish | Push git标签 (v*) | 自动构建并发布到PyPI |
| release | 发布时 | 创建GitHub Release页面 |
# 1. 确保所有测试通过
pytest
# 2. 创建版本标签
git tag -a v0.1.6 -m "Release v0.1.6"
# 3. 推送标签(自动触发发布工作流)
git push origin v0.1.6
ArXiv日期解析包含完整的回退机制:
published字段published_parsed和updated_parsed字段published_parsed → updated_parsed → datetime.now()Z表示UTC,使用datetime.fromisoformat解析最小验证脚本:
python scripts/min_check_feedparser_fallback.py
该脚本验证RSS和Atom格式在缺少日期字段时的回退逻辑。
欢迎贡献代码和改进建议!
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)详见: 完整贡献指南
本项目采用 MIT 许可证。详见 LICENSE 文件。
Q: 部分旧文章会拖低时间过滤准确率吗? A: 不会。所有时间过滤严格按 UTC 执行,并跳过无法识别的发布时间,准确率不会被历史无效数据影响。
Q: GitHub 和 HuggingFace 需要 API 密钥吗?
A: 不需要。两者都可以在无密钥的情况下使用。设置 GITHUB_TOKEN 可以将速率限制从 60 次/小时提升到 5000 次/小时;设置 HUGGINGFACE_API_KEY 可以提高速率限制。
Q: 如何不使用API密钥运行测试? A: 使用VCR cassettes!测试会自动使用预录制的HTTP响应。详见VCR说明。
Q: 是否可以在生产环境中使用此库? A: 可以,但请确保:
Q: 如何贡献新的搜索源? A: 详见架构设计中的"添加新搜索源"部分。
详见: 完整FAQ
QueryEnhancer 全面迁移至 Google 官方全新 SDK(from google import genai),采用现代 Client 架构google-generativeai 抛出的 FutureWarning 弃用警告google-generativeai,引入 google-genai>=0.1.0QueryEnhancer 的 Mock 生成、缓存与异常降级容灾测试用例web_search APIBAIDU_SEARCH_API_KEY 环境变量支持与自动启用SearchConfig / AdvancedSearchConfig / FlexibleSearchConfig / APIKeyManager 同步支持search_filter.range.page_time + 客户端 UTC 过滤)SearchConfig、AdvancedSearchConfig、FlexibleSearchConfig 中均可使用FlexibleSearchConfig - 支持为每个引擎独立配置参数FlexibleAINewsCollector - 灵活收集器,支持引擎级别参数控制EnhancedQuery 数据模型QueryEnhancer 工具类ai_news_collector_lib/
├── __init__.py # 主模块入口
├── cli.py # 命令行接口
├── config/ # 配置模块
│ ├── __init__.py
│ ├── settings.py # SearchConfig / AdvancedSearchConfig
│ ├── flexible_config.py # FlexibleSearchConfig
│ └── api_keys.py # API密钥管理
├── core/ # 核心功能
│ ├── __init__.py
│ ├── collector.py # 基础收集器 (AINewsCollector)
│ ├── advanced_collector.py # 高级收集器 (AdvancedAINewsCollector)
│ └── flexible_collector.py # 灵活收集器 (FlexibleAINewsCollector)
├── models/ # 数据模型
│ ├── __init__.py
│ ├── article.py # 文章模型
│ ├── enhanced_query.py # LLM 增强查询模型
│ └── result.py # 结果模型
├── tools/ # 搜索工具 (15 个搜索引擎)
│ ├── __init__.py
│ └── search_tools.py # 各种搜索工具实现
├── utils/ # 工具函数
│ ├── __init__.py
│ ├── cache.py # 缓存管理
│ ├── content_extractor.py # 内容提取
│ ├── keyword_extractor.py # 关键词提取
│ ├── query_enhancer.py # LLM 查询增强
│ ├── reporter.py # 报告生成
│ └── scheduler.py # 定时任务
└── examples/ # 使用示例
├── basic_usage.py
└── advanced_usage.py
tests/
├── conftest.py # pytest配置
├── test_basic.py # 基础功能测试
├── test_integration_basic.py # 基础集成测试
├── test_integration_advanced.py # 付费API集成测试
├── test_new_search_tools.py # 新搜索引擎测试
├── cassettes/ # VCR cassette文件
│ ├── basic_ai_hn_ddg.yaml
│ ├── advanced_ml_hn_ddg.yaml
│ └── ...
└── test_arxiv_fallback_offline.py # ArXiv特殊测试
祝你使用愉快! 🎉
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
requestsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | requests>=2.28.0 | runtime |
beautifulsoup4Environment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | beautifulsoup4>=4.11.0 | runtime |
feedparserEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | feedparser>=6.0.0 | runtime |
python-dotenvEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | python-dotenv>=0.19.0 | runtime |
google-genaiEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | google-genai>=1.47.0 | runtime |
aiohttpEnvironment{
"extras": "",
"marker": "extra == \"advanced\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | aiohttp>=3.8.0; extra == "advanced" | runtime |
redisEnvironment{
"extras": "",
"marker": "extra == \"advanced\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | redis>=4.0.0; extra == "advanced" | runtime |
scheduleEnvironment{
"extras": "",
"marker": "extra == \"advanced\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | schedule>=1.2.0; extra == "advanced" | runtime |
apschedulerEnvironment{
"extras": "",
"marker": "extra == \"advanced\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | apscheduler>=3.9.0; extra == "advanced" | runtime |
nltkEnvironment{
"extras": "",
"marker": "extra == \"nlp\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | nltk>=3.8; extra == "nlp" | runtime |
spacyEnvironment{
"extras": "",
"marker": "extra == \"nlp\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | spacy>=3.4.0; extra == "nlp" | runtime |
textblobEnvironment{
"extras": "",
"marker": "extra == \"nlp\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | textblob>=0.17.0; extra == "nlp" | runtime |
fastapiEnvironment{
"extras": "",
"marker": "extra == \"web\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | fastapi>=0.80.0; extra == "web" | runtime |
uvicornEnvironment{
"extras": "",
"marker": "extra == \"web\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | uvicorn>=0.18.0; extra == "web" | runtime |
streamlitEnvironment{
"extras": "",
"marker": "extra == \"web\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | streamlit>=1.20.0; extra == "web" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.0.0; extra == "dev" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio>=0.20.0; extra == "dev" | runtime |
blackEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | black>=22.0.0; extra == "dev" | runtime |
flake8Environment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | flake8>=5.0.0; extra == "dev" | runtime |
mypyEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | mypy>=0.950; extra == "dev" | runtime |
vcrpyEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | vcrpy>=4.4.0; extra == "dev" | runtime |
bdist_wheel · 69.73 KB · Python py3
sdist · 991.07 KB · Python source
Shared repository data · GitHub
Observed since 21 Oct, 11:31 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:bc0e0d1c1db69717b87beb94c031820645ecd2dd7d78e2a405e0fcef4d1ae5f6