SandiRidwan/tiktok-hotel-search-aggregator
GitHub: SandiRidwan/tiktok-hotel-search-aggregator
该项目通过捕获 TikTok 移动端会话,批量抓取应用内 Places 搜索中的酒店列表和多家 OTA 平台的实时价格数据,存入 SQLite 并导出 CSV。
Stars: 0 | Forks: 0
      
完整演示 —— 关键词运行、实时仪表盘、最终数据集
| 指标 | 值 |
|-------:|:------|
| 🔑 已处理关键词 | **1,617 / 1,617 — 100% 完成** |
| 🏨 目标 | TikTok 搜索 → "Places" 标签页(酒店/住宿结果) |
| 💰 已捕获价格来源 | Trip.com · Agoda · Traveloka · tiket.com · Rakuten Travel |
| 🌏 覆盖范围 | 印度尼西亚城市 + 部分亚洲地区(甲米、横滨、清迈、乔治市) |
| 🔐 会话方法 | mitmproxy 流量捕获 → 真实的 `sessionid` / `device_id` / `install_id` |
| 🧩 签名层 | 本地 FastAPI RPC 签名服务器——作为轻量级签名替代方案,并非密码学破解 |
| 🗄️ 存储 | SQLite WAL 模式 · 基于 `(poi_id, merchant)` 去重 · 支持按关键词检查点/恢复 |
| 📊 实时监控 | 终端仪表盘,3秒刷新,检查进度无需停止爬虫 |
| 📁 导出 | 一键 CSV 导出,完整数据集,随时可交付给客户 |
## 🏗️ 架构
```
┌──────────────────────────────────────────────────────────────────────┐
│ SIGN SERVER (FastAPI · :8080) │
│ POST /sign → { X-Gorgon, X-Khronos } per request │
└──────────────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ MASS SCRAPER (tiktok_mass_scraper) │
│ real session (sessionid/device_id from mitmproxy capture) │
│ + signed headers from Sign Server │
│ → GET search/place/?keyword=...&offset=N │
│ → loop offset until empty response │
└──────────────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ SQLite (WAL mode) │
│ data_hotel — hotel + merchant price rows, UNIQUE(poi_id, merchant) │
│ progres_keyword — checkpoint table, resume-safe on restart │
└──────────────────────────────┬───────────────────────────────────────┘
│
┌────────────┴─────────────┐
▼ ▼
monitor_live.py export_csv.py
(3s refresh dashboard) (full dataset → CSV)
```
## ⚡ 解决的技术挑战
### 挑战 1 — TikTok 的私有移动 API 没有公开文档
**问题:** TikTok 应用内搜索的 "Places" 标签页并不是公开的 API——既没有文档,也没有 SDK,而且请求需要浏览器爬虫根本不具备的设备身份和签名 header。
**解决方案:** 在 Android 设备/模拟器上手动执行特定的用户操作(搜索酒店关键词,点击 Places 标签页)时,使用 **mitmproxy** 捕获真实的 App 流量。这为我们揭示了真实的 endpoint、确切的查询参数以及有效的会话——这是构建一切其他功能的基础。
```
# 从拦截的流量中捕获的 session 值 — 而非猜测。
# 通过 environment variables 替换为你自己捕获的值,切勿硬编码。
SESSION_ID = os.getenv("TIKTOK_SESSION_ID")
HEADERS = {
"Cookie": f"sessionid={SESSION_ID}; install_id=...; store-country-code=id;",
"User-Agent": "
| 层级 | 技术 |
|-------|------------|
| **语言** | Python 3.12 |
| **流量捕获** | mitmproxy — 提取真实的会话/设备身份 |
| **签名层** | FastAPI — 本地 RPC 签名服务器 |
| **HTTP 客户端** | requests,附带真实捕获的会话 header |
| **数据库** | SQLite — WAL 模式,检查点-恢复模式 |
| **监控** | 自定义终端仪表盘(3秒轮询) |
| **导出** | csv 模块 — 按需全量数据集导出 |
## 📝 经验教训
1. **对于某些 endpoint,会话身份胜过签名复杂性** — 一个真实的 `sessionid` 和设备指纹比格式完美但虚假的签名具有更高的信任度。在寻求最困难的解决方案之前,务必先检查系统实际验证的是什么。
2. **mitmproxy 是了解未公开移动 API 的最快途径** — 观察一个真实的请求胜过数小时的猜测参数名称。
3. **诚实对待技术范围很重要** — 将签名替代方案如实标注(而非密码学破解),可以在客户或审查者要求技术细节时使工作经得起推敲。
4. **检查点表挽救了长时间运行的任务** — 运行中期的单一 schema 不匹配并没有导致从零开始重启;检查点重置使得恢复过程自动化了。
5. **实时仪表盘在长任务中物超所值** — 无需为了检查进度而暂停长达数天的抓取工作。
## 👤 作者
标签:Python, SQLite, 命令控制, 数据采集, 无后门, 逆向工具, 酒店数据