前言 DMM 联盟 ItemList 接口支持关键词检索全站商品,是日系选品系统的核心检索源。网上多数示例仅实现基础关键词请求,忽略该接口独有的限制:多业务 floor 类目隔离、offset 存在上限、日文关键词编码陷阱、不同类目返回字段异构、分页末尾会出现重复商品。本文不再做简单入门演示,聚焦跨类目批量检索、分页边界校验、结果去重、字段统一映射,封装一套适合后台检索任务的稳定实现。 Python 完整代码 核心设计思路 Offset 边界保护:DMM 接口 offset 最大限制 1000,很多人写无限循环分页,到上限直接报错。代码提前拦截超限参数,避免批量任务崩溃。 日文关键词编码:日文检索词必须手动 URL 编码,直接传日文会出现检索为空的问题,这是高频踩坑点。 CID 全局去重:接口分页尾部存在商品漂移,同一商品在不同 offset 重复返回,使用 cid 集合过滤重复数据。 跨 floor 字段兼容:图书、游戏、通贩等类目返回字段不一致,采用 对接踩坑要点 Affiliate 联盟 ID 末尾必须是 990~999,否则直接鉴权失败。 floor 参数需要和 site 匹配,跨 floor 检索不能一次性完成,需要循环多个 floor 分别检索再合并结果。 hits 单页上限 100,建议控制 20~50,单次请求量大更容易触发限流。批量检索必须添加请求间隔。 返回 total 仅为估算值,不能完全依靠 total 循环分页,需要判断返回 item 列表为空终止任务。 使用接口返回数据对外展示,需要遵守 DMM 联盟协议,添加官方版权声明。
import requests
import urllib.parse
import time
class DmmKeywordSearchClient:
def __init__(self, api_id, affiliate_id):
self.api_id = api_id
self.affiliate_id = affiliate_id
self.base_url = "https://api.dmm.com/affiliate/v3/ItemList"
self.seen_cid = set() # 全局CID去重
def search_keyword(self, keyword, floor="book", site="DMM", hits=20, offset=1):
# offset上限校验,超出直接返回空,规避接口报错
if offset > 1000:
return {"ok": False, "msg": "offset超出接口上限", "data": []}
# 日文关键词URL编码
keyword_encode = urllib.parse.quote(keyword)
params = {
"api_id": self.api_id,
"affiliate_id": self.affiliate_id,
"site": site,
"floor": floor,
"hits": hits,
"offset": offset,
"keyword": keyword_encode,
"output": "json"
}
try:
resp = requests.get(self.base_url, params=params, timeout=15)
resp.raise_for_status()
raw = resp.json()
except Exception as e:
return {"ok": False, "msg": f"请求异常:{str(e)}", "data": []}
result = raw.get("result", {})
item_list = result.get("items", [])
clean_result = []
for item in item_list:
cid = item.get("cid")
if not cid or cid in self.seen_cid:
continue
self.seen_cid.add(cid)
item_out = {
"cid": cid,
"title": item.get("title", "").strip(),
"price": item.get("price", ""),
"maker": item.get("maker", {}).get("name", ""),
"url": item.get("url", ""),
"floor": item.get("floor", ""),
"genres": [g.get("name", "") for g in item.get("genre", [])]
}
clean_result.append(item_out)
return {"ok": True, "total": result.get("total",0), "data": clean_result}
if __name__ == "__main__":
client = DmmKeywordSearchClient("你的API_ID", "你的联盟ID")
res = client.search_keyword(keyword="ゲーム", floor="game", hits=20, offset=1)
print(f"检索总数量:{res['total']}, 有效商品:{res['data']}").get()安全取值,防止某个字段缺失直接抛出异常。