🟦《别再爬京东了!JOS官方API合法拿数据,月100万次内免费(附Demo)》(附Python源码)
结论先行:京东商品/订单/物流/库存查询用京东开放平台(JOS) 官方API(
jingdong.ware.read.get、jingdong.pop.order.search、jingdong.etms.trace.get等),企业应用默认免费额度通常数万~百万次/月,合法、结构化、不封IP;爬虫面临 ToS 违规、滑块验证、页面改版维护噩梦,生产系统禁用。
一、为什么放弃爬虫选 JOS API?
维度 | Selenium/Requests 爬京东商品页 | 京东开放平台 JOS API |
|---|---|---|
合规性 | ⚠️ 违反京东 robots.txt + ToS | ✅ 签约 JOS,合规 |
稳定性 | 页面类名变/懒加载/滑块 → 挂 | 接口版本化管理 |
数据质量 | HTML 解析易漏 SKU/券后价 | JSON 含 SKU、库存(需token)、价格 |
封禁 | IP/UA 段封禁、代理池成本 | 按 QPS 限流不封 IP,买包可提 |
成本 | 代理+服务器+开发维护 | 免费额度内 ¥0(超量 ≈¥0.02~0.05/百次 聚石塔内) |
二、前置准备(10分钟)
注册 京东开放平台→创建「商家自用型应用」(需企业支付宝认证;学习可用个人号但订单接口无权限)
获取
App Key+App Secret(密码)应用 →接口权限 → 申请:
jingdong.ware.read.get(商品详情)jingdong.ware.sku.read(SKU列表,可选)jingdong.pop.order.search(订单列表)jd.order.detail.get(订单明细)jingdong.etms.trace.get(物流轨迹)
如需订单/店铺私有数据 →卖家账号 OAuth2 授权换取 AccessToken(session 参数)
三、Python完整 Demo —— 商品详情 + 订单增量探测(合法获取)
# jd_legal_api_demo.py """ 京东JOS 官方API合法获取数据 Demo - 商品详情 jingdong.ware.read.get - 订单列表 jingdong.pop.order.search (需 seller AccessToken) 签名: MD5(AppSecret + KV_ASCII_sorted + AppSecret), timestamp=秒级(10位) """ import hashlib import json import requests import time from datetime import datetime, timedelta from typing import Dict, List, Optional 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex class JdLegalClient: GW = "https://api.jd.com/routerjson" def __init__(self, app_key: str, app_secret: str): self.ak = app_key self.ask = app_secret # ───── JOS MD5签名 ───── def _sign(self, params: Dict) -> str: filt = sorted((k, v) for k, v in params.items() if v is not None and str(v).strip() != '' and k != 'sign') qs = ''.join(f"{k}{v}" for k, v in filt) return hashlib.md5(f"{self.ask}{qs}{self.ask}".encode() ).hexdigest().upper() def _call(self, method: str, biz: Dict, access_token: str = None) -> Dict: p = { "app_key": self.ak, "method": method, "timestamp": str(int(time.time())), # ← 秒级!不同于1688毫秒 "format": "json", "v": "2.0", "sign_method": "md5", "360buy_param_json": json.dumps(biz, ensure_ascii=False, separators=(',', ':')) } if access_token: p["access_token"] = access_token p["sign"] = self._sign(p) r = requests.post(self.GW, data=p, timeout=15) r.raise_for_status() d = r.json() # 提取响应 resp_key = method.replace(".", "_") + "_response" if resp_key not in d: for k in d: if k.endswith("_response"): resp_key = k break data = d.get(resp_key, d) if "error_response" in str(data): err = d.get(resp_key, {}).get("error_response") or d.get("error_response") if err: code = str(err.get("code", "")) zh = err.get("zh_desc") or err.get("en_desc") if "no permission" in zh: raise PermissionError( "❌ 无权限:确认企业应用+接口已申请+" "订单类需传卖家AccessToken\n原始: " + zh ) raise Exception(f"JOS[{code}]: {zh} sub={err.get('sub_code')}") raise Exception(f"JOS error: {d}") return data # ─── ① 商品详情(公开字段可不传token)─── def get_ware(self, ware_id: str, token: str = None) -> Dict: """ ware_id: 京东 SPU ID (wareId) fields 建议含 skus_json 以取各SKU价格/库存(需seller token查自己店铺) """ fields = ( "ware_id,title,brand_name,cid,category_name," "price,jd_price,market_price,stock_num," "skus_json,image_path,desc,outer_id,approve_status" ) return self._call( "jingdong.ware.read.get", {"wareId": ware_id, "fields": fields}, access_token=token ).get("ware", {}) # ─── ② 订单增量列表(需卖家 token)─── def list_orders(self, token: str, minutes_back: int = 30, page: int = 1, order_state: str = "WAIT_SELLER_STOCK_OUT") -> Dict: now = datetime.now() start = (now - timedelta(minutes=minutes_back)).strftime("%Y-%m-%d %H:%M:%S") end = now.strftime("%Y-%m-%d %H:%M:%S") return self._call( "jingdong.pop.order.search", { "start_modified": start, "end_modified": end, "order_state": order_state, "page": page, "page_size": 50 }, access_token=token ).get("popOrderSearch", {}).get("orderSearch", {}) # ========================================================= # 使用示例 # ========================================================= if __name__ == "__main__": client = JdLegalClient( app_key="YOUR_JD_APP_KEY", app_secret="YOUR_JD_APP_SECRET" ) try: # ★ 商品详情 — 不需token(公开字段) ware = client.get_ware("100012345678") # ← 替换真实 wareId print("✅ 商品标题:", ware.get("title")) print(" 京东价 :", ware.get("jd_price")) print(" SPU库存:", ware.get("stock_num")) # SKU解析 import json as _j skus = _j.loads(ware["skus_json"]) if ware.get("skus_json") else [] print(f" SKU数 : {len(skus)}") for s in skus[:2]: print(f" skuId:{s.get('skuId')} ¥{s.get('jdPrice')} 库:{s.get('stockNum')}") # ★ 订单 — 需传卖家AccessToken (OAuth2授权) # SELLER_TOKEN = "SELLER_ACCESS_TOKEN" # orders = client.list_orders(SELLER_TOKEN, minutes_back=30) # print("近30min变更订单数:", orders.get("orderTotal")) except PermissionError as pe: print(pe) except Exception as e: print("❌", e) print("→ 检查: AppKey/Secret正确、jingdong.ware.read.get 已申请、wareId有效")四、免费额度说明(2026版)
接口族 | 是否免费 | 典型企业免费额度 |
|---|---|---|
商品查询 | ✅ 免费额度内 | 5万~100万次/月 |
订单列表 | ✅ 免费额度内(超量按基础计费≈¥0.02~0.05/百次 聚石塔内) | 同上 |
物流轨迹 | ✅ 免费额度内 | — |
京东联盟选品 | ✅ 备案后免费(API不收费成交抽佣) | 10万~100万次/天 |
电子面单取号 | 💰 按面单数 | 或含仓配包 |
CRM/数据增值 | 💰 需购包 | — |
五、避坑清单
坑 | 现象 | 解决 |
|---|---|---|
| 用毫秒时间戳 | JOS用秒级 |
订单接口 403 | 个人应用 / 未申请 / 传买家token | 企业应用 + 申请权限 +卖家 OAuth AccessToken |
| 查别人店铺公开数据 | 自己店铺查须传 |
签名 fail 偶发 | 空值/ | 严格过滤空 + 紧凑JSON( |
沙箱返回 mock | 正常 | 切生产网关看真实数据 |
六、面试/方案一句话
京东商品/订单/物流数据用官方 JOS API(
jingdong.ware.read.get/jingdong.pop.order.search等)合法获取,企业应用默认免费额度内月调用可达百万次,MD5签名用秒级时间戳、参数按ASCII升序拼AppSecret+KV+AppSecret;订单类接口需卖家OAuth AccessToken,完全替代爬虫规避风控与法律风险。
需要我补京东OAuth2授权码→AccessToken Python完整代码 或订单增量同步APScheduler(断点续跑+Token自动刷新) 吗?