某鱼平台二手商品搜索接口开发实战:个人闲置与商家转让数据获取方案
在二手电商系统开发和闲置物品交易分析场景中,某鱼平台作为国内领先的 C2C 二手交易平台,其商品数据包含丰富的个人闲置物品信息、价格趋势和交易特征。本文将将篇将详细介绍某鱼平台 API 的调用方法,包括认证机制、搜索参数配置、反爬特有的数据解析及反爬策略,并提供可直接复用的 Python 代码实现,帮助开发者合规获取二手商品数据。
一、某鱼平台接口基础信息
某鱼平台提供的开放接口主要包括商品搜索、详情查询和卖家信息获取等功能,其中/api/v2/search/items
是获取二手商品列表的核心接口,特别适用于个人闲置物品的检索。
接口特点:
- 采用 Cookie+Token 的复合认证机制,部分接口需要登录状态
- 支持按物品类型、新旧程度、价格区间、地理位置等多维度筛选
- 包含二手商品特有的闲置原因、使用时长、瑕疵描述等字段
- 提供卖家信用、粉丝数、交易评价等社交化交易数据
接口端点:https://api.mouyu.com/api/v2/search/items
点击获取key和secret
二、认证机制与核心参数
1. 认证方式
某鱼接口采用较为严格的认证机制:
- 基础接口可通过公开 Cookie 访问,高级接口需要登录态 Token
- 登录态获取需通过手机验证码或扫码登录,生成
sessionid
和token
- 请求头需包含
User-Agent
、Referer
和Origin
等字段,模拟浏览器环境
2. 核心搜索参数
keyword
:搜索关键字(物品名称、品牌等,必填)category_id
:分类 ID(3C 数码 / 家居用品 / 服装鞋帽等,可选)item_status
:物品状态(0 - 全新,1 - 几乎全新,2 - 九成新,3 - 八成新,4 - 七成新及以下)price_min
/price_max
:价格区间(可选)distance
:距离范围(单位 km,1-10km,可选)city
:城市名称(按地理位置筛选,可选)sort_type
:排序方式(0 - 综合,1 - 价格低到高,2 - 价格高到低,3 - 最新发布)page
:页码(默认 1)limit
:每页条数(1-20,默认 10)is_promotion
:是否仅显示促销商品(true/false,可选)
3. 响应数据结构
total_count
:总结果数has_more
:是否有更多数据items
:商品列表数组filters
:可用筛选条件request_id
:请求 ID(用于问题排查)
三、完整代码实现
以下是 Python 实现的某鱼平台商品搜索功能,包含认证处理、搜索参数构建、数据解析和反爬策略:
import requests
import time
import random
import json
from typing import Dict, List, Optional, Any, Tuple
from urllib.parse import urlencode
import logging
from fake_useragent import UserAgent# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('mouyu_api')class MouyuSearchAPI:def __init__(self, cookie: str = "", proxy_pool: List[str] = None, use_login: bool = False):"""初始化某鱼搜索API客户端:param cookie: 平台Cookie:param proxy_pool: 代理IP池列表:param use_login: 是否使用登录态(获取更多数据)"""self.base_url = "https://api.mouyu.com"self.search_endpoint = "/api/v2/search/items"self.detail_endpoint = "/api/v2/items/detail"self.cookie = cookieself.use_login = use_loginself.proxy_pool = proxy_pool or []self.ua = UserAgent()self.session = self._init_session()self.token = self._extract_token()def _init_session(self) -> requests.Session:"""初始化请求会话,配置持久连接和基础头信息"""session = requests.Session()# 设置基础请求头base_headers = {"Accept": "application/json, text/plain, */*","Accept-Encoding": "gzip, deflate, br","Accept-Language": "zh-CN,zh;q=0.9","Connection": "keep-alive","Origin": "https://m.mouyu.com","Referer": "https://m.mouyu.com/","Cookie": self.cookie}session.headers.update(base_headers)return sessiondef _extract_token(self) -> Optional[str]:"""从Cookie中提取token(如果有)"""if not self.cookie:return Nonefor cookie in self.cookie.split(';'):key_value = cookie.strip().split('=')if len(key_value) == 2 and key_value[0] == 'token':return key_value[1]return Nonedef _get_random_headers(self) -> Dict[str, str]:"""生成随机请求头,降低反爬风险"""headers = {"User-Agent": self.ua.random,"X-Requested-With": "XMLHttpRequest","X-Csrf-Token": self.token if self.token else "","Timestamp": str(int(time.time() * 1000))}# 登录态额外头信息if self.use_login and self.token:headers["Authorization"] = f"Bearer {self.token}"return headersdef _get_proxy(self) -> Optional[Dict[str, str]]:"""从代理池获取随机代理"""if self.proxy_pool:proxy = random.choice(self.proxy_pool)return {"http": proxy, "https": proxy}return Nonedef _add_random_delay(self, base: float = 1.0, range: float = 1.0) -> None:"""添加随机延迟,模拟人类浏览行为"""delay = base + random.random() * rangetime.sleep(delay)logger.debug(f"延迟 {delay:.2f} 秒")def search_items(self,keyword: str,category_id: Optional[str] = None,item_status: Optional[int] = None,price_min: Optional[float] = None,price_max: Optional[float] = None,city: Optional[str] = None,sort_type: int = 0,page: int = 1,limit: int = 10) -> Dict[str, Any]:"""搜索某鱼平台商品:param keyword: 搜索关键字:param category_id: 分类ID:param item_status: 物品状态:param price_min: 最低价格:param price_max: 最高价格:param city: 城市名称:param sort_type: 排序方式:param page: 页码:param limit: 每页条数:return: 搜索结果"""# 限制每页最大条数limit = min(limit, 20)# 构建查询参数params: Dict[str, Any] = {"keyword": keyword,"sort_type": sort_type,"page": page,"limit": limit,"version": "2.3.0"}# 添加可选参数if category_id:params["category_id"] = category_idif item_status is not None:params["item_status"] = item_statusif price_min is not None:params["price_min"] = price_minif price_max is not None:params["price_max"] = price_maxif city:params["city"] = city# 准备请求配置headers = self._get_random_headers()proxy = self._get_proxy()url = f"{self.base_url}{self.search_endpoint}?{urlencode(params)}"try:# 随机延迟,避免被识别为爬虫self._add_random_delay()# 发送请求logger.info(f"搜索商品: {keyword}, 页码: {page}")response = self.session.get(url,headers=headers,proxies=proxy,timeout=15)# 检查响应状态if response.status_code == 401:return {"success": False, "error_msg": "认证失败,请检查Cookie或登录状态"}if response.status_code == 429:return {"success": False, "error_msg": "请求过于频繁,已被临时限制"}response.raise_for_status()# 解析响应result = response.json()# 处理API错误if result.get("code") != 0:logger.error(f"API错误: {result.get('message')}")return {"success": False,"error_code": result.get("code"),"error_msg": result.get("message")}# 解析搜索结果return self._parse_search_result(result.get("data", {}))except requests.exceptions.RequestException as e:logger.error(f"请求异常: {str(e)}")return {"success": False,"error_msg": f"请求异常: {str(e)}"}except Exception as e:logger.error(f"处理响应失败: {str(e)}")return {"success": False,"error_msg": f"处理响应失败: {str(e)}"}def _parse_search_result(self, raw_data: Dict[str, Any]) -> Dict[str, Any]:"""解析搜索结果为结构化数据"""# 分页信息pagination = {"total_count": raw_data.get("total_count", 0),"page": raw_data.get("page", 1),"limit": raw_data.get("limit", 10),"has_more": raw_data.get("has_more", False)}# 解析商品列表items = []for item in raw_data.get("items", []):# 处理二手商品特有信息used_info = {"status": item.get("item_status"), # 新旧程度"status_text": self._get_status_text(item.get("item_status")),"used_time": item.get("used_time"), # 使用时长"defect_desc": item.get("defect_desc") # 瑕疵描述}# 卖家信息seller = {"id": item.get("seller_id"),"nickname": item.get("seller_nickname"),"credit": item.get("seller_credit"), # 信用等级"fans_count": item.get("seller_fans_count"), # 粉丝数"location": item.get("seller_location") # 所在地}items.append({"item_id": item.get("item_id"),"title": item.get("title"),"price": float(item.get("price", 0)),"original_price": float(item.get("original_price", 0)),"discount": round(float(item.get("price", 0)) / float(item.get("original_price", 1)) * 100, 1) if item.get("original_price") else 0,"category": {"id": item.get("category_id"),"name": item.get("category_name")},"used_info": used_info,"seller": seller,"sales_status": item.get("sales_status"), # 在售/已售"publish_time": item.get("publish_time"), # 发布时间"view_count": item.get("view_count"), # 浏览数"comment_count": item.get("comment_count"), # 评论数"images": {"main": item.get("main_image"),"thumbnails": item.get("thumbnail_images", [])},"url": f"https://m.mouyu.com/item/{item.get('item_id')}.html","tags": item.get("tags", [])})# 解析可用筛选条件filters = self._parse_filters(raw_data.get("filters", {}))return {"success": True,"pagination": pagination,"items": items,"filters": filters,"request_id": raw_data.get("request_id")}def _get_status_text(self, status_code: Optional[int]) -> str:"""将物品状态代码转换为文本描述"""status_map = {0: "全新",1: "几乎全新",2: "九成新",3: "八成新",4: "七成新及以下"}return status_map.get(status_code, "未知")def _parse_filters(self, raw_filters: Dict[str, Any]) -> Dict[str, Any]:"""解析筛选条件"""filters = {}# 分类筛选if "categories" in raw_filters:filters["categories"] = [{"id": item.get("id"), "name": item.get("name"), "count": item.get("count")}for item in raw_filters["categories"]]# 物品状态筛选if "item_status" in raw_filters:filters["item_status"] = [{"code": item.get("code"), "name": item.get("name"), "count": item.get("count")}for item in raw_filters["item_status"]]# 价格区间筛选if "price_ranges" in raw_filters:filters["price_ranges"] = raw_filters["price_ranges"]return filtersdef batch_search(self,keyword: str,max_pages: int = 3,**kwargs) -> Dict[str, Any]:"""批量获取多页搜索结果:param keyword: 搜索关键字:param max_pages: 最大获取页数:param**kwargs: 其他搜索参数:return: 合并的搜索结果"""all_items = []current_page = 1has_more = Truewhile current_page <= max_pages and has_more:# 搜索当前页result = self.search_items(keyword=keyword,page=current_page,**kwargs)if not result.get("success"):return result# 收集商品数据all_items.extend(result.get("items", []))# 更新分页信息pagination = result.get("pagination", {})has_more = pagination.get("has_more", False)# 准备下一页current_page += 1return {"success": True,"total_items": len(all_items),"items": all_items,"summary": {"total_available": pagination.get("total_count", 0),"fetched_pages": current_page - 1}}# 使用示例
if __name__ == "__main__":# 替换为你的Cookie(可选,部分接口需要)COOKIE = "your_cookie_here"# 代理配置(可选)PROXY_POOL = [# "http://ip1:port",# "http://ip2:port"]# 初始化API客户端mouyu_api = MouyuSearchAPI(cookie=COOKIE,proxy_pool=PROXY_POOL,use_login=False # 如需获取更多数据可设为True(需有效Cookie))# 示例1:搜索二手手机phone_result = mouyu_api.search_items(keyword="iPhone 13",category_id="1001", # 假设1001是手机分类item_status=1, # 几乎全新price_min=3000,price_max=5000,city="上海",sort_type=1, # 价格从低到高page=1,limit=10)if phone_result["success"]:print(f"搜索结果: 找到 {phone_result['pagination']['total_count']} 件相关商品")if phone_result["items"]:item = phone_result["items"][0]print(f"标题: {item['title']}")print(f"价格: {item['price']}元 (原价: {item['original_price']}元, {item['discount']}折)")print(f"新旧程度: {item['used_info']['status_text']}")print(f"卖家: {item['seller']['nickname']} ({item['seller']['location']})")print(f"发布时间: {item['publish_time']}")# 示例2:批量搜索笔记本电脑# laptop_result = mouyu_api.batch_search(# keyword="笔记本电脑",# item_status=2, # 九成新# max_pages=2# )# # if laptop_result["success"]:# print(f"\n批量搜索: 共获取 {laptop_result['total_items']} 件商品")
四、代码核心功能解析
1. 反爬策略实现
- 随机生成 User-Agent,模拟不同设备和浏览器环境
- 实现动态延迟机制,根据请求频率自动调整等待时间
- 支持代理 IP 池配置,分散请求来源,降低单 IP 被封风险
- 完整模拟浏览器请求头,包含 Referer、Origin 等关键字段
2. 二手商品特色处理
- 专门解析物品新旧程度、使用时长、瑕疵描述等二手特有信息
- 计算折扣比例,直观展示二手商品性价比
- 提取卖家信用等级、粉丝数等社交化交易数据
- 区分商品销售状态(在售 / 已售),辅助市场分析
3. 认证机制适配
- 支持匿名访问和登录态两种模式,灵活应对不同接口权限
- 自动从 Cookie 中提取认证 Token,简化登录流程
- 处理 401/429 等常见认证和限流错误,提供友好提示
4. 数据结构化与扩展
- 按业务维度组织数据,区分基础信息、二手特性、卖家信息等
- 解析可用筛选条件,便于前端构建高级筛选界面
- 提供批量搜索功能,自动处理分页和结果合并
五、实战注意事项
1. 接口权限与限制
- 匿名访问有严格的频率限制(通常每分钟不超过 10 次请求)
- 登录态访问需定期更新 Cookie,避免失效
- 部分敏感数据(如卖家联系方式)需要特殊权限
2. 反爬与合规
- 控制请求频率,建议单 IP 日请求不超过 1000 次
- 避免使用多线程 / 多进程高并发请求,模拟人类浏览速度
- 数据使用需遵守平台用户协议,不得用于商业竞品分析
3. 搜索策略优化
- 二手商品搜索建议结合新旧程度和价格区间筛选,提高精准度
- 按地理位置筛选时,使用城市名而非区域名,覆盖范围更广
- 批量获取数据时,合理设置
max_pages
参数,避免触发限制
4. 数据处理建议
- 标题中常包含新旧程度描述,可通过文本分析提取关键信息
- 价格波动较大,建议多次采样取平均值
- 对已售商品数据进行分析,可获取真实成交价格
六、功能扩展方向
- 开发二手商品价格评估模型,基于同类商品数据预测合理价格
- 构建卖家信用评估系统,结合多维度数据识别优质卖家
- 实现商品降价提醒功能,监控目标商品价格变化
- 开发瑕疵识别工具,基于描述文本自动分类商品瑕疵类型
通过本文提供的方案,开发者可以合规获取某鱼平台的二手商品数据,为二手交易分析、价格监控等应用提供支持。实际开发中,建议特别注意接口调用频率和数据使用合规性,尊重平台规则和用户隐私。