上海网上房地产数据采集器 · 瑞数 6 代(RS6)后缀池方案
Hot path ~21ms · Suffix Pool · Multi-lane session rotation · One-command CSV export
English · 中文 · How it works · Disclaimer
| 痛点 | 这个仓库怎么解 |
|---|---|
瑞数 6 直接 requests 只能拿到 412 |
Cookie 对 + 一次性 URL 后缀自动搞定 |
| 每次都开浏览器 / Playwright,慢到没法用 | 常驻 Node worker + 后缀池,热路径中位 21ms |
| 后缀「用一次就废」 | 一次 VM 批量掐断 XHR,白拿 200 张未使用的票 |
| 不知道怎么采全站数据 | python collect_all.py → 11 个接口,350+ 行 CSV |
这不是「再包一层 Selenium」。核心是:票不发出去,服务端就不知道它存在 —— 再把票摊薄到池子里供热路径消费。
fangdi-collector collects public housing stats from Shanghai's fangdi.com.cn behind Ruishu / River Security 6.
- Resident Node worker runs the site's own challenge JS in
sdenv - Python keeps a multi-lane suffix pool so business calls stay ~21ms
FangdiClientAPI +collect_all.pyfor one-shot CSV export
pip install -r requirements.txt
cd worker && npm install && cd ..
python start_worker.py # optional but recommended
python example.py
# or dump everything:
python collect_all.pyfrom fangdi import FangdiClient
import config as C
with FangdiClient(paths=C.ALL_PATHS, batch=5) as fd:
for e in fd.get_all_estates(): # ~97 projects, no filters
print(e["districtname"], e["project"], e["leaving_num"])上海「网上房地产」(www.fangdi.com.cn)的数据采集工具。 站点有瑞数 6 代防护,直接请求业务接口只会拿到 412。 本工具自动处理 Cookie + 一次性后缀,让你像调普通 API 一样拿数据。
业务接口 30/30 通过
单次 中位 21.5ms p95 23.0ms 最快 18.5ms 最慢 25.2ms
热路径同步补池次数: 0
一次采齐(collect_all.py)约 7 秒产出 350+ 行,覆盖楼盘 / 板块 / 成交 / 排行等。
- Python 3.10+
- Node.js 18+(
node -v检查)
git clone https://github.com/lyt26/fangdi-collector.git
cd fangdi-collector
pip install -r requirements.txt
cd worker && npm install && cd ..python start_worker.py平时约 0.8s 起来。npm install 后第一次可能要更久(杀软扫依赖),只发生一次。
python example.py
# 或一键全量 CSV:
python collect_all.py输出目录默认 ./output/,UTF-8 BOM,Excel 可直接打开。
网页上的「区域 / 面积 / 均价 → 不限」是前端筛选。 服务端这些接口大多不接收筛选参数,直接返回全量。
所以「什么都不限制」= 不传参数直接调。
from fangdi import FangdiClient
import config as C
with FangdiClient(paths=C.ALL_PATHS, batch=5) as fd:
estates = fd.get_all_estates() # ~97 个楼盘
regions = fd.get_all_regions() # ~121 个板块(districtID="")
pudong = [e for e in estates if e["districtname"] == "浦东新区"]| 方法 | 内容 |
|---|---|
get_all_estates() |
全市楼盘清单 |
get_all_regions() |
全部板块字典 |
get_district_bargain() |
各区在售(站点接口本身约 5s) |
get_more_district_bargain() |
各区成交 |
get_sell_upcoming() |
即将上市 |
get_seven_days_ranking() |
近七日排行 |
get_house_sold_stat() / get_sh_sold_stat() |
一手 / 存量可售统计 |
get_sh_uptown_clinch() / get_sh_clinch_total() |
小区成交 / 汇总 |
get_index_message() |
首页汇总 |
完整字段与排错见下文,或直接读 fangdi.py / config.py。
FangdiClient(
paths=C.ALL_PATHS, # 必须提前声明要采的接口(票与路径绑定)
lanes=2, # 独立会话轮转数
batch=200, # 一次挑战每接口出多少张票
)| 方法 | 说明 |
|---|---|
request(path, data=None) |
打任意已声明的受保护接口 |
list_districts() / list_projects() |
首页数据拍平 |
status() |
池子状态;waits 正常应为 0 |
close() |
释放后台线程(with 自动调用) |
加新接口三步:
- F12 → Network 抄下
.action路径(去掉?XJlCTRRM=...) - 写进
config.py的常量并加入ALL_PATHS fd.request(C.PATH_MY_NEW, data={...})
打业务接口必须同时满足:
- Cookie:
UA1L1zGonajvO(服务端发)+UA1L1zGonajvP(客户端算) - URL 一次性参数:
?XJlCTRRM=...(约 119 字符) - 该参数从未被用过
类比:Cookie = 身份证;后缀 = 一次性门票。
| 规则 | 结果 |
|---|---|
| 票用过就废 | 第二次 → 400 |
| 票和 Cookie 绑定 | 串会话 → 400 |
| 票和接口路径绑定 | 串路径 → 400 |
[造会话] 首页 412 → 拿到 O Cookie + 挑战
Node 跑原版 JS:算 P Cookie + 连开 N 次 XHR 并掐断
→ 白拿 N 张服务端没见过的票
[热路径] 取票 + Cookie → HTTP 业务请求 ≈ 21ms
再叠加:
- 多会话轮转(lanes)
- 后台预补(低于水位 / 快过期时提前建替补)
于是热路径几乎永远不卡:status()["waits"] == 0。
88 字节后缀全部受完整性保护,目前做不到纯代数拼装;必须跑一次原版 JS 批量收票。摊薄后每票约 2.8ms 量级。
fangdi-collector/
├── fangdi.py # 对外 API(日常只用这个)
├── collect_all.py # 一键采齐 → CSV
├── example.py # 最小示例
├── test_acceptance.py # 验收
├── start_worker.py # 启停常驻 worker
├── config.py # 站点 / 池子 / 接口常量
├── client.py # 挑战 + HTTP
├── pool.py # 后缀池
├── worker/ # Node 计算核(sdenv)
├── docs/banner.png # GitHub 封面
└── output/ # CSV 输出(本地生成,不入库)
python test_acceptance.py 30期望:全部 200、中位 ~21ms、waits=0。
| 现象 | 处理 |
|---|---|
worker 依赖没装 |
cd worker && npm install |
| 启动失败 | 看 worker.log;Node ≥ 18;改 WORKER_PORT |
| 全 400 | 路径没写进 paths / Cookie 与票不匹配 |
| 全 412 | 挑战失败或站点改版 → 清 js_cache/ 重跑 |
waits 持续 > 0 |
调大 batch 或 lanes |
- 新接口必须进
paths(否则自动整池重建,慢约 1s) getDistrictBargain站点本身约 5s(与本工具无关);可用首页housingSupply代替- 无服务端筛选 / 翻页 —— 拉全量后本地筛
- 默认单出口 IP;大规模请自行给 session 挂代理
- 会话 TTL 保守 150s(
SESSION_TTL_SEC) - 请控制频率,别打挂目标站
本项目仅供学习研究瑞数类防护的交互模型、会话绑定与性能优化。
- 数据来自公开页面接口;请遵守目标站点服务条款与当地法律法规
- 禁止用于未授权大规模爬取、倒卖数据或干扰服务
- 作者不对使用本工具造成的任何后果负责
Use at your own risk. Educational / research purposes only.
If this saved you a weekend of fighting 412 pages — ⭐ Star it.
如果你也烦透了 412 / 慢自动化,点个 Star 让更多人看见这套后缀池思路。
