DewXIT/osint-feed
GitHub: DewXIT/osint-feed
一个基于配置驱动的 Node.js 新闻采集器,从 RSS 和 HTML 页面抓取文章、自动去重并生成可直接注入 LLM 上下文的紧凑摘要。
Stars: 2 | Forks: 0
# osint-feed
基于配置驱动的 Node.js 新闻采集器。从 RSS 订阅源和 HTML 页面中提取文章,进行去重处理,并生成准备注入 LLM 上下文窗口的紧凑摘要。
内部不包含 AI。不对你的技术栈做任何假设。只需文章输入,结构化数据输出。
有 RSS 时使用 RSS。没有时使用 HTML 选择器。针对特定主题的过滤应由调用此库的应用程序负责处理。
## 为什么需要它
你正在构建需要最新新闻上下文的应用——例如 SITREP(态势报告)生成器、威胁监控器或研究助手。你拥有 30 多个跨语言和格式的信息源。你需要将数据压缩得足够紧凑,以便在 Llama/GPT 上下文窗口中运行而不超支预算。
现有工具要么仅支持 Python (newspaper4k),要么是需要大量维护的自托管平台,要么是商业 API (Newscatcher, NewsAPI)。在 JS/TS 生态系统中,没有哪种工具能实现配置驱动的多源采集并内置适配 LLM 的压缩功能。
`osint-feed` 填补了这一空白。
## 安装
```
npm install osint-feed
```
需要 Node.js 18+。
## 快速开始
```
import { createHarvester } from "osint-feed";
const harvester = createHarvester({
sources: [
{
id: "bbc-world",
name: "BBC World",
type: "rss",
url: "https://feeds.bbci.co.uk/news/world/rss.xml",
tags: ["global", "uk"],
interval: 15,
},
{
id: "nato",
name: "NATO Newsroom",
type: "html",
url: "https://www.nato.int/cps/en/natohq/news.htm",
tags: ["nato"],
interval: 30,
selectors: {
article: ".event-list-item",
title: "a span:first-child",
link: "a",
date: ".event-date",
},
},
],
});
// Fetch everything
const articles = await harvester.fetchAll();
// Or get an LLM-ready digest
const { articles: digest, stats } = await harvester.digest();
console.log(`${stats.totalFetched} articles -> ${stats.afterDedup} unique -> ${stats.estimatedTokens} tokens`);
```
## 源类型
### RSS / Atom
开箱即用。无需选择器——订阅源会被自动解析。
```
{
id: "france24",
name: "France24",
type: "rss",
url: "https://www.france24.com/en/rss",
tags: ["global", "europe"],
interval: 15,
}
```
### HTML 抓取
你可为每个源定义 CSS 选择器。该库使用 [cheerio](https://github.com/cheeriojs/cheerio)——无需无头浏览器,也没有 Puppeteer 的开销。
这依然是配置驱动的抓取:该库不会自动发现文章列表或推断哪些内容与你的用例相关。
```
{
id: "defence24",
name: "Defence24",
type: "html",
url: "https://defence24.pl/",
tags: ["poland", "defence"],
interval: 15,
selectors: {
article: "article", // repeating container
title: "h2 a", // title text (within article)
link: "h2 a", // link href (within article)
date: "time", // optional: publication date
summary: ".lead", // optional: description text
},
}
```
## API
### `createHarvester(options)`
创建采集器实例。选项:
| 选项 | 类型 | 默认值 | 描述 |
|--------|------|---------|-------------|
| `sources` | `SourceConfig[]` | 必填 | 源定义数组 |
| `dedup.known` | `() => string[]` | — | 返回数据库中已存在的哈希值(用于跨会话去重) |
| `digest` | `DigestOptions` | 见下文 | 默认摘要设置 |
| `requestTimeout` | `number` | `15000` | HTTP 超时时间(毫秒) |
| `requestGap` | `number` | `1000` | 请求之间的最小间隔毫秒数(速率限制) |
| `maxItemsPerSource` | `number` | `50` | 单个源返回的文章数上限 |
| `fetch` | `Function` | global fetch | 用于代理或测试的自定义 fetch 函数 |
| `onError` | `Function` | — | 每个源的获取或解析错误的回调 |
| `onWarning` | `Function` | — | 非致命源诊断信息的回调 |
### `harvester.fetchAll()`
获取所有已启用的源。返回 `Article[]`。
如果其中一个源失败,该方法仍会返回其他源的文章,并在提供 `onError` 的情况下报告问题。
### `harvester.fetch(sourceId)`
按 ID 获取单个源。
### `harvester.fetchByTags(tags)`
获取匹配任意给定标签的源。
### `harvester.digest(options?)`
核心功能。获取所有源,然后运行压缩流水线:
1. **去重** — 将相似的标题分组(基于 Jaccard 相似度)并保留内容最丰富的版本
2. **排序** — 最新优先
3. **标签预算** — 限制每个标签的文章数量,以免单一主题占据主导
4. **截断** — 将每篇文章的内容截断至 N 个字符
5. **Token 预算** — 从底部开始修剪,直到低于 token 限制
```
const { articles, stats } = await harvester.digest({
maxTokens: 12_000, // total token budget
maxArticlesPerTag: 10, // max articles per tag group
maxContentLength: 500, // chars per article content
similarityThreshold: 0.6, // title dedup threshold (0-1)
sort: "recency",
});
// stats.totalFetched → 700 (raw from all sources)
// stats.afterDedup → 200 (unique stories)
// stats.afterBudget → 80 (within tag limits)
// stats.estimatedTokens → 18000 (final token count)
```
### `harvester.start(callbacks)` / `harvester.stop()`
按配置的间隔运行源。由你自行处理存储。
```
harvester.start({
onArticles: async (articles, source) => {
await db.insert("articles", articles);
console.log(`${articles.length} new from ${source.name}`);
},
onError: (err, source) => {
console.error(`${source.name} failed:`, err);
},
onWarning: (warning, source) => {
console.warn(`${source.name}: ${warning.code} - ${warning.message}`);
},
});
// Later:
harvester.stop();
```
## 文章 Schema
```
interface Article {
sourceId: string; // matches source config id
url: string; // canonical article URL
title: string;
content: string | null; // full text (when available)
summary: string | null; // short description
publishedAt: Date | null;
hash: string; // SHA-256 of URL (dedup key)
fetchedAt: Date;
tags: string[]; // inherited from source
}
```
## 跨会话去重
该库会自动处理批次内的去重。对于跨会话去重(不重复处理数据库中已有的文章),请传入 `known` 回调:
```
const harvester = createHarvester({
sources,
dedup: {
known: async () => {
const rows = await db.query("SELECT hash FROM articles");
return rows.map(r => r.hash);
},
},
});
// fetchAll() now skips articles whose URL hash is already known
```
## 诊断
该库保持了正常路径的简洁性:`fetchAll()` 和 `digest()` 依然直接返回文章数据。
如果你希望了解部分失败或低质量源输出的情况,请使用 `onError` 和 `onWarning`。
- `onError` 涵盖诸如超时、HTTP 错误和解析失败等严重错误。
- `onWarning` 涵盖诸如源结果为空、缺少发布日期或单源截断等非致命问题。
这符合典型的小型库 OSS 模式:易于使用的默认值,为日志和监控提供的可选钩子。
## 范围与限制
- RSS 和 HTML 是一等源类型。
- 当你能定义稳定的列表选择器时,HTML 效果最佳。
- 该库不执行页面 JavaScript 或运行无头浏览器。
- 该库不决定哪些内容与你的领域相关;请在下游应用你自己的过滤器。
## 配合 Next.js 使用
```
// app/api/feed/route.ts
import { createHarvester } from "osint-feed";
const harvester = createHarvester({ sources: [...] });
export async function GET() {
const { articles, stats } = await harvester.digest({ maxTokens: 8000 });
return Response.json({ articles, stats });
}
```
## 配合 Express 使用
```
import express from "express";
import { createHarvester } from "osint-feed";
const app = express();
const harvester = createHarvester({ sources: [...] });
app.get("/digest", async (_req, res) => {
const result = await harvester.digest();
res.json(result);
});
```
## 摘要算法的工作原理
来自 10 个 RSS + 3 个 HTML 源冒烟测试的实际数据:
```
Raw fetch: 324 articles
After title dedup: 319 unique stories
After tag budget: 47 (8 per tag, 6 tags)
Estimated tokens: 5,781
```
这仅占 **Llama 3 128k 上下文的 1.8%**。为系统提示词、历史记录和推理留足了空间。
如果 35 个源每 15 分钟轮询一次,你每小时将获得约 700 篇文章。摘要流水线会将其压缩至约 80 篇文章 / 约 1.8 万个 token。可根据需要调整 `maxArticlesPerTag` 和 `maxTokens`。
## 依赖项
仅有两个:
- [`cheerio`](https://github.com/cheeriojs/cheerio) — HTML 解析
- [`rss-parser`](https://github.com/rbren/rss-parser) — RSS/Atom 解析
没有无头浏览器。没有原生模块。没有臃肿代码。
## 许可证
MIT
## 免责声明
本库是用于获取和解析公开可用的网络内容的工具。用户需自行负责遵守目标网站的服务条款及适用法律。作者对本库的使用方式不承担任何责任。
标签:DLL 劫持, ESC4, GNU通用公共许可证, HTTP/HTTPS抓包, LLM数据预处理, MITM代理, Node.js, npm包, OSINT, RSS解析, SITREP, TypeScript, URL抓取, 去重, 大语言模型, 威胁情报, 安全插件, 开发者工具, 态势感知, 情报收集, 数据可视化, 数据抓取, 数据清洗, 文本摘要, 新闻采集, 漏洞研究, 自动化攻击