7.1 KiB
continuation.md
checkpoint@ 2026-07-17 07:51
当前状态
| 项目 | 值 |
|---|---|
| 新闻源 | 14 个(13 Web + 1 API: xwlb 新闻联播) |
| Qdrant | 本地文件模式 data/qdrant_storage/ |
| 日报 | 5 板块: AI摘要 / 新闻联播 / 财经新闻 / 公告调研 / 数据总览 |
| 调度器 | APScheduler,systemd a-share-research.service |
| LLM | deepseek-v4-flash(绝不允许擅自修改) |
| 服务器 | pi@192.168.1.160,项目 /home/pi/news/ |
| 抓取方式 | js_render=false → httpx 直连;js_render=true → Playwright |
本次完成 (2026-07-17) — 禁用个股日报
操作: STOCK_REPORT_TIME= 设为空,run_scheduler.py 加空值守卫。
文件:
scripts/run_scheduler.pyL127-141:STOCK_REPORT_TIME为空时跳过注册并输出已禁用个股日报- Pi
.env:STOCK_REPORT_TIME=08:00→STOCK_REPORT_TIME=
恢复方法: 改回 STOCK_REPORT_TIME=08:00,重启 a-share-research。
本次完成 (2026-07-13) — eeo 反爬修复 + 静态直连 + 超时保护
问题
eeo(经济观察网)在 Pi 上用 Playwright 连续抓取后触发反爬,服务器故意不响应导致 page.goto() 超时(30s → 60s 均超时)。而在本机 Mac 上一切正常——反爬是 IP 级别的,Pi 的 IP 已被限流。
根因
eeo 是纯静态页面(js_render: false),但 Crawl4AI 始终走 Playwright。eeo 检测到 headless Chrome 的连续请求模式后,对后续请求挂起 TCP 连接直至超时。
修复
crawler/engine.py — 三个改动:
-
新增
_fetch_static():js_render=False且无wait_for的源用httpx直连,绕过 Playwright 反爬。HTML → Markdown 用markdownify+ BeautifulSoup 清洗。 -
新增
_TimeoutGuard:同源连续超时 2 次后自动跳过剩余请求,防止整个 pipeline 被单一故障源拖死。 -
_crawl_once分流逻辑:js_render=False 且无 wait_for → _fetch_static() (httpx) 其他 → Playwright
configs/sources.yaml — eeo 源两处调整:
- 删除
wait_for: "css:body"(静态页面无需等待 CSS 选择器) page_timeout_ms: 30000 → 60000(恢复 Crawl4AI 默认值)
测试结果
| 环境 | Playwright (修复前) | httpx (修复后) |
|---|---|---|
| Mac 本地 | 全部通过 | 36 篇 / 7 秒 |
| Pi 服务器 | 前 3 个请求全部 15s 超时 | 36 篇 / 9 秒,0 失败 |
验证方法
Pi 上本地复现了反爬(Playwright 前 3 个 URL 全部超时),同批 URL 用 httpx 全部瞬间返回——证明方案有效。
历史 (2026-06-24)
1. 环境变量全面审计与修复
触发: 服务器 dedup 步骤超时 120s。
修复 6 个文件:
| # | 文件 | 修改 |
|---|---|---|
| 1 | scheduler/pipeline.py |
超时解析重写:TIMEOUT_{NAME} > PIPELINE_STEP_TIMEOUT > 硬编码 > 1800s |
| 2 | a_share_cli/main.py |
新增 _resolve_timeout(),CLI 命令超时改为读环境变量 |
| 3 | crawler/cninfo.py |
CNINFO_PDF_BASE 从硬编码改为 os.environ.get() |
| 4 | scheduler/reporter.py |
STOCK_REPORT_DAYS 默认值 7→15;max_tokens 600→1500 |
| 5 | .env |
清除 10 个死配置;EMBEDDING_MODEL→LOCAL_EMBEDDING_MODEL;补全遗漏参数 |
| 6 | .env.example |
重写为仅包含实际生效配置 |
审计结果:
| 类别 | 数量 | 处理 |
|---|---|---|
| ✅ 正常工作 | 23 | — |
| ❌ 死配置 | 10 | 已清除 |
| ⚠️ 默认值不一致 | 1 | 已统一 |
| 🔧 硬编码 | 1 | 已修复 |
2. AI 摘要截断修复
触发: 日报 AI 摘要内容极少(137 字),末尾被截断。
根因: _llm_call() max_tokens=600 太低,deepseek-v4-flash 输出被硬截断。
修复: max_tokens 三处提升 + 截断检测日志。
| 位置 | 修复前 | 修复后 |
|---|---|---|
_llm_call() 默认 |
600 | 1500 |
| 分块摘要 | 400 | 800 |
| 合并摘要 | 600 | 1500 |
验证: 手动生成日报,摘要从 137 字 → 486 字,结构完整无截断。
超时优先级(最终设计)
TIMEOUT_{NAME} 环境变量 ← 单步精确控制
↓ 未设
PIPELINE_STEP_TIMEOUT 环境变量 ← 全局兜底 (覆盖所有硬编码)
↓ 未设
STEP_TIMEOUTS 硬编码字典 ← 代码内默认值
↓ 步骤不在字典中
1800s ← 最终兜底
服务器 .env 当前配置
# ---- 调度 ----
SCHEDULE_TIMES=07:00,12:00,18:00,22:00
CNINFO_SCHEDULE_TIME=06:00
STOCK_REPORT_TIME=08:00
STOCK_REPORT_DAYS=15
# ---- Pipeline 超时 ----
PIPELINE_STEP_TIMEOUT=3600
TIMEOUT_CRAWLER=900
TIMEOUT_XWLB=60
TIMEOUT_EXTRACTOR=300
TIMEOUT_DEDUP=900
TIMEOUT_LLM=900
TIMEOUT_EMBEDDING=900
TIMEOUT_QDRANT=1800
TIMEOUT_CNINFO_CRAWL=900
TIMEOUT_CNINFO_EXTRACT=900
TIMEOUT_CNINFO_PDF=900
调度器
| 时间 | 步骤 |
|---|---|
| 06:00 | cninfo 公告管道 |
| 07:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant→日报 |
| 08:00 | 个股日报 |
| 12:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant |
| 18:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant |
| 22:00 | crawler→xwlb→extractor→dedup→llm→embedding→qdrant |
常用命令
# 全链路 + 日报
uv run a-share pipeline --once --report
# 仅日报 (指定日期)
/home/pi/.local/bin/uv run python -m scripts.run_scheduler --once --date 20260623 --steps report
# 检索
uv run a-share search "关键词" --top 5
# 同步到服务器
scp <file> pi@192.168.1.160:/home/pi/news/<path>/
# 重启服务
ssh pi@192.168.1.160 "sudo systemctl restart a-share-research"
已知技术债务
| 问题 | 文件 | 状态 |
|---|---|---|
| QdrantClient HTTP 超时硬编码 10s | vectorstore/client.py:93 |
无可配环境变量 |
| DashScope HTTP 超时硬编码 60s | embedding/remote.py:81 |
无可配环境变量 |
| 超时解析逻辑在 pipeline.py 和 main.py 各一份 | 两处 | 可抽取公共函数 |
deepseek-v4-flash 概率性返回空 |
— | 分块处理 + 失败跳过 |
| wallstreetcn JS 渲染拦截 | — | 正文提取率 0% |
| eeo 列表页仍走 Playwright(仅文章页走 httpx) | crawler/engine.py |
列表页目前正常,暂不改动 |
架构备忘:静态/动态抓取分流
SourceConfig.js_render == False 且 wait_for == None
→ _fetch_static() → httpx 直连 (无浏览器, 快, 不被反爬)
→ markdownify + BeautifulSoup (清洗 script/style 后转 Markdown)
SourceConfig.js_render == True 或 wait_for 有值
→ _crawl_once() → Playwright (现有逻辑)
_TimeoutGuard:
- 同源连续超时 2 次 →
skip=True - 后续请求在
semaphore内检查guard.skip,立即返回Skipped - 一次成功即重置计数器
记忆
never-change-llm-model.md— 绝不允许修改大模型配置deploy-html.md— user_guide.html 同步到 Pi 和 Web 服务器sync-sources-yaml.md— 修改 sources.yaml 前从 Pi 拉取,改完推回update-user-guide.md— 更新 md+html+部署