ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

得物App商品数据采集实战:接口解析与反爬策略

得物App商品数据采集实战:接口解析与反爬策略

1. 项目背景与核心价值

做电商数据抓取和分析的朋友应该都遇到过这样的场景:需要批量获取商品详情数据,但官方没有提供完整的API文档。这时候就需要通过技术手段解析网页接口,构建自己的数据采集方案。最近我在做得物App的商品数据采集时,就完整走通了这套流程。

得物作为国内领先的潮流电商平台,其商品数据对市场分析、价格监控、竞品研究都有重要价值。但官方并未开放完整的开发者接口,这就需要我们通过技术手段获取数据。本文将分享从接口发现、参数逆向到数据结构解析的全过程,包含大量实战中积累的经验技巧。

2. 接口发现与请求构造

2.1 接口定位方法

首先需要在App或网页端抓包找到目标接口。我推荐使用Charles或Fiddler这类专业抓包工具,配置手机代理后,在得物App浏览商品时就能捕获到相关请求。

关键识别点:

  • 接口路径通常包含"/product/detail"这类关键词
  • 请求方法多为GET或POST
  • 响应数据为JSON格式且包含完整商品信息

典型得物商品详情接口示例:

https://app.dewu.com/api/v1/product/detail?productId=123456

2.2 关键请求参数解析

通过分析多个请求样本,我总结出以下核心参数:

参数名类型必填说明示例值
productIdString是商品唯一ID"A123456"
platformString否平台标识"android"
versionString否App版本号"5.9.0"
signString是签名参数"a1b2c3d4e5"

特别注意:sign参数是接口验证的关键,需要通过逆向工程分析其生成算法。根据我的分析,得物采用的是常见的MD5加密方式,将多个参数按特定顺序拼接后加密生成。

2.3 请求头设置技巧

除了URL参数,请求头也需要特别注意以下字段:

headers = { "User-Agent": "Mozilla/5.0 (Linux; Android 10) Dewu/5.9.0", "X-Requested-With": "com.shizhuang.duapp", "Authorization": "Bearer xxxxxxx", # 登录态令牌 "Accept-Encoding": "gzip" }

实测发现,得物服务端会校验User-Agent和X-Requested-With字段,如果缺失或格式不正确会返回403错误。Authorization字段在获取某些敏感数据(如库存、价格走势)时需要提供。

3. 响应数据结构深度解析

3.1 基础商品信息结构

典型响应数据采用多层嵌套的JSON结构。以下是核心字段的解析:

{ "code": 0, "message": "success", "data": { "productId": "A123456", "title": "Nike Air Force 1 纯白", "subTitle": "2023新款 经典复刻", "price": 899, "originalPrice": 1299, "discount": "6.9折", "mainImages": [ "https://img.dewu.com/xxx1.jpg", "https://img.dewu.com/xxx2.jpg" ], "specs": [ { "name": "颜色", "values": ["纯白", "米白"] }, { "name": "尺码", "values": ["38", "39", "40"] } ], "stock": 128, "sales": 5600, "brand": { "id": 1001, "name": "Nike" }, "seller": { "shopId": "S888", "shopName": "潮流正品专营店", "rating": 4.9 } } }

3.2 特殊字段处理技巧

  1. 价格字段:得物采用整数存储价格(单位:分),需要除以100转换。但要注意某些促销价可能直接是元单位,需根据priceType字段判断。

  2. 图片URL:图片链接有时会返回相对路径,需要拼接域名前缀。实测发现CDN地址有多个,建议统一替换为"https://img.dewu.com"。

  3. 规格参数:specs数组中的值可能存在HTML标签,需要做清洗处理。例如:

    import re clean_text = re.sub(r'<[^>]+>', '', dirty_text)

3.3 嵌套数据提取

对于复杂的嵌套数据,建议使用JSONPath或递归函数处理。例如获取所有规格组合:

def extract_specs(data): specs = {} for item in data['data']['specs']: specs[item['name']] = item['values'] return specs

4. 反爬机制与应对策略

4.1 常见反爬手段

根据我的实测经验,得物主要采用以下防护措施:

  1. 请求频率限制:单IP超过30次/分钟会触发验证码
  2. 签名验证:sign参数错误直接返回403
  3. 设备指纹:通过HTTP头检测异常设备
  4. 行为验证:连续访问会弹出滑动验证码

4.2 实战解决方案

  1. IP代理池:建议使用优质住宅代理,按1-2秒间隔请求

    proxies = { 'http': 'http://user:pass@proxy_ip:port', 'https': 'https://user:pass@proxy_ip:port' }
  2. 请求随机化:

    • 随机User-Agent(准备至少20个常见手机UA)
    • 随机请求间隔(0.5-3秒)
    • 随机添加无关参数(如timestamp=xxx)
  3. 签名破解:通过逆向App分析sign生成算法,核心逻辑通常是:

    def generate_sign(params): secret = '得物密钥' # 需要逆向获取 param_str = '&'.join([f'{k}={v}' for k,v in sorted(params.items())]) return md5(param_str + secret).hexdigest()

5. 数据存储与清洗建议

5.1 数据库设计

推荐使用MongoDB存储非结构化数据,关键字段建立索引:

db.products.createIndex({ "productId": 1, "brand.id": 1, "price": 1 })

关系型数据库设计参考:

CREATE TABLE products ( id VARCHAR(20) PRIMARY KEY, title VARCHAR(200), price INT, original_price INT, discount VARCHAR(10), stock INT, sales INT, brand_id INT, shop_id VARCHAR(20), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

5.2 数据清洗要点

  1. 价格异常值:过滤price>original_price的异常记录
  2. 图片去重:使用MD5校验去除重复图片URL
  3. 规格标准化:将"38码/US7"等文本统一转换为数字
  4. 时间处理:将时间戳转换为标准datetime格式

6. 常见问题排查实录

6.1 接口返回403错误

可能原因:

  • 签名计算错误(检查参数顺序和密钥)
  • 请求头缺失(特别是User-Agent)
  • IP被封禁(更换代理测试)

调试方法:

print(response.request.headers) # 检查发送的请求头 print(response.url) # 检查完整请求URL

6.2 数据字段缺失

解决方案:

  1. 检查接口版本(有些字段只在特定版本返回)
  2. 添加登录态(部分数据需要认证)
  3. 确认商品状态(下架商品返回字段较少)

6.3 验证码触发后处理

建议流程:

  1. 立即停止当前IP的请求
  2. 更换IP和设备指纹
  3. 降低请求频率至5次/分钟
  4. 考虑使用自动化验证码识别服务

7. 性能优化实践

7.1 并发控制策略

使用异步请求提升效率,但要注意得物的频率限制:

import aiohttp import asyncio async def fetch(session, url): async with session.get(url) as response: return await response.json() async def main(): conn = aiohttp.TCPConnector(limit=5) # 控制并发数 async with aiohttp.ClientSession(connector=conn) as session: tasks = [fetch(session, url) for url in urls] return await asyncio.gather(*tasks)

7.2 缓存机制实现

对静态数据使用本地缓存:

from diskcache import Cache cache = Cache('tmp/dewu_cache') @cache.memoize(expire=3600) def get_product_detail(product_id): # 接口请求代码

8. 法律合规提醒

在进行数据采集时,请特别注意:

  1. 遵守得物的Robots协议
  2. 不采集用户隐私数据
  3. 控制请求频率避免影响正常服务
  4. 数据仅用于个人分析,不进行商业售卖

建议在夜间低谷期(凌晨1-6点)进行大规模采集,并将请求间隔设置为3秒以上。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进