npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sanmis/crawler

v1.0.1

Published

Enterprise-grade declarative web crawler engine for policy & information monitoring — multi-source, LLM extraction, pluggable industry presets (tax, education, social-security, government)

Readme

@sanmis/crawler

企业级通用 Web 爬虫引擎 — 声明式 DSL,一行配置即可监控任意行业的政策/信息变更

TypeScript License npm

目录


核心理念

信息差 = 竞争力。本引擎解决的核心问题:

  1. 自动化监控:无人值守监控任意网站的信息变更
  2. 结构化提取:将非结构化的 HTML 页面转为结构化数据
  3. 智能差异:精准识别新增/修改/不变的内容
  4. 零代码配置:通过声明式 DSL 定义数据源,无需编写爬虫代码
  5. 行业通用:不绑定特定领域,支持税务、教育、社保、金融等所有行业

行业支持

| 行业 | 预置数据源 | Prompt 模板 | 典型场景 | |------|-----------|-------------|---------| | 税务 | 中/美/英/港/新/日/OECD 共 7 个 | tax-policy-extraction | 个税、企税、增值税政策监控 | | 教育 | 中国研招网、美国教育部 共 2 个 | education-policy-extraction | 考研、专升本、四六级、留学生政策 | | 社保 | 中国人社部、美国 SSA、日本厚劳省 共 3 个 | social-security-extraction | 养老、医保、失业、工伤政策 | | 政府 | 可配置任意政府网站 | government-announcement-extraction | 国务院/部委公告、政策解读 | | 通用 | 任意网站 | general-policy-extraction | 所有行业的政策信息提取 |

架构概览

┌─ Source Config DSL ───────────────────────────────────────┐
│  声明式配置:URL、提取规则、调度策略、LLM 参数、行业分类     │
└──────────────────────┬────────────────────────────────────┘
                       ▼
┌─ Pipeline 管线 ───────────────────────────────────────────┐
│                                                           │
│  Scrape ──► Detail Crawl ──► Extract ──► Normalize       │
│    │                           │                    │     │
│    │                ┌──────────┴──────┐              │     │
│    │                │ CSS   LLM  Hybrid │            │     │
│    │                └──────────┬──────┘              │     │
│    │                           ▼                    │     │
│    │              Validate ──► Dedup ──► Diff       │     │
│    │                                    │           │     │
│    └────────────────────────────────────┘           │     │
│                     ▼                               │     │
│              ReviewGate ──► Store                   │     │
│                                                           │
└──────────────────────┬────────────────────────────────────┘
                       ▼
┌─ Storage Adapters ────────────────────────────────────────┐
│  PostgreSQL │ Memory (更多适配器开发中)                      │
└───────────────────────────────────────────────────────────┘

核心特性

  • 声明式 DSL — JSON/TypeScript 配置即数据源,零爬虫代码
  • 4 种爬取器 — HTTP (cheerio) / Browser (Playwright) / RSS / API
  • 3 种提取策略 — CSS 选择器 / LLM 语义 / Hybrid 混合
  • 智能差异检测 — 哈希 + Jaccard + TF-IDF 语义相似度
  • 9 步质量管线 — Scrape → DetailCrawl → Extract → Normalize → Validate → Dedup → Diff → Review → Store
  • 三级审查门控 — Auto Approved / Auto Flagged / Pending Review
  • 12 个行业预设 — 税务 7 + 教育 2 + 社保 3,覆盖中/美/英/港/新/日/OECD
  • 6 个 LLM Prompt 模板 — 税务/教育/社保/政府/通用/文章提取
  • 自动调度 — 内置 Cron 调度器 + Webhook 回调
  • Dry Run 模式 — 预览抓取结果但不存储
  • Proxy + ETag + Cookie — 全传输层支持(含 Browser 模式)
  • 并发详情页爬取 — 列表页 → 详情页自动跟进
  • 增量模式 — 跳过已处理的 URL,大幅节省请求
  • 结构化错误 — SyncError code/category/message/detail/url/stack
  • 双存储适配器 — Memory(测试)/ PostgreSQL(生产)
  • CJS + ESM 双输出 — 兼容所有 Node.js / Bundler 环境

快速开始

安装

npm install @sanmis/crawler

# 可选:需要浏览器渲染时安装
npm install playwright
npx playwright install chromium

5 分钟示例

import { CrawlerEngine } from "@sanmis/crawler"

// 1. 创建引擎(默认不注册任何预设,按需注册)
const engine = new CrawlerEngine({
  maxConcurrency: 2,
  logLevel: "info",
})

// 2. 配置 LLM(可选,用于语义增强)
if (process.env.LLM_API_KEY) {
  engine.useGenericLLM(
    process.env.LLM_API_KEY,
    process.env.LLM_BASE_URL || "https://api.deepseek.com/v1",
    "deepseek-chat"
  )
}

// 3. 注册行业预设数据源
await engine.registerTaxPresets()          // 税务
// await engine.registerEducationPresets()  // 教育
// await engine.registerSocialSecurityPresets() // 社保

// 4. 或注册自定义数据源
engine.registerSource(myCustomSource)

// 5. 从文件/URL 加载配置
// await engine.registerSourceFromFile("./my-source.json")
// await engine.registerSourceFromUrl("https://example.com/source.json")

// 6. 初始化存储
await engine.init()

// 7. 监听新增
engine.on("item:new", (payload) => {
  console.log(`New item: ${payload.item?.title}`)
})

// 8. 执行同步
const report = await engine.syncAll()
console.log(report)
// {
//   totalNew: 5,
//   totalChanged: 2,
//   autoApproved: 4,
//   pendingReview: 1,
//   errors: 0,
//   durationMs: 3200
// }

按行业/分类同步

// 按分类同步
const report = await engine.syncByCategory("education")

// 按国家同步
const report = await engine.syncByCountry("中国")

// 同步单个数据源
const result = await engine.syncSource("china-tax-announcements")

Source Config DSL

核心概念:一个数据源就是一个完整的配置对象,包含传输、爬取、提取、审查的全套规则。

完整示例:中国教育部

const educationSource: SourceConfig = {
  id: "china-education-policy",
  name: "研招网 — 考研政策",
  country: "中国",
  language: ["zh-CN"],
  category: "education",           // 行业分类
  
  // ── 传输配置 ──
  transport: {
    baseUrl: "https://yz.chsi.com.cn",
    rateLimit: { requestsPerMinute: 20, concurrency: 2 },
    retry: { maxRetries: 3, backoff: "exponential", baseDelayMs: 1000, retryOnStatus: [429, 503, 502] },
    userAgent: { strategy: "rotate" },
    timeout: 15000,
    // proxy: "http://127.0.0.1:7890",   // 代理配置
    // cookies: "session=xxx",             // 登录态 Cookie
  },

  // ── 爬取策略 ──
  scraper: {
    type: "http",              // http | rss | browser | api
    entryPoints: [
      { url: "https://yz.chsi.com.cn/kyzx/kydt/" },
    ],
    pagination: {
      type: "css",
      selector: ".pageBox a.next",
      maxPages: 3,
    },
    // 详情页二级爬取:跟进每个链接获取全文
    detailLink: {
      selector: "a[href*='content']",
      attribute: "href",
      type: "url",
      transform: ["absoluteUrl"],
    },
    incremental: true,         // 增量模式:跳过已处理的 URL
    preprocess: {
      removeSelectors: ["script", "style", ".footer"],
    },
  },

  // ── 提取规则 ──
  extractor: {
    strategy: "hybrid",        // css | llm | hybrid

    cssRules: {
      container: ".news-list li a",
      fields: {
        title: { selector: "$self", type: "text", required: true, transform: ["trim"] },
        url: { selector: "$self", attribute: "href", type: "url", transform: ["absoluteUrl"] },
      },
    },

    llmRules: {
      enabled: true,
      promptTemplate: "education-policy-extraction",  // 教育专用模板
      condition: "always",
      fields: [
        { name: "educationType", description: "教育类型", type: "enum", enumValues: ["postgraduate", "undergraduate_upgrade", "cet"] },
        { name: "examName", description: "考试名称", type: "string" },
        { name: "importantDates", description: "关键日期", type: "array" },
      ],
    },
  },

  dedup: {
    strategies: [
      { type: "url", priority: 1 },
      { type: "title-fingerprint", priority: 2 },
    ],
    ttl: 365,
  },

  schedule: {
    cron: "0 9,15 * * *",
    timezone: "Asia/Shanghai",
    enabled: true,
  },
}

注册方式

// 方式1:代码内声明
engine.registerSource(mySource)

// 方式2:从 JSON 文件加载
await engine.registerSourceFromFile("./my-source.json")

// 方式3:从远程 URL 加载
await engine.registerSourceFromUrl("https://config.example.com/source.json")

// 方式4:使用内置行业预设
await engine.registerTaxPresets()
await engine.registerEducationPresets()
await engine.registerSocialSecurityPresets()
await engine.registerAllPresets()  // 全部加载

管线生命周期

每次 syncSource() 调用会经过以下阶段:

| 阶段 | 组件 | 说明 | |------|------|------| | 1. Scrape | HttpScraper / RssScraper / BrowserScraper | 爬取列表页,支持分页 | | 2. Detail Crawl | Pipeline (Optional) | 跟进详情页链接获取全文 | | 3. Extract | CssExtractor / HybridExtractor | 提取结构化数据 | | 4. Normalize | Normalizer | 标准化(日期/URL/空白字符) | | 5. Validate | Validator | 数据校验,过滤无效 | | 6. Dedup | Deduplicator | 三级去重(URL/标题/内容) | | 7. Diff | DiffEngine | 与历史快照对比 | | 8. Review | ReviewGate | 置信度评分,自动/人工分流 | | 9. Store | StorageAdapter | 持久化 + 更新快照 | | 10. Notify | EventEmitter | 触发事件通知 |

事件系统

engine.on("item:new", async (payload) => {
  await db.insert(policyUpdates).values({ ... })
})

engine.on("item:changed", async (payload) => {
  // 记录变更历史
})

engine.on("review:needed", async (payload) => {
  await sendNotif(`New item needs review: ${payload.item?.title}`)
})

提取策略

CSS 提取器(免费,95%+ 场景)

LLM 提取器(付费,边缘场景)

Hybrid 混合提取器(推荐)


内置预设数据源

税务

| ID | 名称 | 国家 | |----|------|------| | china-tax-announcements | 国家税务总局 | 中国 | | us-irs-news | IRS News | 美国 | | uk-hmrc-news | HMRC News | 英国 | | hk-ird-press-releases | 香港税务局 | 香港 | | sg-iras-news | IRAS | 新加坡 | | jp-nta-news | 日本国税庁 | 日本 | | oecd-tax-rss | OECD Tax RSS | 国际 |

教育

| ID | 名称 | 国家 | |----|------|------| | china-education-policy | 研招网 | 中国 | | us-education-policy | US Dept of Education | 美国 |

社保

| ID | 名称 | 国家 | |----|------|------| | china-social-security-policy | 人社部/医保局 | 中国 | | us-social-security-policy | SSA & Medicare | 美国 | | jp-social-security-policy | 厚生劳动省 | 日本 |


API 参考

CrawlerEngine

| 方法 | 说明 | |------|------| | syncAll(opts?) | 同步所有启用数据源(支持 { dryRun: true }) | | syncSource(id, opts?) | 同步单个数据源(支持 { dryRun: true }) | | syncByCountry(country, opts?) | 按国家同步 | | syncByCategory(category, opts?) | 按行业分类同步 | | registerSource(config) | 注册数据源(含配置合法性校验) | | registerSources(configs) | 批量注册 | | unregisterSource(id) | 注销数据源 | | registerSourceFromFile(path) | 从 JSON 文件加载 | | registerSourceFromUrl(url) | 从远程 URL 加载 | | registerTaxPresets() | 注册税务预设 | | registerEducationPresets() | 注册教育预设 | | registerSocialSecurityPresets() | 注册社保预设 | | registerAllPresets() | 一次性加载所有预设 | | startAutoSchedule(opts?) | 开启自动调度(支持 webhook 回调) | | stopAutoSchedule() | 关闭自动调度 | | tick() | 手动触发一轮调度 | | getStats() | 获取引擎运行统计(分类/国家分布、指标) | | getSources() | 获取所有注册的数据源 | | useDeepSeek(key) | 设置 DeepSeek | | useOpenAI(key) | 设置 OpenAI | | useGenericLLM(key, url, model?) | 设置兼容 OpenAI 的 LLM | | setStorage(adapter) | 设置存储适配器 | | init() | 初始化(建表/连接) | | shutdown() | 停止引擎(关闭调度、断开存储) | | on(event, handler) | 事件监听 |

Events

| 事件 | 触发时机 | |------|---------| | source:start | 数据源开始同步 | | source:done | 数据源同步完成 | | source:error | 数据源同步出错 | | item:new | 发现新增条目 | | item:changed | 发现变更条目(内容更新) | | review:needed | 条目需要人工审核 | | sync:complete | 所有数据源同步完成 |