最后更新: 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)
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
core/snowball.py流程:
managed_seeds 拿起点(比如 @bbs3000)输入: managed_seeds + TG 账号
输出: 写入 channels 表,source='seed' 或 'snowball'
瓶颈: 单账号 FloodWait(历史上裂变 200 个就被限速)
core/search_engine.py流程:
managed_keywords 拿 108 个关键词(如"机场推荐"、"发卡网")t.me/xxx → 写 channels 表nav_sites 表外部依赖: Serper API(当前 key 已失效,400 Bad Request)
输出: channels + nav_sites
core/github_crawler.py流程:
t.me/xxx 链接外部依赖: GitHub Search API(无 token,rate limit 10 req/min)
输出: channels 表,source='github'
core/scraper.py流程:
channels 表拿 status='pending' 的频道get_entity(channel_username) 解析频道last_message_id)MessageService 系统消息跳过extract_contacts_enhanced 快速判是否含联系方式merchants_rawdelay_message,频道间 sleep delay_channel输入: channels 表 + managed_settings.tg_scraper.*
输出: merchants_raw
瓶颈: 单账号每 200-500 次 get_entity 触发 FloodWait 10-24 小时
core/web_crawler.py + core/nav_filter.py流程:
nav_sites 表拿 status='pending' 的网页rule_filter(url):
/api/、/login/、?ref= 等)→ filtered@tg_username → 走 TG 入库路径merchants_raw/contact、/about 等子页,用 extractor 提取联系方式输入: nav_sites 表
输出: merchants_raw
core/cleaner.py流程:
_filter_blacklist(本地,秒级)xxxbot 后缀)→ 标 botoriginal_message 非空且不含中文 → 标 invalid_deduplicate(本地,秒级)_verify_merchant(最贵,TG API)client.get_entity(username) 去 TG 服务器验证输入: merchants_raw status='raw'
输出: merchants_clean (valid/invalid/bot/duplicate/group)
瓶颈: 同 Phase 4,TG API 节流严
core/scorer.py6 个维度(总权重 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 网页死号预检(新加) |
/contact, /about, /关于我们)channels.last_message_id)pipeline_state.json 单一权威)backup/ 目录 14+ 份历史快照)/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 | 正常 |
| 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: 只对下一个新任务生效| 表 | 记录数 |
|---|---|
| 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 |
_verify_merchant 没有独立节流 — 是触发 FloodWait 的根因| # | 修复 | 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 |
root / 4e8F2McWxRC7iEa0b4/opt/tg-lead-scraper/venv/tg-lead-scraper-api (systemd)admin / admin (Basic Auth)/opt/tg-lead-scraper/logs/scraper_YYYY-MM-DD.logjournalctl -u tg-lead-scraper-api/opt/tg-lead-scraper/data/pipeline_state.json位于 /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/
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/
MerchantRaw(...),不能用 Merchant(...)promote_merchant() helpermerchant_by_id(session, id)session.query(Merchant)(指向视图,零改动)git -c user.email=refactor@local -c user.name=refactor commit -m "..."
managed_seeds 表(不是 config.yaml)managed_keywords 表managed_settings 表(通过 /api/config/settings 改)seed_channels/keywords 已废弃,启动时打警告