转转(58 同城旗下二手平台)无官方公开商品详情 API ,主流采集方案为逆向 App / 网页接口(JSON 数据直采) 与 网页爬虫,以下从接口分析、Python 代码、反爬、合规、主题文章全维度拆解,可直接落地。
一、转转商品数据采集核心方案(API / 爬虫二选一)
方案 1:逆向 App 接口(推荐,数据全、效率高、稳定)
转转 App 采用加密 JSON 接口,直接返回结构化商品数据(标题、价格、成色、SKU、描述、卖家信息等),无 HTML 解析损耗,是工业级采集首选。
- 核心接口(抓包获取)
- 商品列表接口:
- 商品详情接口:
- 搜索接口:
- 关键参数:
itemId(商品 ID)、cityId(城市)、categoryId(分类)、page/pageSize(分页) - 加密特征:请求头含
zz-token、sign、timestamp、deviceId,参数需验签(MD5 + 固定密钥)
方案 2:网页爬虫(简易,适合小规模 / 测试)
转转 Web 端商品页为动态渲染,数据嵌在 HTML 或异步 JS 接口,适合小批量采集。
- 列表页:
- 详情页:
- 数据特点:价格、标题、成色、地区可 XPath 提取,描述、图片需解析 JS 或异步接口。
二、转转商品详情接口字段(逆向 App 返回 JSON 结构)
json
{
"code": 200,
"msg": "success",
"data": {
"itemId": "123456789", // 商品唯一ID
"title": "95新 iPhone 14 128G 紫色",
"price": "3299", // 售价
"originalPrice": "5999", // 原价
"quality": "95新", // 成色:99新/95新/9成新/8成新/7成新
"qualityDesc": "屏幕细微划痕,功能全好",
"categoryName": "手机",
"brand": "Apple",
"model": "iPhone 14",
"area": "北京 朝阳区", // 地区
"sellerName": "转转优品",
"sellerLevel": "钻石卖家",
"sellerScore": 4.9,
"publishTime": "2026-03-20 14:30",
"browseCount": 1280, // 浏览量
"wantCount": 86, // 想要人数
"desc": "国行全原装,无拆无修,电池健康88%...", // 商品描述
"images": [ // 商品图片(多图)
"https://img.zhuanzhuan.com/xxx/1.jpg",
"https://img.zhuanzhuan.com/xxx/2.jpg"
],
"attributes": [ // 规格属性
{"name": "容量", "value": "128GB"},
{"name": "颜色", "value": "紫色"},
{"name": "保修", "value": "过保"}
],
"service": ["正品保障", "7天无理由", "顺丰包邮"] // 服务标签
}
}
三、Python 采集实战代码(App 接口 + 网页爬虫双版本)
版本 1:App 接口采集(核心代码,含签名 / 请求头)
依赖:pip install requests
python
运行
import requests
import hashlib
import time
import json
# 转转App基础配置(抓包获取)
BASE_URL = "https://appapi.zhuanzhuan.com"
APP_KEY = "zz_app_android" # 固定
APP_SECRET = "xxxxxx" # 抓包/逆向获取密钥
DEVICE_ID = "8675xxxxxx" # 设备ID(抓包)
# 生成签名(转转官方算法)
def generate_sign(params: dict) -> str:
# 1. 参数按key字典排序
sorted_params = sorted(params.items())
# 2. 拼接参数串 + 密钥
param_str = "".join([f"{k}{v}" for k, v in sorted_params])
sign_str = f"{APP_SECRET}{param_str}{APP_SECRET}"
# 3. MD5加密转大写
return hashlib.md5(sign_str.encode("utf-8")).hexdigest().upper()
# 获取商品详情(App接口)
def get_zz_item_detail(item_id: str) -> dict:
api_path = "/api/zz/item/detail"
timestamp = str(int(time.time()))
# 业务参数
params = {
"itemId": item_id,
"cityId": "110100", # 北京
"appKey": APP_KEY,
"timestamp": timestamp,
"deviceId": DEVICE_ID
}
# 生成签名
params["sign"] = generate_sign(params)
# 请求头(抓包完整复制)
headers = {
"User-Agent": "ZhuanZhuan/10.0.0 (Android 13; Xiaomi 13)",
"Content-Type": "application/x-www-form-urlencoded",
"zz-token": "your_zz_token", # 登录token(抓包)
"Accept-Encoding": "gzip"
}
try:
resp = requests.get(BASE_URL + api_path, params=params, headers=headers, timeout=10)
resp.raise_for_status()
return resp.json()
except Exception as e:
return {"error": f"请求失败:{str(e)}"}
# 测试调用
if __name__ == "__main__":
item_id = "123456789" # 替换真实商品ID
detail = get_zz_item_detail(item_id)
print(json.dumps(detail, ensure_ascii=False, indent=2))