蘑菇街 item_search 接口对接全攻略:从入门到精通

内容分享13小时前发布
0 0 0

蘑菇街作为聚焦年轻女性用户的时尚电商平台,其商品搜索功能(对应
item_search
接口,非官方命名)是获取服饰、美妆、家居等品类商品列表的核心工具,广泛应用于电商选品、竞品分析、趋势监控等场景。由于蘑菇街官方 API 对个人开发者限制严格,多数场景需通过页面解析或第三方服务实现搜索对接。本文将系统讲解
item_search
接口的对接逻辑、参数解析、加密处理及反爬应对,帮助开发者构建稳定的商品搜索数据获取系统。

一、接口基础认知(核心功能与场景)

核心功能蘑菇街
item_search
接口通过关键词、分类、价格等多维度条件筛选商品,返回符合条件的列表数据,核心字段包括:

基础信息:商品 ID(
item_id
)、标题、主图、类目、详情页 URL、上架时间价格信息:原价、现价、折扣(如 “7 折”)、优惠券标识(如 “满 199 减 20”)交易数据:月销量(如 “月销 5000+”)、评价数、好评率(如 “97% 好评”)店铺信息:店铺名称、店铺类型(如 “旗舰店”“普通店”)、所在地特色标签:是否预售、是否正品、是否包邮、是否网红同款

典型应用场景

电商卖家选品:按 “夏季连衣裙 小个子” 搜索,筛选高销量、高好评商品竞品监控:跟踪 “韩系彩妆” 关键词下竞品的价格、销量变化趋势分析:统计 “2024 秋冬外套” 类目的价格分布、款式热度比价工具:整合蘑菇街与淘宝、拼多多同款商品,提供价格对比

接口特性

反爬严格:依赖 JS 加密参数(如
_m_h5_tk
)、IP 限制、Cookie 验证,搜索接口反爬强度高于详情页动态加载:搜索结果通过 AJAX 接口动态加载,需解析 JSON 数据(非静态 HTML)分页限制:默认每页 20 条,最多支持 50 页(约 1000 条结果),超过则返回空C 端属性:数据包含消费者视角字段(如折扣、优惠券、网红标签),与批发平台差异显著

二、对接前置准备(环境与参数解析)

开发环境

开发语言:Python(推荐,适合处理加密参数与动态接口)核心库:
网络请求:
requests
(同步请求)、
aiohttp
(异步批量搜索)加密处理:
execjs
(执行 JS 生成签名)、
hashlib
(MD5 加密)反爬工具:
fake_useragent
(随机 User-Agent)、
proxy_pool
(代理 IP 池)数据解析:
jsonpath
(提取 JSON 数据)、
pandas
(清洗列表数据)

搜索 URL 与核心参数蘑菇街搜索依赖动态接口(非静态 URL),核心接口为:

移动端搜索接口:
https://gateway.mogujie.com/api/search/search
需通过 POST 请求传递参数,核心参数如下(封装在 JSON 中):

筛选条件 参数名 示例值 说明
关键词
keyword

夏季连衣裙
 
韩系彩妆
支持中文、组合词(如 “显瘦 牛仔裤”)
分类 ID
cateId

103508
(女装)、
103512
(彩妆)
分类 ID 需通过首页分类接口获取
价格区间(始)
priceMin

100
最低价格(元),如
100
表示≥100 元
价格区间(终)
priceMax

300
最高价格(元),如
300
表示≤300 元
排序方式
sort

sale
(销量降序)
见 “排序参数表”
分页
page

1
 
2
 … 
50
页码,默认 1,最大 50
特色筛选
isPreSale

1
(仅预售)
1 – 仅显示预售商品,0 – 不限
发货地
location

杭州
 
广州
筛选特定地区发货的商品

排序参数表蘑菇街搜索支持多种排序方式,对应
sort
参数值如下:

排序方式
sort
参数值
适用场景
综合推荐
default
默认排序,平衡销量与相关性
销量降序
sale
筛选热门商品(高月销优先)
价格升序
priceAsc
低价商品筛选
价格降序
priceDesc
高端商品筛选
最新上架
new
新款监控(如当季新品)

加密参数解析(核心难点)搜索接口需携带与详情页相同的
_m_h5_tk

_m_h5_tk_enc
参数,生成逻辑如下:

从 Cookie 中提取
_m_h5_tk

token
前缀(格式为
token_xxxx
);拼接
token + "&" + timestamp + "&" + appKey + "&" + data

appKey=12574478
);通过 MD5 加密生成签名,最终
_m_h5_tk=token_签名

三、接口调用流程(基于动态接口)

以 “搜索夏季连衣裙,价格 100-300 元,按销量降序排序” 为例,核心流程为参数组装→加密参数生成→接口请求→数据解析→分页遍历

请求参数组装搜索接口的请求体(
data
)为 JSON 格式,示例:

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": ["包邮", "网红同款"]
}

分页处理

分页通过
page
参数控制,每次请求递增页码;终止条件:返回的商品列表长度 < 20(最后一页)或页码≥50;分页间隔:每页请求间隔 2-4 秒(随机波动),避免触发反爬。

四、代码实现示例(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
的动态维护

问题
_m_h5_tk
有效期约 2 小时,失效后接口返回
-1001
错误,需重新生成。解决方案
定期(1.5 小时)通过浏览器或 Selenium 自动刷新 Cookie,更新
_m_h5_tk
;代码中检测
-1001
错误码,触发 Cookie 更新逻辑(示例代码已包含错误处理);维护 Cookie 池(3-5 个有效 Cookie),失效时自动切换。

搜索接口数据碎片化

问题:部分字段(如好评率、店铺评分)未在搜索接口返回,需结合详情页补充。解决方案
搜索接口优先提取核心字段(价格、销量、标题),非核心字段通过后续
item_get
接口补充;对批量搜索任务,采用 “搜索列表→筛选目标商品→详情页补充” 的分步策略,减少无效请求。

反爬机制对抗

问题:蘑菇街搜索接口反爬严格,高频请求会触发 IP 封锁、参数失效或验证码。解决方案
代理 IP 轮换:使用高匿代理池,每 2-3 页切换 IP,避免单一 IP 被标记;请求频率控制:单 IP 每分钟请求≤3 次,两次请求间隔 2-4 秒(随机波动);UA 与 Cookie 多样化:随机切换移动端 User-Agent,搭配不同 Cookie 池降低风险;验证码处理:若触发验证码,暂停当前代理并切换 Cookie,使用 Selenium 手动验证(适合低频场景)。

分页限制与数据量控制

问题:蘑菇街搜索最多返回 50 页(1000 条),且超过 30 页后数据质量下降(重复或低相关商品)。解决方案
合理设置
page_limit
(建议≤20),平衡数据量与相关性;对大额需求,通过多关键词拆分搜索(如 “夏季连衣裙 长款”“夏季连衣裙 短款”);分页时检查商品 ID 去重,避免重复数据(示例代码中
unique_items
逻辑)。

六、最佳实践与合规要点

系统架构设计采用 “加密参数管理 + 分布式搜索” 架构,适配蘑菇街严格反爬:

参数管理层:集中生成与缓存
_m_h5_tk
,定时更新,支持多 Cookie 池切换;任务调度层:通过消息队列(如 RabbitMQ)分发搜索任务,控制单队列并发数≤2;采集层:多节点并行搜索,每个节点绑定独立代理与 Cookie,节点间间隔≥5 秒;存储层:用 Redis 缓存搜索结果(10 分钟过期),MySQL 存储历史趋势数据。

性能优化策略

异步批量搜索:使用
aiohttp
并发处理多关键词(控制并发数≤3),共享代理池;增量搜索:对相同关键词 + 条件,1 小时内返回缓存结果,减少重复请求;失败重试机制:对
403
(反爬)、
-1001
(参数失效)错误,自动重试 2 次(更换资源后)。

合规性与风险控制

遵守平台规则:蘑菇街开放平台明确禁止未授权爬取,商业使用需申请官方 API;频率限制:单 IP 日请求量≤300 次,避免对服务器造成压力;数据用途:不得将数据用于恶意竞争、虚假宣传,需注明来源 “蘑菇街”。

反爬适应性调整

定期(每周)检查接口参数变化(如
appKey
或加密逻辑更新),同步更新代码;当搜索接口升级(如新增
sign2
参数),通过抓包工具(Charles)逆向最新 JS 代码,提取新规则;建立降级方案:动态接口失败时,fallback 到 PC 端静态页面解析(适合数据精度要求不高的场景)。

七、总结

蘑菇街
item_search
接口的对接核心在于加密参数
_m_h5_tk
的动态维护
严格的反爬对抗策略分页数据的高效处理。开发者需重点关注:

搜索接口的参数加密逻辑(与详情页共用
_m_h5_tk
生成规则);代理池与 Cookie 池的精细化管理(应对高频请求与参数失效);分页限制与数据去重(平衡数据量与有效性)。

通过本文的技术方案,可构建稳定的商品搜索系统,为电商选品、市场分析等场景提供数据支持。实际应用中,需优先考虑官方 API 接入,确保合规使用数据。

© 版权声明

相关文章

暂无评论

none
暂无评论...