Metadata-Version: 2.4
Name: yh-crawler
Version: 0.2.2
Summary: YunHou Document Crawler
Author: YunHou
License: MIT
Project-URL: Homepage, https://github.com/jkt-check/yunhou-crawler
Project-URL: Repository, https://github.com/jkt-check/yunhou-crawler
Project-URL: Issues, https://github.com/jkt-check/yunhou-crawler/issues
Project-URL: Changelog, https://github.com/jkt-check/yunhou-crawler/blob/master/CHANGELOG.md
Keywords: crawler,scrapy,ai,rag,knowledge-base,documentation
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Scrapy
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: scrapy>=2.11.0
Requires-Dist: playwright>=1.40.0
Requires-Dist: scrapy-playwright>=0.0.33
Requires-Dist: patchright>=1.60.0
Requires-Dist: pypinyin>=0.49.0
Requires-Dist: browserforge>=1.2.0
Requires-Dist: msgspec>=0.21.0
Requires-Dist: trafilatura>=1.12.0
Requires-Dist: lxml>=4.9.0
Requires-Dist: scrapling>=0.4.8
Requires-Dist: litellm>=1.40.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: tldextract>=5.0.0
Requires-Dist: w3lib>=2.1.0
Requires-Dist: aiohttp[speedups]>=3.9.0
Requires-Dist: curl_cffi>=0.5.10
Requires-Dist: fake-useragent>=1.4.0
Provides-Extra: shield
Requires-Dist: ddddocr>=1.4.0; extra == "shield"
Provides-Extra: test
Requires-Dist: pytest>=7.0.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
Requires-Dist: responses>=0.24.0; extra == "test"
Dynamic: license-file

# YunHou Document Crawler

[![PyPI version](https://img.shields.io/pypi/v/yh-crawler.svg)](https://pypi.org/project/yh-crawler/)
[![CI status](https://github.com/jkt-check/yunhou-crawler/actions/workflows/ci.yml/badge.svg)](https://github.com/jkt-check/yunhou-crawler/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![codecov](https://codecov.io/gh/jkt-check/yunhou-crawler/branch/master/graph/badge.svg)](https://codecov.io/gh/jkt-check/yunhou-crawler)

> **English**: see [README.en.md](./README.en.md) for the full English documentation.

通用文档知识库爬虫系统，支持多目标网站配置化抓取。

## 功能特性

- **配置驱动**: YAML 定义目标网站的 URL 规则、内容选择器、深度限制等
- **智能分析**: LLM 自动分析网站结构，生成抓取策略
- **增量抓取**: 智能内容哈希比对，仅处理真正变更的页面
- **反爬机制**: 随机延迟、UA 轮换、代理管理、指纹伪装
- **代理池集成**: 支持开源代理池 API（jhao104/proxy_pool 等）
- **RAG 集成**: 检测变更后可导出供向量化引擎使用

## 快速开始

### 本地开发

```bash
# 开发模式安装（可编辑）
pip install -e ".[test]"

# 安装 Playwright 浏览器
playwright install chromium

# 验证
yh-crawler --help
```

> 如果仅需作为 CLI 工具运行（不写代码），`pip install -e .` 也足够。带 `[test]` 会多装 pytest 等测试依赖，方便本地跑 `pytest tests/`。

## 核心命令

```
yh-crawler
├── ai-crawl <url>        # AI 爬虫：LLM 引导的智能抓取
│   Options: -i/--intent (必填), --max-pages,
│            --model, --thinking-level, --dry-run, -o/--output
├── analyze <url>         # LLM 分析网站并生成配置
├── init                  # 初始化工作目录（默认 ~/.yh_crawler，$YH_CRAWLER_HOME 覆盖）
├── crawl <site>          # 抓取已配置站点
├── export <site>         # 导出文档（用于 RAG）
├── export-latest <site>  # 导出最新版本文档
├── fetch <url>           # 调试：抓取单个 URL
├── shell [url]           # 调试：交互式 Shell
├── status <site>         # 查看站点状态
├── clear <site>          # 清除站点缓存
├── purge <site>          # 删除站点所有数据（存储+SQLite）
├── sites                 # 列出已配置站点
├── list                  # 列出所有 Spider
└── version               # 显示版本
```

## 作为 Python 库使用（Integrator 指南）

yh-crawler 设计为既可作为 CLI 工具使用，也可被其他 Python 应用（如 yunhou-rag）作为库依赖调用。

### 安装

```bash
# 基础安装（CLI + Python API）
pip install yh-crawler

# 阿里云 5秒盾识别（需要 ddddocr，在 Python 3.14 上无 wheel）
pip install "yh-crawler[shield]"

# 开发模式（包含 pytest 等测试依赖）
pip install -e ".[test]"
```

### 集成方需要注入的环境变量

调用 `yh-crawler` 前，集成方应在子进程 env 中注入：

| 变量 | 推荐设置 | 说明 |
|------|---------|------|
| `OPENAI_API_KEY` | 必填 | LLM API 密钥 |
| `OPENAI_BASE_URL` | 按你的 LLM 提供商 | 例如 `https://api.deepseek.com` |
| `OPENAI_MODEL` | 真实可用模型 | 不要用 base.yml 的占位符 |
| `STORAGE_BASE_DIR` | 集成方控制的目录 | 文档 + SQLite db 统一写到这。**推荐值**：`/var/your-app/yh-storage` |
| `YH_CRAWLER_HOME` | 集成方控制的配置目录 | 不设也行（会从 package 资源读默认 base.yml），但建议设置以便覆盖配置 |
| `CRAWLER_DB_PATH` | 通常不设 | 高级覆盖：直接指定 SQLite db 路径 |

### 配置路径解析优先级

`ConfigLoader` 解析 `BASE_CONFIG_PATH` 等配置路径时，按以下优先级：

1. **绝对路径** —— 直接使用（monkeypatch 或显式硬编码）
2. **`$YH_CRAWLER_HOME` + 相对路径** —— 拼接到 `$YH_CRAWLER_HOME/config/...`（推荐给集成方用）
3. **cwd 下的相对路径** —— 直接使用（dev 模式）
4. **`~/.yh_crawler/` + 相对路径** —— 用户级默认（`yh-crawler init` 的默认目标）
5. **pip 安装模式** —— `importlib.resources` 从 `config/` 包内数据读取

### Python API 示例

```python
import subprocess
import os

# 注入集成方控制的路径
env = os.environ.copy()
env["STORAGE_BASE_DIR"] = "/var/your-app/yh-storage"
env["YH_CRAWLER_HOME"] = "/var/your-app/yh-config"
env["OPENAI_API_KEY"] = "sk-..."
env["OPENAI_BASE_URL"] = "https://api.deepseek.com"
env["OPENAI_MODEL"] = "deepseek-chat"

result = subprocess.run(
    ["yh-crawler", "ai-crawl",
     "https://docs.example.com",
     "-i", "API documentation",
     "-o", "/var/your-app/yh-staging/documents"],
    env=env,
    capture_output=True, text=True,
)
# 文档输出到 /var/your-app/yh-staging/documents/*.md
# 元数据索引 /var/your-app/yh-staging/documents/metadata.json
```

## 示例工作流

```bash
# AI 爬虫：意图驱动的智能抓取
yh-crawler ai-crawl https://docs.ksyun.com -i "边缘节点计算文档"
yh-crawler ai-crawl https://help.aliyun.com -i "ECS产品文档" --max-pages 50

# 1. 分析新网站
yh-crawler analyze https://help.aliyun.com --focus "产品文档"

# 2. 抓取网站
yh-crawler crawl aliyun --limit=100

# 3. 查看状态
yh-crawler status aliyun

# 4. 导出数据
yh-crawler export-latest aliyun -o ./for_rag

# 5. 增量导出（供 RAG 更新）
yh-crawler export aliyun --latest --since-version=1
```

## 代理池配置

支持集成开源代理池 API（如 [jhao104/proxy_pool](https://github.com/jhao104/proxy_pool)）：

### 启动代理池服务

```bash
# Docker 启动代理池（建议绑定到 localhost）
docker run -d -p 127.0.0.1:5010:5010 jhao104/proxy_pool

# 或使用项目提供的脚本
./scripts/start-proxy-pool-service.sh start
```

### 配置代理池

```yaml
# config/base.yml
anti_crawler:
  proxy:
    enabled: true
    pool_api:
      enabled: true
      pool_type: jhao104        # jhao104 / scylla / flask / custom
      api_url: "http://localhost:5010"
      timeout: 10
      max_retries: 3           # API 调用重试次数
      auto_delete_failed: true # 自动删除失败代理
      allow_private_ips: false # 拒绝私有 IP（SSRF 防护，推荐）
```

### 安全说明

- `allow_private_ips: false`（默认）会拒绝所有私有 IP 地址（如 127.x, 10.x, 192.168.x）
- 这是为了防止 SSRF 攻击，代理池返回内网地址可能导致安全问题
- 生产环境建议将代理池 API 绑定到 `127.0.0.1` 而非公网

## 技术栈

- **爬虫框架**: Scrapy + scrapy-playwright
- **内容提取**: Trafilatura + CSS 选择器
- **存储**: SQLite（去重） + 文件系统
- **反爬机制**: curl_cffi 指纹伪装 + Playwright stealth
- **LLM**: OpenAI API + LiteLLM (用于网站分析和AI爬虫)
- **浏览器**: Playwright (AI爬虫页面渲染)

## 项目结构

```
.
├── cli/yh_crawler.py          # CLI 入口
├── yh_crawler/                # 🆕 顶层公共包：re-export shim（`from yh_crawler import ...`）
│   └── __init__.py            #   re-export __version__ / ConfigLoader / CrawlerDB / resolve_db_path
├── crawler/                   # Scrapy 项目（spiders / middlewares / pipelines / settings）
├── core/
│   ├── anti_crawler/          # 反爬机制（10 个模块，含 shield_solver）
│   ├── ai_crawler/            # AI 爬虫（LLM 引导，7 个模块）
│   ├── config_loader.py       # 配置加载
│   ├── config_generator.py    # 配置生成
│   ├── config_validator.py    # 配置校验
│   ├── content_extractor.py   # 内容提取 + 质量验证
│   ├── crawler_db.py          # SQLite 去重数据库
│   ├── dedup.py               # 双层去重封装
│   ├── domain.py              # 域名校验（SSRF 防护）
│   ├── evaluator.py           # 评估器
│   ├── hash.py                # 哈希计算（智能哈希 + make_filename 可读文件名）
│   ├── html_parser.py         # HTML 解析
│   ├── llm_analyzer.py        # LLM 网站分析
│   ├── llm_client.py          # LLM 客户端（LiteLLM + thinking）
│   ├── playwright_stealth.py  # Playwright 隐身脚本
│   ├── playwright_utils.py    # Playwright 渲染工具
│   ├── render_detector.py     # SSR/CSR 渲染类型检测
│   └── scrapling_renderer.py  # Scrapling 渲染器
├── config/
│   ├── base.yml               # 全局配置
│   ├── proxies/               # 代理白名单配置
│   └── sites/                 # 站点配置
├── samples/                   # analyze 命令的输出样例
├── docs/                      # 设计与计划文档
├── scripts/                   # 运维脚本（proxy 池、定时抓取、测试等）
├── storage/                   # 文档存储（SQLite schema 嵌入在 core/crawler_db.py 中）
├── tests/                     # 单元测试 + E2E 测试
├── pyproject.toml             # 🆕 唯一打包配置（替代 setup.py）
├── requirements.txt
├── pytest.ini
└── scrapy.cfg
```

## 运行测试

```bash
# 运行所有测试
pytest tests/ -v

# 仅单元测试（跳过集成 / 慢速测试）
pytest tests/ -v -m "not integration and not slow"

# E2E 测试（需网络可达 help.aliyun.com）
pytest tests/e2e/ -v --timeout=180

# 跑某个模块
pytest tests/test_hash.py tests/test_dedup.py -v
```

测试标记（`pytest.ini` 中定义）：

| 标记 | 用途 |
|------|------|
| `integration` | 集成测试（需要网络/服务） |
| `slow` | 慢速测试 |
| `e2e` | 端到端测试 |

E2E 测试位于 `tests/e2e/`，按测试类分组覆盖 CLI 基础、Analyze、Crawl、LLM API、Playwright、Scrapling 渲染器、阿里云 5秒盾抓取等完整用户流程，详见 `tests/e2e/test_real_e2e.py` 与 `tests/e2e/test_aliyun_shield_crawl.py`。

## 文档 / Documentation

- [架构说明 / Architecture](./docs/architecture.md)
- [安全说明 / Security](./docs/security.md)
- [开发指南 / Development](./docs/development.md)
- [变更日志 / Changelog](./CHANGELOG.md)
- [贡献指南 / Contributing](./CONTRIBUTING.md)

## 许可证

MIT License
