Skill · 本地数据源获取引擎 v2.1
定位:本地优先的数据获取层——统一查询本地 SQLite 数据源(网页库 / Skill 库),命中即返回;未命中则远程抓取并入库(缓存优先,远程兜底) 触发词:「抓取」「查本地」「查数据」「获取 XX 内容」「批量抓取」 固化指令:任何「抓取/搜索/查找 XX」且需获取内容时,自动调用本 Skill——先查本地数据源,未命中再远程抓取并入库。 前置条件:httpx / parsel 已安装;LLM 提取需 embedding 引擎(可选) 职责边界:语义层(brain / memory / lcm_grep)由小夏直接调用;本引擎负责文档/数据层获取。
一、核心接口(原有)
scrape(url) → dict
单页抓取,同步接口,Windows 兼容。
from crawler_core import scrape
data = scrape('https://example.com/chapter/1.html')
# 返回: title / h1 / desc / content / images / links / url / hash / length
bfs_scrape(start_url, config=None) → dict ⭐ NEW
BFS 深度爬取:自动从目录页发现所有章节 URL,零手动配置。
from crawler_core import bfs_scrape, BFSConfig
# 方式一:默认配置(max_depth=2, max_total=1000)
result = bfs_scrape('https://example.com/book/1234/')
links = result['links'] # 去重后的章节 URL 列表
stats = result['stats'] # 统计信息
visited = result['visited_urls'] # 全部已访问 URL(调试用)
# 方式二:自定义配置
config = BFSConfig(
max_depth=3, # 最大爬取深度(默认2)
max_total=500, # 全局 URL 上限(默认1000)
url_pattern=r'\.html$', # 仅爬 .html 页面
exclude_dirs=['/ads/', '/tag/'], # 排除含这些词的 URL
)
result = bfs_scrape('https://example.com/book/1234/', config=config)
print(f"发现 {len(result['links'])} 个章节")
# links 可直接传入 batch_scrape()
batch_scrape(result['links'], 'output.txt')
返回 stats 字段说明:
| 字段 | 含义 |
|------|------|
| total_discovered | 全部发现的 URL 数量 |
| dir_pages_visited | 访问的目录页数量 |
| chapter_candidates | 发现的章节候选数(去重前)|
| final_links | 去重后章节数(传入 batch_scrape 的数量)|
| duplicates | 重复 URL 数量 |
| elapsed | 总耗时(秒)|
| max_total_hit | 是否达到全局上限 |
BFSConfig 参数说明:
| 参数 | 默认值 | 说明 |
|------|--------|------|
| max_depth | 2 | 最大爬取深度(避免太深)|
| max_total | 1000 | 全局 URL 上限(防止失控)|
| max_per_page | 100 | 单页最大提取链接数 |
| rate_limit | True | 是否启用自适应速率限制 |
| domain | 自动 | 限制爬取的域名(默认从 start_url 提取)|
| url_pattern | None | URL 白名单正则(示例:r'\.html$')|
| exclude_dirs | [] | 排除的目录关键字列表 |
scrape_to_db(url) → dict
抓取 + 自动去重入库(SQLite + FTS5)。
from crawler_core import scrape_to_db
data = scrape_to_db('https://example.com/page.html')
# _source: 'fresh' 新抓取 / 'cache' 命中缓存
batch_scrape(links, output_path, ...) → dict
批量抓取唯一写入口。自动处理:去重 + 正序排序 + 断点续传 + 正文清洗 + 进度报告 + 自适应速率 + 多分块策略 + LLM 驱动提取(可选)。
from crawler_core import batch_scrape, sort_links
# ① 获取章节链接(方式一:BFS 自动发现)
from crawler_core import bfs_scrape
result = bfs_scrape('https://example.com/book/1234/')
links = result['links']
# 方式二:用户直接提供链接
links = [...]
# ② 正序排列(URL 数字升序)
links = sort_links(links)
# ③ 批量抓取
result = batch_scrape(
links=links,
output_path='books/小说名_整理版.txt',
checkpoint_path='books/小说名.checkpoint',
batch_report=50,
content_min_len=200,
header='《小说名》\n作者:XXX\n共 N 章\n================\n\n',
rate_limit=True,
chunk_strategy='by_size',
chunk_size=50,
llm_extract_types=['entities', 'keywords'], # 可选
)
print(result)
# {'total': 100, 'success': 99, 'failed': 1,
# 'elapsed': 600.0, 'output_size': 5242880,
# 'chunks_count': 2, 'chunk_size': 50}
llm_extract(url, content, extract_types=None) → dict
LLM 驱动的结构化数据提取(v1.3)。
from crawler_core import llm_extract
result = llm_extract(
url='http://example.com/chapter/1.html',
content='张三先生是一位武林高手...',
extract_types=['entities', 'keywords', 'summary']
)
# 返回: {url, entities:[{entity,type,count}], keywords:[{keyword,score}], summary}
extract_entities(text, max_entities=20) → list[dict]
独立调用:抽取人物 / 作品 / 地名实体(regex + 频次统计)。
extract_keywords(text, top_k=10, ngram_range=(2,3)) → list[dict]
独立调用:抽取关键词(依赖 embedding 引擎,未装返回空)。
scrape_many(urls, concurrency=3) → list[dict]
并发批量抓取(无文件写入)。
add_task(url) → bool
唤起迅雷下载。
from thunder_bridge import add_task
add_task('https://目标文件链接')
二、辅助函数
sort_links(links, reverse=False) → list[str]
URL 列表去重 + 正序排列(按 URL 数字 ID 升序)。
clean_chapter_content(text) -> str
标准正文清洗:去开头分隔线 + 清章末网站 UI + 去章内广告 + 合并空行。
extract_url_id(url) -> int
从 URL 提取数字 ID(用于排序)。
chunk_by_size(links, chunk_size=50) -> list[list[str]]
按 size 分块(v1.2 · 借鉴 Crawl4AI chunk_strategy)。
estimate_total_chunks(links, chunk_size=50) -> dict
预估分块统计信息。
scrape_with_retry(url, max_retry=2) -> dict | None
带自动重试的单页抓取(内容 <200 字自动重试)。
三、速率控制
RateLimiter
自适应速率限制器:连续成功逐步加速(最快 0.2s),连续失败逐步减速(最长 5s)。
ProgressReporter
每 N 章结构化进度报告:含速度 / ETA / 失败详情。
四、存储结构
data/crawler_db.sqlite3(本地文件,不上传):
| 表 | 用途 |
|----|------|
| crawl_history | URL / 标题 / 正文 / 图片 / 链接 |
| crawl_fts | FTS5 全文检索索引 |
| dl_tasks | 迅雷下载任务记录 |
| crawl_checkpoints | 批量抓取断点续传记录 |
from crawler_db import CrawlerDB
db = CrawlerDB()
db.search('关键词') # FTS5 检索
db.stats() # 统计
db.load_checkpoints() # 获取已完成 URL
db.save_checkpoints_batch(['/path/1.html'])
db.clear_checkpoints() # 清空断点(慎用)
五、配置
config.yaml:
thunder:
path: "D:\\软件安装\\Thunder\\Program\\Thunder.exe"
crawler:
timeout: 30
user_agent: "Mozilla/5.0 ..."
六、版本变更
v2.0 (2026-08-02) · 转型本地数据源获取引擎
| 变更 | 说明 |
|------|------|
| 定位转型 | 从「网页爬虫」重构为「本地优先的数据获取层」 |
| 新增 local_engine.py | 统一查询接口 query_local() + 数据源路由 + 缓存优先抓取 fetch_or_cache() |
| 数据源路由 | web(crawler_db) / skill(skill_reference.db),可扩展注册 |
| 职责边界 | 语义层(brain/memory/lcm)由小夏直调,本引擎聚焦文档/数据层 |
| 清理 | 删除 tests/ 临时文件 + crawl_shiri 项目残留脚本 |
v2.1 (2026-08-02) · 数据源扩展 + 自动兜底
| 变更 | 说明 |
|------|------|
| 新增数据源 | workspace(WorkspaceSource) / memory(MemorySource) / brain(BrainSource) 经 register_source() 接入 |
| query_local 新增 auto_remote | 本地全空且 query 为 URL 时,自动 fetch_or_cache() 兜底 |
| 职责边界调整 | memory/brain 现已可统一查询,语义层仍建议小夏直调优先 |
v3.0.1 (2026-08-05) · P0 四件套 · 反爬+决策矩阵+fit_markdown+设备模拟
| 变更 | 说明 | 来源 |
|------|------|------|
| 新增 anti_detection.py | 反爬 stealth 策略 + 决策矩阵 + 设备模拟(PC/iPhone/Android)| 借鉴 playwright-scraper-skill stealth JS |
| 新增 fit_markdown.py | LLM 友好 Markdown 输出,trafilatura 主路 + readability 备路,省 80% token | 借鉴 crawl4ai-skill fit_markdown |
| 新增 enhanced_tools.py | sitemap 发现 + DuckDuckGo 搜索 + XPath/CSS/Regex 三模提取 | 借鉴 crawl4ai-skill + smart-crawler |
| 新增决策矩阵 get_crawl_method() | 普通→httpx / 动态渲染→Playwright Simple / Cloudflare→Stealth | 借鉴 playwright-scraper-skill |
| 新增 generate_playwright_init_script() | Playwright 注入 JS 隐藏自动化特征(webdriver/navigator/permissions)| 借鉴 playwright-scraper-skill |
| 新增 CrawlConfig 配置类 | level/device/delay/retry/timeout 统一配置,含 CrawlConfig.auto(url) 自动探测 | 内联设计 |
| 修复 trafilatura v2.2 API 兼容 | reset_counter 在 v2.2 移除,改为 extract 直调 | 实测踩坑修复 |
| 补装 readability-lxml | QClaw Python 统一依赖环境 | 环境规范化 |
九、本地数据源引擎(核心 v2.0)
定位落地:crawler-assistant 转型为「本地优先的数据获取层」的核心实现。统一管理本地 SQLite 数据源,提供统一查询 + 缓存优先抓取 fallback。
query_local(query, sources=None, n=10, merge=True, auto_remote=False) → dict
统一本地查询入口,自动路由到最优本地源并融合。
from local_engine import query_local, engine_stats
# 默认轻量查询(web + skill)
r = query_local('合同审查', n=5)
# {'total': 12, 'results': [{...含 _source 标记...}]}
# 扩展源查询(含 workspace/memory/brain)
r = query_local('十日终焉', sources=['web','skill','memory'], n=5)
# 空结果自动远程兜底(query 为 URL 时 fetch_or_cache)
r = query_local('https://example.com/page', auto_remote=True)
# {'total': N, 'results': [...], '_auto_remote': True}
# 引擎统计(全源)
print(engine_stats())
# {'web': {...}, 'skill': {...}, 'workspace': {...}, 'memory': {...}, 'brain': {...}}
数据源路由表(已注册):
| key | 数据源 | 后端 | 内容 |
|-----|--------|------|------|
| web | WebCrawlSource | crawler_db.sqlite3 | 已抓取网页/文档 |
| skill | SkillRefSource | skill_reference.db | GitHub Skill 文档 |
| workspace | WorkspaceSource | workspace 文件 Glob | 工作区 .md/.txt/.py |
| memory | MemorySource | memory/*.md | 小夏记忆文件 |
| brain | BrainSource | gbrain CLI 桥接 | 个人知识库 |
扩展点:workspace / memory / brain 已通过 register_source() 接入(v2.1),query_local 无需改动即可查询新源。
fetch_or_cache(url, db_path='data/crawler_db.sqlite3', force=False) → dict
缓存优先的远程抓取 fallback:命中 crawler_db 缓存返回 cache,未命中则 scrape_to_db 抓取入库 fresh。这是「本地优先,远程兜底」的落地函数。
engine_stats(sources=None) → dict
引擎统计:各本地数据源状态。
与语义层职责边界:brain / memory / lcm_grep 属语义记忆层。本引擎 v2.1 已支持统一查询 workspace/memory/brain 数据源,但语义层仍建议优先由小夏直接调用(brain-ops 协议);引擎层面提供统一入口,便于 Agent 一站式获取本地数据。lcm_grep 为 qclaw 内置会话检索,不在本引擎范围内。
十、Skill 参考数据库模块(新增 v1.7)
定位:从 GitHub clawhub 仓库爬取 Skill 文档与代码,构建本地参考数据库,供 Agent/Skill 升级时查阅对比。
新增模块 skill_hub.py
| 接口 | 说明 |
|------|------|
| build_skill_reference() | 核心入口,从 GitHub clawhub 全量抓取 Skill |
| build_skill_reference_sync() | 同步包装,Windows 直接调用 |
| search_skills(query) | FTS5 全文检索 Skill 参考库 |
| get_skill(slug) | 获取某 Skill 的所有文件内容 |
| list_all_slugs() | 列出数据库中所有 Skill slug |
| db_stats() | 数据库统计(Skill 数/文件数/大小) |
数据源:
- GitHub:
openclaw/clawhub仓库.agents/skills/.codex/.config目录 - 抓取方式:GitHub REST API(
api.github.com),无需 Token(匿名 60 req/h)
数据库:data/skill_reference.db,SQLite + FTS5 |
from crawler_assistant.scripts.skill_hub import (
build_skill_reference_sync,
search_skills,
get_skill,
db_stats,
)
# 构建参考数据库(全量首次 ~10min,增量约2min)
result = build_skill_reference_sync()
# {'new': 300, 'skipped': 0, 'error': 2, 'db': 'data/skill_reference.db'}
# FTS5 全文检索
results = search_skills('合同审查')
for r in results:
print(f"[{r['slug']}] {r['name']}")
print(f" snippet: {r['snippet']}")
# 获取某 Skill 全部文件
files = get_skill('autoreview')
for f in files:
print(f"{f['file_path']} | {len(f['content'])} chars")
# 数据库统计
print(db_stats())
# {'skills': 120, 'files': 380, 'db_size_mb': 4.2}
抓取范围:
| 目录 | 说明 |
|------|------|
| .agents/skills | OpenClaw Skills 主目录 |
| .codex | Codex Skills |
| .config | 配置类 Skill |
增量策略:首次全量;后续运行时比对 SHA,只抓变更文件,不重复下载。
常见障碍:
| 障碍 | 解决方案 |
|------|---------|
| raw.githubusercontent.com 被封锁 | 无影响,走 GitHub API 返回 base64 内容 |
| 腾讯 SkillHub JS 渲染 | 暂不支持,需后续探明内部 API |
| GitHub API rate limit | 自动等待 60s 后重试;匿名 60次/h |
v1.6 (2026-07-31) · BFS 根 Bug 修复 · 正式发布
| Bug | 根因 | 修复 |
|-----|------|------|
| Event loop is closed(BFS 中批量失败)| httpx 全局 _client 跨 loop 缓存,loop.close() 后状态丢失 | 删除全局 client,fetch() 改为 async with httpx.AsyncClient() 单次创建用完即关 |
| extract_links 丢失所有相对路径链接 | href.startswith('http') 过滤掉 ../xxx.html 等相对路径 | 改为 urljoin(base_url, h) 后直接收集,不做 http 前缀过滤 |
| BFS 发现 0 章节(_is_dir_page 过度保守)| 默认返回 True(其他全目录),.html 后缀无法区分 | 反转默认假设:默认章节页(False),只在明确目录特征时返回 True |
真实验收数据(Python 官方文档):
- BFS depth=1, max_total=20:
166 个章节发现,9 个目录页访问,19.6s - 内容验证(3 章节):title/content_len 全正确
v1.4 (2026-07-31) · 借鉴 Crawl4AI · P2 BFS 深度爬取
| 变更 | 说明 | 来源 |
|------|------|------|
| 新增 bfs_scrape() | BFS 自动发现章节 URL,无需手动配置目录 | 借鉴 Crawl4AI BFS 深度爬取 |
| 新增 BFSConfig | BFS 配置类(深度/上限/域名/白名单/黑名单)| 借鉴 Crawl4AI config 理念 |
| 新增 _is_dir_page() | 启发式判断 URL 是目录页还是章节页 | 内联实现,无外部依赖 |
| 目录页/章节页自动分流 | BFS 队列只进目录页,章节页直接收集 | 借鉴 Crawl4AI 爬取策略 |
| 零手动配置 | bfs_scrape(url) 即可自动发现全部章节 | 极简用户接口 |
P2 典型工作流:
用户输入: 抓取 https://example.com/book/1234/
↓
工具调用: bfs_scrape('https://example.com/book/1234/')
↓ 发现 1000 个章节,访问 20 个目录页
↓
batch_scrape(links, 'output.txt')
↓
完成!
v1.3 (2026-07-31) · 借鉴 Crawl4AI · P1 LLM 驱动提取
| 变更 | 说明 | 来源 |
|------|------|------|
| 新增 llm_extract() | 结构化数据提取(entities/keywords/summary)| 借鉴 Crawl4AI schema extraction |
| 新增 extract_entities() | 人物/作品/地名实体独立抽取 | 借鉴 entity extraction |
| 新增 extract_keywords() | 关键词+权重(复用 embedding 引擎 n-gram)| 借鉴 keyword extraction |
| 新增 llm_extract_types 参数 | batch_scrape 可选触发 LLM 提取 | 借鉴 extract_types |
| 零新增强依赖 | embedding 未装时降级,不阻断主流程 | 轻量内联设计 |
v1.2 (2026-07-31) · 借鉴 Crawl4AI · P0 分块策略
| 变更 | 说明 |
|------|------|
| 新增 chunk_by_size() | 按 size 分块 |
| 新增 estimate_total_chunks() | 分块预估统计 |
| 升级 batch_scrape() | 新增 chunk_strategy + chunk_size 参数 |
| 保持向后兼容 | 默认 chunk_strategy='by_id',与 v1.1 一致 |
v1.1 (2026-07-30)
| 变更 | 说明 |
|------|------|
| 新增 batch_scrape() | 批量抓取唯一写入口 |
| 新增 clean_chapter_content() | 标准正文清洗 |
| 新增 sort_links() | URL 正序排列 |
| 新增 RateLimiter | 自适应速率控制 |
| 新增 ProgressReporter | 结构化进度报告 |
| 新增 crawl_checkpoints 表 | 断点记录 |
七、借鉴映射表
| Crawl4AI 能力 | 本 Skill 对应实现 | 版本 |
|--------------|-----------------|------|
| 多分块策略(chunk_strategy)| chunk_by_size() | v1.2 |
| LLM 驱动结构化提取 | llm_extract() + extract_entities/keywords | v1.3 |
| BFS 深度爬取 | bfs_scrape() + BFSConfig | v1.4 |
设计哲学:Crawl4AI(GitHub 76k stars)是 AI 网页爬虫标杆。本 Skill 借鉴其三大核心能力,保持轻量内联架构——不引入 Docker / Playwright / 外部 API 依赖,所有能力零外部依赖可独立运行。
八、文件清单
skills/crawler-assistant/
├── SKILL.md ← 本文件(v3.0)
├── config.yaml ← 迅雷路径配置
├── scripts/
│ ├── local_engine.py ← 【v2.0 核心】本地数据源统一引擎
│ ├── anti_detection.py ← 【v3.0】反爬 stealth + 决策矩阵 + 设备模拟
│ ├── fit_markdown.py ← 【v3.0】LLM 友好 Markdown 输出
│ ├── enhanced_tools.py ← 【v3.0】sitemap + DuckDuckGo + XPath 三模提取
│ ├── crawler_core.py ← 抓取核心(BFS/批量/清洗)
│ ├── crawler_db.py ← 数据持久层(crawler_db.sqlite3)
│ ├── skill_hub.py ← Skill 参考数据库(skill_reference.db)
│ └── thunder_bridge.py ← 迅雷协议唤起
└── tests/
├── test_chunk_strategy.py
├── test_skill_hub.py
└── test_skill_hub_write.py
十一、依赖
| 包 | 用途 | 状态 | |----|------|------| | httpx | HTTP 请求 | 必须 | | parsel | HTML 解析 | 必须 | | aiohttp | 异步并发 | 必须 | | PyYAML | 解析 SKILL.md frontmatter | 必须(已内置)| | trafilatura | LLM 友好 Markdown 正文提取 | 必须(v3.0 新增)| | readability-lxml | HTML 正文提取(备路)| 必须(v3.0 新增)| | embedding 引擎 | n-gram 关键词抽取 | 可选(未装降级)|
pip install httpx parsel aiohttp
# PyYAML 为 Python 内置,无需单独安装
微信扫一扫