结论先行:1688 有开放的官方商品查询接口(alibaba.item.get/alibaba.offer.search),完全免费、合法、稳定,只需企业实名应用 + AppKey。比爬虫稳定10倍,不怕封IP、不改版、不违规。
一、为什么放弃爬虫选官方API?
维度 | 爬虫(Selenium/Requests) | 1688官方API |
|---|---|---|
合法性 | ⚠️ 违反ToS,有法律风险 | ✅ 签约开放平台,合规 |
稳定性 | 页面改版即挂 | 接口版本化管理 |
数据质量 | 需自己解析HTML,易缺字段 | 结构化JSON,含SKU/价格/图片 |
限流 | IP被封 | 明确QPS(免费10~20/s),可买包扩容 |
维护成本 | 高(选择器/JS逆向) | 几乎零(签名+调用) |
二、前置准备(10分钟搞定)
- 注册 1688开放平台→ 创建「自用型应用」
- 企业支付宝认证(个人账号无法调订单/部分字段)
- 申请接口权限:
alibaba.offer.search(商品搜索)alibaba.item.get(商品详情)- 记录
App Key+App Secret - (订单类需
Access Token,商品查询可不传)
三、完整Python源码 —— 搜索 + 获取详情
# ali1688_official_api.py
import hashlib
import time
import requests
import urllib.parse
from typing import Dict, List, Optional
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class Ali1688OfficialClient:
"""
1688 官方商品API客户端(合法替代爬虫)
网关 + MD5签名 标准实现
"""
GATEWAY_SEARCH = "https://gw.open.1688.com/openapi/param2/2/alibaba.offer.search/2.0"
GATEWAY_COMMON = "https://gw.open.1688.com/openapi/http/2/1"
def __init__(self, app_key: str, app_secret: str, access_token: str = None):
self.app_key = app_key
self.app_secret = app_secret
self.access_token = access_token
# ────────────────────────────────────────
# 1688 标准 MD5 签名
# ────────────────────────────────────────
def _sign(self, params: Dict) -> str:
filtered = sorted((k, v) for k, v in params.items() if v is not None)
qs = ''.join(f"{k}{v}" for k, v in filtered)
raw = f"{self.app_secret}{qs}{self.app_secret}"
return hashlib.md5(raw.encode('utf-8')).hexdigest().upper()
def _call(self, url: str, method: str, biz: Dict) -> Dict:
api_params = {
"method": method,
"app_key": self.app_key,
"timestamp": str(int(time.time() * 1000)), # 毫秒!
"format": "json",
"v": "2.0",
"sign_method": "md5",
}
if self.access_token:
api_params["session"] = self.access_token
# param2 = URL编码后的业务参数字符串
api_params["param2" if "param2" in url or method == "alibaba.offer.search" else "param"] = \
urllib.parse.quote_plus(str(biz).replace("'", '"'))
api_params["sign"] = self._sign(api_params)
resp = requests.get(url, params=api_params, timeout=15)
resp.raise_for_status()
data = resp.json()
if "error_response" in data:
err = data["error_response"]
raise Exception(f"1688 API Error [{err.get('code')}]: {err.get('msg')}")
result_key = [k for k in data if k != "error_response"][0]
return data[result_key]
# ────────────────────────────────────────
# 1️⃣ 关键词搜索商品列表
# ────────────────────────────────────────
def search_products(self,
keyword: str,
page_no: int = 1,
page_size: int = 40,
price_min: Optional[float] = None,
price_max: Optional[float] = None) -> Dict:
"""
返回: { offers: [...], totalResult: int, pageNo, pageSize }
⚠️ price单位 分!beginPrice=1500 表示 ≥15元
"""
biz = {
"keywords": keyword,
"pageNo": page_no,
"pageSize": min(page_size, 50),
"sortType": "booked" # 按成交量降序(选品推荐)
}
if price_min is not None:
biz["beginPrice"] = str(int(price_min * 100))
if price_max is not None:
biz["endPrice"] = str(int(price_max * 100))
return self._call(self.GATEWAY_SEARCH, "alibaba.offer.search", biz)
# ────────────────────────────────────────
# 2️⃣ 获取商品详情(含SKU/图片/批发价)
# ────────────────────────────────────────
def get_product_detail(self, offer_id: str, fields: str = None) -> Dict:
"""
fields示例: "item_id,title,price,sku_list,pics,spec_info,min_order_quantity"
"""
biz = {"item_id": offer_id}
if fields:
biz["fields"] = fields
result = self._call(
self.GATEWAY_COMMON, "alibaba.item.get", biz
)
return result.get("alibaba_item_get_response", {}).get("item", {})
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
# =========================================================
# 使用示例
# =========================================================
if __name__ == "__main__":
client = Ali1688OfficialClient(
app_key="YOUR_APP_KEY",
app_secret="YOUR_APP_SECRET",
access_token=None # 商品搜索/详情可不传
)
try:
# —— ① 搜索 ——
result = client.search_products(
keyword="不锈钢保温杯 定制",
price_min=15,
price_max=60,
page_no=1,
page_size=10
)
offers = result.get("offers", [])
total = result.get("totalResult", 0)
print(f"✅ 找到 {total} 个商品,本页 {len(offers)} 条")
if not offers:
exit()
# —— ② 取第一个商品查详情 ——
offer_id = str(offers[0].get("offerId"))
detail = client.get_product_detail(
offer_id,
fields="item_id,title,price,sku_list,pics,min_order_quantity"
)
print(f"\n📦 商品标题: {detail.get('title')}")
print(f" 起批价: ¥{detail.get('price')}")
print(f" 最小起订量: {detail.get('min_order_quantity')}")
print(f" 主图: {detail.get('pics', [''])[0]}")
skus = detail.get("sku_list", [])
if skus:
print(f" SKU数量: {len(skus)}")
for sku in skus[:2]:
print(f" - {sku.get('spec_attributes')} 价:{sku.get('price')} 库存:{sku.get('amount_on_sale')}")
except Exception as e:
print(f"❌ {e}")四、返回关键字段映射(B2B重点)
1688返回字段 | 含义 | ERP用途 |
|---|---|---|
offerId/ item_id | 商品唯一ID | 外部单号关联 |
title | 商品标题 | 显示名 |
price | 起批参考价(字符串) | 比价基准 |
sku_list[].price | SKU阶梯价 | 采购成本核算 |
sku_list[].spec_attributes | 规格(颜色/尺寸) | SKU映射 |
min_order_quantity | MOQ | 采购校验 |
pics[] | 主图URL | 商品图片 |
supplierName | 供应商店铺 | 溯源 |
五、生产级建议
- 字段过滤:传
fields参数只取需要的字段 → 响应体积小、不易触发限流 - 限速:免费应用 QPS≈10,封装里加
time.sleep(0.15)或用令牌桶 - 翻页终止条件:
pageNo * pageSize >= totalResult停止翻页 - 增量更新:加
gmtModified筛选(部分接口支持),每天只拉变更商品 - 密钥安全:
AppSecret放环境变量 / 配置中心,严禁硬编码
六、一句话总结(面试版)
1688商品数据用官方开放平台API(alibaba.offer.search+alibaba.item.get)合法获取,MD5签名按参数名ASCII升序拼AppSecret+KV+AppSecret再大写,免费额度足够中小企业做选品/比价/主数据同步,不要用爬虫。
需要我补充 OAuth2获取AccessToken代码、每日增量同步APScheduler脚本 或 SKU与内部ERP编码映射表设计 吗?