蘑菇街作为聚焦年轻女性用户的时尚电商平台,其商品搜索功能(对应接口,非官方命名)是获取服饰、美妆、家居等品类商品列表的核心工具,广泛应用于电商选品、竞品分析、趋势监控等场景。由于蘑菇街官方 API 对个人开发者限制严格,多数场景需通过页面解析或第三方服务实现搜索对接。本文将系统讲解
item_search接口的对接逻辑、参数解析、加密处理及反爬应对,帮助开发者构建稳定的商品搜索数据获取系统。
item_search
一、接口基础认知(核心功能与场景)
核心功能蘑菇街接口通过关键词、分类、价格等多维度条件筛选商品,返回符合条件的列表数据,核心字段包括:
item_search
基础信息:商品 ID()、标题、主图、类目、详情页 URL、上架时间价格信息:原价、现价、折扣(如 “7 折”)、优惠券标识(如 “满 199 减 20”)交易数据:月销量(如 “月销 5000+”)、评价数、好评率(如 “97% 好评”)店铺信息:店铺名称、店铺类型(如 “旗舰店”“普通店”)、所在地特色标签:是否预售、是否正品、是否包邮、是否网红同款
item_id
典型应用场景
电商卖家选品:按 “夏季连衣裙 小个子” 搜索,筛选高销量、高好评商品竞品监控:跟踪 “韩系彩妆” 关键词下竞品的价格、销量变化趋势分析:统计 “2024 秋冬外套” 类目的价格分布、款式热度比价工具:整合蘑菇街与淘宝、拼多多同款商品,提供价格对比
接口特性
反爬严格:依赖 JS 加密参数(如)、IP 限制、Cookie 验证,搜索接口反爬强度高于详情页动态加载:搜索结果通过 AJAX 接口动态加载,需解析 JSON 数据(非静态 HTML)分页限制:默认每页 20 条,最多支持 50 页(约 1000 条结果),超过则返回空C 端属性:数据包含消费者视角字段(如折扣、优惠券、网红标签),与批发平台差异显著
_m_h5_tk
二、对接前置准备(环境与参数解析)
开发环境
开发语言:Python(推荐,适合处理加密参数与动态接口)核心库:
网络请求:(同步请求)、
requests(异步批量搜索)加密处理:
aiohttp(执行 JS 生成签名)、
execjs(MD5 加密)反爬工具:
hashlib(随机 User-Agent)、
fake_useragent(代理 IP 池)数据解析:
proxy_pool(提取 JSON 数据)、
jsonpath(清洗列表数据)
pandas
搜索 URL 与核心参数蘑菇街搜索依赖动态接口(非静态 URL),核心接口为:
移动端搜索接口:需通过 POST 请求传递参数,核心参数如下(封装在 JSON 中):
https://gateway.mogujie.com/api/search/search
| 筛选条件 | 参数名 | 示例值 | 说明 |
|---|---|---|---|
| 关键词 | |
|
支持中文、组合词(如 “显瘦 牛仔裤”) |
| 分类 ID | |
(女装)、(彩妆) |
分类 ID 需通过首页分类接口获取 |
| 价格区间(始) | |
|
最低价格(元),如表示≥100 元 |
| 价格区间(终) | |
|
最高价格(元),如表示≤300 元 |
| 排序方式 | |
(销量降序) |
见 “排序参数表” |
| 分页 | |
… |
页码,默认 1,最大 50 |
| 特色筛选 | |
(仅预售) |
1 – 仅显示预售商品,0 – 不限 |
| 发货地 | |
|
筛选特定地区发货的商品 |
排序参数表蘑菇街搜索支持多种排序方式,对应参数值如下:
sort
| 排序方式 | 参数值 |
适用场景 |
|---|---|---|
| 综合推荐 | |
默认排序,平衡销量与相关性 |
| 销量降序 | |
筛选热门商品(高月销优先) |
| 价格升序 | |
低价商品筛选 |
| 价格降序 | |
高端商品筛选 |
| 最新上架 | |
新款监控(如当季新品) |
加密参数解析(核心难点)搜索接口需携带与详情页相同的和
_m_h5_tk参数,生成逻辑如下:
_m_h5_tk_enc
从 Cookie 中提取的
_m_h5_tk前缀(格式为
token);拼接
token_xxxx(
token + "&" + timestamp + "&" + appKey + "&" + data);通过 MD5 加密生成签名,最终
appKey=12574478。
_m_h5_tk=token_签名
三、接口调用流程(基于动态接口)
以 “搜索夏季连衣裙,价格 100-300 元,按销量降序排序” 为例,核心流程为参数组装→加密参数生成→接口请求→数据解析→分页遍历:
请求参数组装搜索接口的请求体()为 JSON 格式,示例:
data
python
data = {
"keyword": "夏季连衣裙",
"cateId": "", # 不限制分类
"priceMin": 100,
"priceMax": 300,
"sort": "sale",
"page": 1,
"pageSize": 20 # 每页条数,固定20
}
加密参数与请求头构建
生成和时间戳:
_m_h5_tk
python
import hashlib
import time
def generate_search_sign(cookie_tk, data_str):
token = cookie_tk.split("_")[0]
timestamp = str(int(time.time() * 1000))
app_key = "12574478"
sign_str = f"{token}&{timestamp}&{app_key}&{data_str}"
sign = hashlib.md5(sign_str.encode()).hexdigest()
return f"{token}_{sign}", timestamp
请求头需包含移动端 User-Agent、Referer、Cookie:
python
headers = {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
"Referer": "https://m.mogujie.com/search",
"Cookie": "_m_h5_tk=xxx; _m_h5_tk_enc=xxx; ...", # 从浏览器获取
"Content-Type": "application/json"
}
接口响应解析搜索接口返回 JSON 数据,核心商品列表在中,单条商品结构示例:
data.result.list
json
{
"itemId": "12345678",
"title": "夏季碎花连衣裙小个子显瘦",
"img": "https://img.mogujie.com/xxx.jpg",
"price": 159,
"originalPrice": 299,
"saleCount": 5200, # 月销量
"commentCount": 1200,
"shopName": "时尚女装店",
"tags": ["包邮", "网红同款"]
}
分页处理
分页通过参数控制,每次请求递增页码;终止条件:返回的商品列表长度 < 20(最后一页)或页码≥50;分页间隔:每页请求间隔 2-4 秒(随机波动),避免触发反爬。
page
四、代码实现示例(Python)
以下是接口的完整实现,包含参数加密、动态接口调用、分页遍历及数据解析:
item_search
import requests
import time
import random
import json
import hashlib
from fake_useragent import UserAgent
from typing import List, Dict
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class MogujieSearchApi:
def __init__(self, cookie: str, proxy_pool: List[str] = None):
self.search_api = "https://gateway.mogujie.com/api/search/search"
self.app_key = "12574478"
self.cookie = cookie
self.cookie_tk = self._extract_cookie_tk() # 提取_m_h5_tk
self.ua = UserAgent()
self.proxy_pool = proxy_pool
def _extract_cookie_tk(self) -> str:
"""从Cookie中提取_m_h5_tk"""
for cookie in self.cookie.split(";"):
if "_m_h5_tk" in cookie:
return cookie.split("=")[1].strip()
raise ValueError("Cookie中未找到_m_h5_tk,请检查Cookie有效性")
def _generate_sign(self, data_str: str) -> tuple:
"""生成加密参数_m_h5_tk和timestamp"""
token = self.cookie_tk.split("_")[0]
timestamp = str(int(time.time() * 1000))
sign_str = f"{token}&{timestamp}&{self.app_key}&{data_str}"
sign = hashlib.md5(sign_str.encode()).hexdigest()
return f"{token}_{sign}", timestamp
def _get_headers(self) -> Dict[str, str]:
"""生成请求头"""
return {
"User-Agent": self.ua.mobile, # 移动端UA反爬较松
"Referer": "https://m.mogujie.com/search",
"Origin": "https://m.mogujie.com",
"Cookie": self.cookie,
"Content-Type": "application/json"
}
def _get_proxy(self) -> Dict[str, str]:
"""随机获取代理"""
if self.proxy_pool and len(self.proxy_pool) > 0:
proxy = random.choice(self.proxy_pool)
return {"http": proxy, "https": proxy}
return None
def _parse_items(self, response_data: Dict) -> List[Dict]:
"""解析商品列表数据"""
items = []
for item in response_data.get("data", {}).get("result", {}).get("list", []):
items.append({
"item_id": item.get("itemId", ""),
"title": item.get("title", ""),
"main_image": item.get("img", ""),
"url": f"https://item.mogujie.com/detail/{item.get('itemId', '')}.htm",
"price": {
"current": item.get("price", 0),
"original": item.get("originalPrice", 0),
"discount": f"{round(item.get('price', 0)/item.get('originalPrice', 1)*10, 1)}折"
if item.get("originalPrice", 0) > 0 else ""
},
"sales": {
"monthly": item.get("saleCount", 0),
"comment_count": item.get("commentCount", 0)
},
"shop": {
"name": item.get("shopName", ""),
"location": item.get("location", "")
},
"tags": item.get("tags", [])
})
return items
def item_search(self,
keyword: str,
cate_id: str = "",
price_min: int = None,
price_max: int = None,
sort: str = "default",
location: str = "",
page_limit: int = 5) -> Dict:
"""
搜索蘑菇街商品列表
:param keyword: 搜索关键词
:param cate_id: 分类ID(可选)
:param price_min: 最低价格(元)
:param price_max: 最高价格(元)
:param sort: 排序方式(default/sale/priceAsc等)
:param location: 发货地(可选)
:param page_limit: 最大页数(默认5)
:return: 标准化搜索结果
"""
try:
all_items = []
current_page = 1
while current_page <= page_limit:
# 1. 构建请求数据
data = {
"keyword": keyword,
"cateId": cate_id,
"sort": sort,
"page": current_page,
"pageSize": 20
}
if price_min is not None:
data["priceMin"] = price_min
if price_max is not None:
data["priceMax"] = price_max
if location:
data["location"] = location
data_str = json.dumps(data, ensure_ascii=False)
# 2. 生成加密参数
m_h5_tk, timestamp = self._generate_sign(data_str)
params = {
"_m_h5_tk": m_h5_tk,
"_m_h5_tk_enc": "",
"timestamp": timestamp,
"appKey": self.app_key
}
# 3. 发送请求(带随机延迟)
time.sleep(random.uniform(2, 4))
headers = self._get_headers()
proxy = self._get_proxy()
response = requests.post(
url=self.search_api,
params=params,
data=data_str,
headers=headers,
proxies=proxy,
timeout=10
)
response.raise_for_status()
response_data = response.json()
# 4. 检查接口状态
if not response_data.get("success"):
error_code = response_data.get("code", -1)
if error_code == -1001:
return {"success": False, "error_msg": "_m_h5_tk失效,请更新Cookie", "code": -1001}
return {"success": False, "error_msg": f"接口返回失败: {response_data.get('msg')}", "code": error_code}
# 5. 解析商品并分页
items = self._parse_items(response_data)
if not items:
break # 无数据,终止分页
all_items.extend(items)
# 若当前页商品数<20,说明是最后一页
if len(items) < 20:
break
current_page += 1
# 去重(基于item_id)
seen_ids = set()
unique_items = []
for item in all_items:
if item["item_id"] not in seen_ids:
seen_ids.add(item["item_id"])
unique_items.append(item)
return {
"success": True,
"total": len(unique_items),
"page_processed": current_page - 1,
"items": unique_items
}
except requests.exceptions.HTTPError as e:
if "403" in str(e):
return {"success": False, "error_msg": "触发反爬,建议更换代理或Cookie", "code": 403}
return {"success": False, "error_msg": f"HTTP错误: {str(e)}", "code": response.status_code}
except Exception as e:
return {"success": False, "error_msg": f"搜索失败: {str(e)}", "code": -1}
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
# 使用示例
if __name__ == "__main__":
# 从浏览器获取的Cookie(需包含_m_h5_tk)
COOKIE = "_m_h5_tk=xxx; _m_h5_tk_enc=xxx; ..." # 替换为实际Cookie
# 代理池(替换为有效代理)
PROXIES = [
"http://123.45.67.89:8888",
"http://98.76.54.32:8080"
]
# 初始化API客户端
search_api = MogujieSearchApi(cookie=COOKIE, proxy_pool=PROXIES)
# 搜索“夏季连衣裙”,价格100-300元,按销量降序,最多3页
result = search_api.item_search(
keyword="夏季连衣裙",
price_min=100,
price_max=300,
sort="sale",
page_limit=3
)
if result["success"]:
print(f"搜索成功:共找到 {result['total']} 件商品,处理 {result['page_processed']} 页")
for i, item in enumerate(result["items"][:5]): # 打印前5条
print(f"
商品 {i+1}:")
print(f"标题:{item['title'][:50]}...")
print(f"价格:现价{item['price']['current']}元 | 原价{item['price']['original']}元 | {item['price']['discount']}")
print(f"销量:月销{item['sales']['monthly']}件 | 评价{item['sales']['comment_count']}条")
print(f"店铺:{item['shop']['name']}({item['shop']['location']})")
print(f"标签:{','.join(item['tags'])} | 详情页:{item['url']}")
else:
print(f"搜索失败:{result['error_msg']}(错误码:{result.get('code')})")
五、关键技术难点与解决方案
加密参数的动态维护
_m_h5_tk
问题:有效期约 2 小时,失效后接口返回
_m_h5_tk错误,需重新生成。解决方案:
-1001
定期(1.5 小时)通过浏览器或 Selenium 自动刷新 Cookie,更新;代码中检测
_m_h5_tk错误码,触发 Cookie 更新逻辑(示例代码已包含错误处理);维护 Cookie 池(3-5 个有效 Cookie),失效时自动切换。
-1001
搜索接口数据碎片化
问题:部分字段(如好评率、店铺评分)未在搜索接口返回,需结合详情页补充。解决方案:
搜索接口优先提取核心字段(价格、销量、标题),非核心字段通过后续接口补充;对批量搜索任务,采用 “搜索列表→筛选目标商品→详情页补充” 的分步策略,减少无效请求。
item_get
反爬机制对抗
问题:蘑菇街搜索接口反爬严格,高频请求会触发 IP 封锁、参数失效或验证码。解决方案:
代理 IP 轮换:使用高匿代理池,每 2-3 页切换 IP,避免单一 IP 被标记;请求频率控制:单 IP 每分钟请求≤3 次,两次请求间隔 2-4 秒(随机波动);UA 与 Cookie 多样化:随机切换移动端 User-Agent,搭配不同 Cookie 池降低风险;验证码处理:若触发验证码,暂停当前代理并切换 Cookie,使用 Selenium 手动验证(适合低频场景)。
分页限制与数据量控制
问题:蘑菇街搜索最多返回 50 页(1000 条),且超过 30 页后数据质量下降(重复或低相关商品)。解决方案:
合理设置(建议≤20),平衡数据量与相关性;对大额需求,通过多关键词拆分搜索(如 “夏季连衣裙 长款”“夏季连衣裙 短款”);分页时检查商品 ID 去重,避免重复数据(示例代码中
page_limit逻辑)。
unique_items
六、最佳实践与合规要点
系统架构设计采用 “加密参数管理 + 分布式搜索” 架构,适配蘑菇街严格反爬:
参数管理层:集中生成与缓存,定时更新,支持多 Cookie 池切换;任务调度层:通过消息队列(如 RabbitMQ)分发搜索任务,控制单队列并发数≤2;采集层:多节点并行搜索,每个节点绑定独立代理与 Cookie,节点间间隔≥5 秒;存储层:用 Redis 缓存搜索结果(10 分钟过期),MySQL 存储历史趋势数据。
_m_h5_tk
性能优化策略
异步批量搜索:使用并发处理多关键词(控制并发数≤3),共享代理池;增量搜索:对相同关键词 + 条件,1 小时内返回缓存结果,减少重复请求;失败重试机制:对
aiohttp(反爬)、
403(参数失效)错误,自动重试 2 次(更换资源后)。
-1001
合规性与风险控制
遵守平台规则:蘑菇街开放平台明确禁止未授权爬取,商业使用需申请官方 API;频率限制:单 IP 日请求量≤300 次,避免对服务器造成压力;数据用途:不得将数据用于恶意竞争、虚假宣传,需注明来源 “蘑菇街”。
反爬适应性调整
定期(每周)检查接口参数变化(如或加密逻辑更新),同步更新代码;当搜索接口升级(如新增
appKey参数),通过抓包工具(Charles)逆向最新 JS 代码,提取新规则;建立降级方案:动态接口失败时,fallback 到 PC 端静态页面解析(适合数据精度要求不高的场景)。
sign2
七、总结
蘑菇街接口的对接核心在于加密参数
item_search的动态维护、严格的反爬对抗策略及分页数据的高效处理。开发者需重点关注:
_m_h5_tk
搜索接口的参数加密逻辑(与详情页共用生成规则);代理池与 Cookie 池的精细化管理(应对高频请求与参数失效);分页限制与数据去重(平衡数据量与有效性)。
_m_h5_tk
通过本文的技术方案,可构建稳定的商品搜索系统,为电商选品、市场分析等场景提供数据支持。实际应用中,需优先考虑官方 API 接入,确保合规使用数据。