爬虫文档.md 19 KB

TG Lead Scraper 系统指南

最后更新: 2026-04-08 代码位置: 23.95.10.148:/opt/tg-lead-scraper/ 前端位置: /Users/admin/claude/tg-lead-scraper-frontend/


一、系统概览

一句话: 从 Telegram 频道/网页/搜索结果/GitHub 自动挖掘商户联系方式,清洗去重验证,最后按质量打分。

核心能力: 输入是"种子频道"和"关键词",输出是可直接外呼的商户清单(带 TG 用户名、网站、邮箱、电话、行业标签、质量分)。


二、数据模型(输入 → 输出)

输入源
├── managed_seeds      TG 频道起点(当前: 1 个 bbs3000)
├── managed_keywords   搜索关键词(当前: 108 个)
└── industry_rules.yaml  行业分类规则
              ↓
   7 阶段 Pipeline 处理
              ↓
中间表
├── channels       发现到的 TG 频道(当前: 1907+ 条)
├── nav_sites      候选导航网页(当前: 646 条)
└── merchants_raw  待清洗商户(当前: 1946 条)
              ↓
   cleaner 三关过滤
              ↓
最终输出
└── merchants_clean  可用商户(valid/invalid/bot/duplicate/group)

三、7 阶段 Pipeline 完整流程

全景图

Phase 1 discover    → Phase 2 search   → Phase 3 github
    ↓                     ↓                    ↓
  (snowball 裂变)      (关键词搜索)         (GitHub README 挖)
    ↓                     ↓                    ↓
  channels 表        nav_sites + channels    channels 表
    ↓                     ↓                    ↓
            ┌─────────────┴──────────────┐
            ↓                            ↓
Phase 4 scrape                    Phase 5 crawl
 (TG 消息抓取)                     (网页爬取)
            ↓                            ↓
      merchants_raw              merchants_raw
            ↓                            ↓
            └──────────┬─────────────────┘
                       ↓
            Phase 6 clean (清洗三关)
                       ↓
            merchants_clean
                       ↓
            Phase 7 score (打分)
                       ↓
         merchants_clean.quality_score

Phase 1: discover (频道发现)

  • 文件: core/snowball.py
  • 做什么: 从种子频道出发,裂变发现更多相关频道

流程:

  1. managed_seeds 拿起点(比如 @bbs3000
  2. 用 TG 账号进入每个种子,读最近 100 条消息
  3. 提取消息里的 forward_from 和 TG 推荐频道
  4. 把新发现的频道当第二层种子继续裂变
  5. 最多 3 层(max_depth),每层 200 个上限,全局 500 个上限(防指数爆炸)
  6. 每个频道间 sleep 5 秒

输入: managed_seeds + TG 账号 输出: 写入 channels 表,source='seed' 或 'snowball' 瓶颈: 单账号 FloodWait(历史上裂变 200 个就被限速)

Phase 2: search (搜索引擎)

  • 文件: core/search_engine.py
  • 做什么: 用关键词去 Google 搜索,把结果里的 TG 频道和导航站分拣入库

流程:

  1. managed_keywords 拿 108 个关键词(如"机场推荐"、"发卡网")
  2. 对每个关键词调 Serper API(Google Search)
  3. 翻页多次,每页 10 条结果
  4. 识别结果 URL:
    • t.me/xxx → 写 channels
    • 是导航站(domain 含 nav/list/catalog)→ 写 nav_sites
    • 是博客/产品官网/社交媒体 → 丢弃
  5. 关键词间 sleep 若干秒避免限速
  6. Serper 失败可 fallback 到 DuckDuckGo

外部依赖: Serper API(当前 key 已失效,400 Bad Request) 输出: channels + nav_sites

Phase 3: github (GitHub 采集)

  • 文件: core/github_crawler.py
  • 做什么: 搜 GitHub 仓库的 README 里的 TG 链接

流程:

  1. 用 query 搜 GitHub repo(按 star 排序)
  2. 下载每个 repo 的 README.md
  3. 要求 README 前 5000 字含中文,过滤英文项目
  4. 正则匹配 t.me/xxx 链接
  5. 链接前后 200 字必须含中文才算有效(过滤通用 bot 和 proxy 频道)
  6. repo 间 sleep 2s,query 间 sleep 5s

外部依赖: GitHub Search API(无 token,rate limit 10 req/min) 输出: channels 表,source='github'

Phase 4: scrape (TG 消息采集) ← 最慢最贵的阶段

  • 文件: core/scraper.py
  • 做什么: 真正进入 TG 频道,读历史消息,提取商户

流程:

  1. channels 表拿 status='pending' 的频道
  2. 对每个频道:
    • get_entity(channel_username) 解析频道
    • GLM 相关性评估:频道名+简介+成员数传给 GLM 判"是不是商户相关",不相关直接 skip
    • 读频道简介(about)
    • 读置顶消息(limit=20)
    • 遍历历史消息(limit=500,断点续传用 last_message_id
  3. 每条消息:
    • MessageService 系统消息跳过
    • 非中文跳过
    • 先用正则 extract_contacts_enhanced 快速判是否含联系方式
    • 有联系方式 → 调 GLM 精准解析 提取 merchant(failover 到正则)
    • 写入 merchants_raw
  4. 消息间 sleep delay_message,频道间 sleep delay_channel

输入: channels 表 + managed_settings.tg_scraper.* 输出: merchants_raw 瓶颈: 单账号每 200-500 次 get_entity 触发 FloodWait 10-24 小时

Phase 5: crawl (网页爬取)

  • 文件: core/web_crawler.py + core/nav_filter.py
  • 做什么: 爬导航站,从网页 HTML 提取商户

流程:

  1. nav_sites 表拿 status='pending' 的网页
  2. 预过滤 rule_filter(url)
    • 黑名单域名(t.me/twitter/google 等 80 个)→ filtered
    • 黑名单扩展名(.apk/.zip/.pdf 等 40 种)→ filtered
    • 黑名单路径(/api//login/?ref= 等)→ filtered
    • 正向信号(含 nav/directory/catalog)→ valid
    • 都不确定 → 弱候选,进 GLM 二次过滤
  3. GLM 二次过滤:问 GLM "这个 URL 是不是导航站",置信度 ≥0.6 才放行
  4. 对通过的网页:
    • 用 requests → cloudscraper → playwright 三层 fallback 抓 HTML
    • HTML 前 5000 字非中文直接跳过
    • BeautifulSoup 解析出商户链接(CSS 选择器可配置)
  5. 对每个商户链接:
    • 如果直接带 @tg_username → 走 TG 入库路径
    • t.me 死号预检(新加的):抓 t.me/{username} 网页,没头像就 drop
    • 活号 → 写 merchants_raw
    • 如果只有网站链接 → 爬商户首页 + /contact/about 等子页,用 extractor 提取联系方式

输入: nav_sites输出: merchants_raw

Phase 6: clean (数据清洗) ← 三关过滤

  • 文件: core/cleaner.py
  • 做什么: 对 raw 商户做黑名单过滤、去重、真实性验证

流程:

第一关: _filter_blacklist(本地,秒级)

  • 黑名单 username(26 个系统 bot + xxxbot 后缀)→ 标 bot
  • 邀请链接哈希(16-24 位 base64 + 高熵)→ 标 invalid
  • original_message 非空且不含中文 → 标 invalid

第二关: _deduplicate(本地,秒级)

  • 同 username 多条记录,按信息丰富度打分(有 website/email/phone 加分)
  • 保留最丰富的一条,副本迁 clean 桶标 duplicate
  • 合并所有 source 链接到 keeper

第三关: _verify_merchant(最贵,TG API)

  • 调 Telethon client.get_entity(username) 去 TG 服务器验证
  • 返回 User 且非 bot → valid(顺手拿 first_name/last_name/is_premium/last_online/active_level)
  • 返回 Bot → bot
  • 返回 Channel/Chat → group
  • UsernameNotOccupied → invalid
  • FloodWait ≤60s → 重试
  • FloodWait >60s → 切账号
  • FloodWait >300s → 直接 break 整轮(修复后)

输入: merchants_raw status='raw' 输出: merchants_clean (valid/invalid/bot/duplicate/group) 瓶颈: 同 Phase 4,TG API 节流严

Phase 7: score (商户评分)

  • 文件: core/scorer.py
  • 做什么: 对 clean 桶的商户按 6 维度加权打分

6 个维度(总权重 1.0):

维度 权重 规则
member_count 0.25 <100→10 / <1k→30 / <1w→50 / <10w→80 / ≥10w→100
premium 0.15 是 TG Premium→100,不是→0
activity 0.25 active→100 / moderate→50 / inactive→20
multi_source 0.20 被多个来源发现→100 / 3+→70 / 2→40 / 1→10
has_website 0.10 有→100,没有→0
has_email 0.05 有→100,没有→0

可选第 7 维: GLM 内容质量打分(默认关闭,因为 GLM API 会 hang)

输入: merchants_raw + merchants_clean 两表 输出: 写 merchant.quality_score(0-100)


四、核心模块对照表

模块 文件 主要职责
snowball core/snowball.py Phase 1 频道裂变
search_engine core/search_engine.py Phase 2 Serper/DuckDuckGo 搜索
github_crawler core/github_crawler.py Phase 3 GitHub README 挖 TG 链接
scraper core/scraper.py Phase 4 TG 消息采集
web_crawler core/web_crawler.py Phase 5 导航站爬取
nav_filter core/nav_filter.py 导航站识别过滤器
cleaner core/cleaner.py Phase 6 清洗三关
scorer core/scorer.py Phase 7 评分
extractor core/extractor.py 联系方式提取(正则+GLM)
classifier core/classifier.py 行业分类(关键词+GLM)
account_manager core/account_manager.py 多 TG 账号轮换 + FloodWait 管理
pipeline core/pipeline.py Pipeline 状态管理
task_manager core/task_manager.py 任务调度 + 看门狗 + 断点续传
config_service core/config_service.py 配置服务 (managed_settings)
database core/database.py ORM + promote_merchant helper
tme_validator core/tme_validator.py t.me 网页死号预检(新加)

五、具体能实现的功能清单

📥 数据采集能力

  • ✅ 从种子 TG 频道裂变发现新频道(snowball,最多 3 层,500 个上限)
  • ✅ 用关键词从 Google 搜索引擎发现 TG 频道和导航网页
  • ✅ 从 GitHub 仓库 README 挖 TG 链接
  • ✅ 抓取 TG 频道历史消息(支持断点续传,单频道最多 500 条)
  • ✅ 抓取 TG 频道简介 + 置顶消息
  • ✅ 爬取导航网站 HTML(三层 fallback: requests / cloudscraper / playwright)
  • ✅ 爬取商户官网 + 常见子页(/contact, /about, /关于我们

🧠 智能识别能力

  • ✅ GLM 频道相关性评估(过滤掉不是商户的频道)
  • ✅ GLM 消息商户解析(提取非标准格式如"加V:xxx"、"t点me/xxx")
  • ✅ GLM 行业分类(机场/发卡/成人等,可配置)
  • ✅ GLM 导航站识别(弱候选 URL 交 GLM 判断)
  • ✅ 正则提取联系方式(TG 用户名 / t.me 链接 / 邮箱 / 电话 / 网址)
  • ✅ 中文检测(非中文消息/商户名直接跳过)
  • ✅ 联系意图识别(文本里含客服/购买/咨询等关键词标记)

🧹 数据清洗能力

  • ✅ 26 条系统 bot 黑名单过滤
  • ✅ 邀请链接哈希识别(base64 + 高熵检测)
  • ✅ 80+ 域名黑名单(社交媒体/大站/政府站)
  • ✅ 40+ 扩展名黑名单(apk/zip/pdf 等非网页资源)
  • ✅ 非中文内容过滤
  • ✅ 同 username 去重 + 信息合并
  • t.me 网页死号预检(新加,16% 死号率,100% 准确)
  • ✅ Telethon 真实性验证(拿头像/显示名/premium/最后在线时间)
  • ✅ FloodWait 智能处理(短等待重试,长等待切账号,超 300s 跳过)

📊 评分与分类

  • ✅ 6 维度加权打分(成员数/Premium/活跃度/多来源/网站/邮箱)
  • ✅ 行业标签(关键词匹配 + GLM)
  • ✅ 活跃度分级(active <3 天 / moderate / inactive >30 天)
  • ✅ Premium 用户识别

🎛️ 运维能力

  • ✅ 多 TG 账号轮换(account_manager)
  • ✅ FloodWait 自动切换账号
  • ✅ 代理池(proxy_mgr,给 GitHub/requests 用)
  • ✅ 断点续传(channels.last_message_id
  • ✅ 任务状态机(pipeline_state.json 单一权威)
  • ✅ 任务 stop / force_stop 接口
  • ✅ 每阶段独立运行(任意阶段都能单独跑)
  • ✅ 全链路 pipeline(7 阶段自动调度)
  • ✅ skip_phases 参数(跳过指定阶段)
  • ✅ 测试模式(item_limit / message_limit 限流)
  • ✅ 实时日志 + 进度回调
  • ✅ 任务并发保护(同类型不能叠跑)

🖥️ 前端能力

  • ✅ 任务启动/停止/预览
  • ✅ 数据总览仪表盘
  • ✅ 采集数据表格(默认按 created_at desc)
  • ✅ 种子频道管理(增删改查)
  • ✅ 候选网页管理
  • ✅ 系统配置(种子 / 关键词 / 流水线阶段 tab)
  • ✅ 系统状态(运行日志 / 监控)
  • ✅ 任务历史(7 个阶段独立按钮)

🗄️ 数据管理

  • ✅ merchants_raw / merchants_clean 桶分离
  • ✅ Merchant 视图兼容(只读 UNION)
  • ✅ promote_merchant helper(原子跨桶迁移,保留 id)
  • ✅ managed_settings 11 条运行参数(hot-reload 和 new_task 两种生效级别)
  • ✅ config_revisions 审计日志
  • ✅ 自动备份(backup/ 目录 14+ 份历史快照)

六、可用任务类型(API /api/tasks/start

task_type 用途 独占
full 跑完整 pipeline(7 阶段) 是(全局空闲才能跑)
discover Phase 1 单跑
search Phase 2 单跑
github Phase 3 单跑
scrape Phase 4 单跑
crawl Phase 5 单跑
clean Phase 6 单跑
score Phase 7 单跑

参数:

  • target: 可选目标(频道名 / 关键词)
  • test_run: 测试模式
    • item_limit: 每轮处理条数上限
    • message_limit: 每频道消息上限
  • skip_phases: 每任务覆盖全局默认的 skip_phases

七、外部依赖

依赖 用途 状态
Telethon + TG API Phase 4 / Phase 6 account_01/02 FloodWait 10-20h
Serper API (Google Search) Phase 2 key 失效,400 错误
GitHub Search API Phase 3 无 token,10 req/min
GLM API 频道评估 / 消息解析 / 行业分类 / 导航站判断 正常
t.me 网页 死号预检 正常,无限速
requests / cloudscraper / playwright Phase 5 正常

八、当前 managed_settings(11 条)

key value type effect_level
pipeline.skip_phases ["scrape"] json new_task
pipeline.checkpoint_interval 30 int runtime
tg_scraper.message_limit_per_channel 500 int runtime
tg_scraper.delay_per_verify 3.0 float runtime
clean.timeout_seconds 3600 int runtime
search.timeout_seconds 3600 int runtime
snowball.max_channels_per_layer 200 int runtime
snowball.max_channels_total 500 int runtime
tme_validator.enabled true bool runtime
tme_validator.rate_per_min 60 int runtime
tme_validator.concurrency 10 int runtime

effect_level 说明:

  • runtime: 改了立即生效
  • new_task: 只对下一个新任务生效

九、当前数据基线 (2026-04-08)

记录数
managed_seeds 1(bbs3000
managed_keywords 108
managed_settings 11
channels 1907+
nav_sites 646(pending=0, scraped=156, filtered=490)
merchants_raw 1946(raw=1571, group=308, glm_parsed=67)
merchants_clean 125(valid=62, invalid=34, duplicate=27, bot=2)
scored 商户 448

十、已知限制

  1. 单账号/双账号 TG API 限速严重 — Phase 4/6 容易卡 13-20 小时
  2. Serper API key 失效 — Phase 2 完全失效
  3. GitHub 无 token — Phase 3 rate limit 10 req/min
  4. cleaner _verify_merchant 没有独立节流 — 是触发 FloodWait 的根因
  5. raw 桶新数据(web 来源)merchant_name 污染 — site_scraper 把 HTML 标签写入 merchant_name

十一、系统能力边界(不能做什么)

  • ❌ 不能绕过 TG 限速(必须等 FloodWait 或加账号)
  • ❌ 不能解析 TG 加密群组内容(只能公开频道)
  • ❌ 不能抓 TG Premium 专享内容
  • ❌ 不能识别图片/视频里的联系方式(只处理文本)
  • ❌ 不能通过 TG 建立会话发消息(纯读)
  • ❌ 不能处理 JS 渲染的单页应用(除非 Playwright fallback)
  • ❌ 不能自动注册 TG 账号(需要手机号+验证码)

十二、2026-04-07/08 修复的 9 个 bug

# 修复 commit
1 crawl GLM 超时 + 进度日志 6380f43
2 cleaner FloodWait 无限 sleep 6380f43
3 score use_glm 强制 True hang 6380f43
4 merchants 默认排序按 created_at desc 04f2d7e
5 cleaner 非中文规则误杀 web 数据 625b0e3
6 config.py bootstrap GET 回填 21e6e29
7 snowball 加每层 200 + 总数 500 上限 50b580d
8 t.me 死号预检模块 027895a
9 tme_validator lint 修复 f0456c7

十三、运维与回滚

服务器信息

  • IP: 23.95.10.148 (RackNerd VPS)
  • SSH: root / 4e8F2McWxRC7iEa0b4
  • 代码: /opt/tg-lead-scraper/
  • 虚拟环境: venv/
  • 服务: tg-lead-scraper-api (systemd)
  • 对外端口: 8134 (Nginx 反代 → 127.0.0.1:8900)
  • API 认证: admin / admin (Basic Auth)

日志位置

  • Pipeline 执行日志: /opt/tg-lead-scraper/logs/scraper_YYYY-MM-DD.log
  • HTTP 日志: journalctl -u tg-lead-scraper-api
  • Pipeline 状态: /opt/tg-lead-scraper/data/pipeline_state.json

DB 备份

位于 /opt/tg-lead-scraper/backup/:

  • leads_pre_refactor_20260407_151503.db — 7 批重构前
  • leads_pre_batch5_20260407_162334.db — 表拆分前
  • leads_pre_rollback_20260408_041132.db — cleaner 规则回滚前
  • leads_pre_dead_purge_20260408_094432.db — 死号清理前
  • seeds_backup_20260407_142211.json/.sql — 原 17 个种子专项备份
  • seeds_full_snapshot_20260407_142211.db — 种子专项全库快照

前端部署

cd /Users/admin/claude/tg-lead-scraper-frontend
npm run build
rsync -az --delete dist/ root@23.95.10.148:/var/www/tg-lead-scraper/

Git tag

  • v7-crawl-clean-score-fixes — 2026-04-08 凌晨修复基线

紧急回滚

# 后端代码回滚到 tag
cd /opt/tg-lead-scraper && git reset --hard v7-crawl-clean-score-fixes
systemctl restart tg-lead-scraper-api

# DB 回滚
cp backup/leads_pre_refactor_20260407_151503.db data/leads.db

# 前端回滚
cd /Users/admin/claude/tg-lead-scraper-frontend
git reset --hard <tag>
npm run build
rsync -az --delete dist/ root@23.95.10.148:/var/www/tg-lead-scraper/

十四、开发规范(重要)

写入 merchant 的规则

  • 新增 merchant → 只能用 MerchantRaw(...),不能用 Merchant(...)
  • 修改 merchant status 跨桶(raw→valid)→ 用 promote_merchant() helper
  • 修改 merchant status 桶内(raw→glm_parsed)→ 直接改对应桶 ORM 对象
  • 按 id 查询未知桶 → 用 merchant_by_id(session, id)
  • 只读聚合查询session.query(Merchant)(指向视图,零改动)

Commit 规范

git -c user.email=refactor@local -c user.name=refactor commit -m "..."

配置权威

  • 种子: managed_seeds 表(不是 config.yaml)
  • 关键词: managed_keywords
  • 运行参数: managed_settings 表(通过 /api/config/settings 改)
  • config.yaml 里的 seed_channels/keywords 已废弃,启动时打警告