前言 网上多数 Mercari 搜索示例直接使用 page 偏移分页做循环抓取,但煤炉搜索接口存在典型分页漂移问题:商品实时上下架,偏移翻页会出现数据重复、条目丢失。本文不采用简单 page 循环,基于移动端搜索接口,实现游标式遍历、内存集合去重、业务字段过滤,适配对日选品、价格监控业务,规避偏移分页带来的数据错乱问题,输出标准化商品列表。 前置准备 调用
Mercari JP 移动端搜索接口,必须配置 X‑Device‑Id、X‑Platform、Authorization
请求头。access_token 存在有效期,401 代表令牌失效;403/429
为访问限流,需要做退避延时。接口支持关键词、价格区间、售卖状态筛选。 python 代码核心解析 1. 游标 cursor 替代 page 偏移分页,缓解商品动态变更带来的分页漂移,减少漏数据、重复数据。
依赖:requests,执行pip install requests
import requests
import time
class MercariSearchClient:
def __init__(self, access_token, device_id):
self.access_token = access_token
self.device_id = device_id
self.base_url = "https://api.mercari.jp/v2/search"
self.session = requests.Session()
self.headers = {
"X‑Device‑Id": self.device_id,
"X‑Platform": "android",
"Authorization": f"Bearer {access_token}",
"User‑Agent": "Mercari/6.200.0 (Linux; Android)"
}
self.seen_ids = set() # 全局去重集合
def search_by_keyword(self, keyword, price_min=0, price_max=0, status="on_sale", cursor=None):
params = {
"q": keyword,
"status": status,
"limit": 50
}
if price_min>0: params["price_min"] = price_min
if price_max>0: params["price_max"] = price_max
if cursor: params["cursor"] = cursor
try:
resp = self.session.get(self.base_url, headers=self.headers, params=params, timeout=12)
if resp.status_code == 401:
return {"success":False,"error":"access_token令牌过期"}
if resp.status_code in (403,429):
time.sleep(5)
resp = self.session.get(self.base_url, headers=self.headers, params=params, timeout=12)
resp.raise_for_status()
raw = resp.json()
except requests.exceptions.RequestException as e:
return {"success":False,"error":f"网络异常:{str(e)}"}
data = raw.get("data",{})
next_cursor = data.get("cursor")
raw_items = data.get("items",[])
item_list = []
status_map = {"on_sale":"在售","sold_out":"已售出","trading":"交易中"}
for item in raw_items:
iid = item.get("id")
if not iid or iid in self.seen_ids:
continue
self.seen_ids.add(iid)
item_list.append({
"item_id":iid,
"title":item.get("title",""),
"price":int(item.get("price",0)),
"status_text":status_map.get(item.get("status"),"未知"),
"thumbnail":item.get("thumb_photo_url",""),
"is_free_shipping": item.get("shipping_payer")=="seller"
})
return {"success":True,"items":item_list,"next_cursor":next_cursor}
if __name__ == "__main__":
cli = MercariSearchClient(access_token="your_token",device_id="your_deviceid")
res = cli.search_by_keyword(keyword="ポケモンカード",price_min=1000)
print(res)
2. 内置 seen_id 集合做内存去重,同一商品不会重复输出,适合循环多页抓取场景。
3. 统一处理 401 令牌过期、403/429 限流,增加休眠退避,给出明确业务错误。
4. 做状态字段映射,过滤无用埋点字段,只保留选品需要的核心数据。