聚美优品的商品搜索功能(对应item_search接口,非官方命名)是通过关键词、分类、价格等条件批量获取商品列表的核心工具,广泛应用于比价平台、导购系统、市场分析等场景。由于聚美优品无公开官方 API,开发者需通过合规的页面解析或第三方服务实现对接。本文将系统讲解item_search接口的对接逻辑、参数解析、技术实现及反爬应对,帮助开发者构建稳定高效的商品搜索数据获取系统。
一、接口基础认知(核心功能与场景)
核心功能聚美优品item_search接口通过多维度筛选条件(关键词、分类、价格区间等)返回符合条件的商品列表,核心数据包括:
基础信息:商品 ID(item_id)、标题、主图、品牌、类目、详情页 URL
价格信息:原价、折扣价、是否参与促销(如 “限时折扣”“满减”)
交易数据:销量(如 “已售 1000+”)、评分、库存状态(有货 / 无货)
筛选标识:是否为自营、是否包邮、促销标签(如 “爆款”“新品”)
典型应用场景
比价平台:按关键词(如 “雅诗兰黛小棕瓶”)搜索聚美商品,与其他平台价格对比
导购系统:根据用户筛选条件(如 “面膜 + 价格 50-100 元 + 销量优先”)推荐商品
市场分析:批量获取 “口红” 类目商品,统计价格分布、品牌占比、折扣力度
库存监控:跟踪特定关键词(如 “防晒霜”)下商品的库存变化,触发补货提醒
接口特性
非官方性:无公开 API,依赖页面解析,受页面结构变更影响较大
多条件筛选:支持关键词、分类、价格、销量、促销等 10 + 维度组合筛选
分页机制:默认每页 20-30 条,最多支持 50 页(约 1000-1500 条结果)
反爬严格性:搜索接口为高频访问入口,反爬机制(IP 限制、请求频率监控)比详情接口更严格
动态渲染:部分结果(如分页加载、筛选后数据)通过 AJAX 接口动态返回
二、对接前置准备(环境与参数解析)
开发环境
开发语言:Python(推荐,生态丰富,适合快速迭代与解析工具集成)
核心库:
网络请求:requests(同步请求)、aiohttp(异步批量搜索,效率更高)
页面解析:BeautifulSoup(HTML 静态解析)、lxml(支持 XPath,高效提取)
反爬工具:fake_useragent(随机 User-Agent)、proxy_pool(代理 IP 池管理)
数据处理:re(正则提取动态数据)、pandas(列表数据清洗)
搜索 URL 结构与核心参数聚美优品搜索页基础 URL 为:https://search.jumei.com/search,核心参数如下(通过 URL 查询字符串传递):
筛选条件 参数名 示例值 说明
关键词 keyword 雅诗兰黛 面膜 支持中文、英文及组合词(如 “进口 口红”)
分类 ID cat 1005(美妆)、1026(护肤) 分类 ID 需通过解析首页分类导航获取
价格区间(始) startPrice 50 最低价格(元),如50表示≥50 元
价格区间(终) endPrice 200 最高价格(元),如200表示≤200 元
排序方式 sort sale_desc(销量降序) 见 “排序参数表”
分页 page 1 2 ... 50 页码,默认 1,最大 50
是否自营 is_self 1(是)、0(否) 1 - 仅显示聚美自营商品
排序参数表聚美搜索支持多种排序方式,对应sort参数值如下:
排序方式 sort参数值 适用场景
综合推荐 default 默认排序,平衡销量与价格
销量降序 sale_desc 筛选热门商品(高转化优先)
价格升序 price_asc 低价引流场景
价格降序 price_desc 高端商品筛选
最新上架 new_desc 新品监控
分类 ID 获取分类 ID(cat)需通过解析聚美首页分类导航获取,示例步骤:
访问聚美首页(https://www.jumei.com/),通过浏览器开发者工具查看分类菜单 HTML;
提取a标签的href(如/category/1005.html中的1005即为美妆分类 ID);
常用分类 ID(示例):
美妆:1005;护肤:1026;个人护理:1032;零食:1112。
三、接口调用流程(基于页面解析)
以 “搜索价格 50-200 元的面膜,按销量降序排序” 为例,核心流程为参数组装→URL 构建→请求发送→列表解析→分页遍历:
URL 构建示例结合参数生成目标搜索 URL:
python
运行
keyword = "面膜"
start_price = 50
end_price = 200
sort = "sale_desc" # 销量降序
page = 1
url = f"https://search.jumei.com/search?keyword={keyword}&startPrice={start_price}&endPrice={end_price}&sort={sort}&page={page}"
请求头与反爬伪装需模拟浏览器请求头,关键字段包括User-Agent、Referer、Cookie(提升可信度):
python
运行
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
"Referer": "https://www.jumei.com/",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8",
"Cookie": "uid=anonymous; sid=xxx; _jme_tuid=xxx" # 从浏览器获取匿名Cookie
}
页面解析与数据提取搜索结果列表通常在 HTML 的
- 标签内,每条商品信息包含以下核心字段:
字段 解析方式(CSS 选择器示例) 说明
商品 ID 从a标签的href中提取(如/item/123.html得123) 唯一标识
标题 .product-title的文本 如 “敷尔佳白膜医用面膜”
主图 .product-img img的src属性 商品主图 URL
原价 .original-price的文本(去除 “¥”) 如 “198”
折扣价 .current-price的文本(去除 “¥”) 如 “128”
销量 .sales-count的文本(提取数字) 如 “已售 3000+” 提取 “3000”
库存状态 .stock-status的文本 如 “有货”“售罄”
促销标签 .promotion-tag的文本 如 “限时折扣”“满 199 减 50”
动态加载与分页处理
部分搜索结果(如分页超过第 5 页)通过 AJAX 接口动态加载,接口格式通常为:https://search.jumei.com/ajax/search?keyword=xxx&page=6(需抓包确认);
分页终止条件:当前页无商品列表(products-list为空)或页码超过 50;
分页间隔:每页请求间隔 5-8 秒(随机波动),避免高频访问触发反爬。
四、代码实现示例(Python)
以下是item_search接口的完整实现,包含多条件筛选、分页遍历、静态 + 动态数据解析及反爬处理:
import requests
import time
import random
import re
from urllib.parse import quote
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from typing import List, Dictclass JumeiSearchApi:
def init(self, proxy_pool: List[str] = None):
self.base_url = "https://search.jumei.com/search"
self.ajax_url = "https://search.jumei.com/ajax/search" # 动态分页接口
self.ua = UserAgent()
self.proxy_pool = proxy_pool # 代理池列表,如["http://ip:port", ...]
self.cat_id_map = self._load_category_map() # 分类ID映射表def _load_category_map(self) -> Dict[str, str]: """加载分类名称-ID映射表(简化版)""" return { "美妆": "1005", "护肤": "1026", "个人护理": "1032", "零食": "1112" } def _get_headers(self) -> Dict[str, str]: """生成随机请求头""" return { "User-Agent": self.ua.random, "Referer": "https://www.jumei.com/", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Cookie": "uid=anonymous; sid=xxx; _jme_tuid=xxx" # 替换为实际Cookie } 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 _clean_price(self, price_str: str) -> float: """清洗价格字符串(去除¥、逗号等)""" if not price_str: return 0.0 price_str = re.sub(r"[^\d.]", "", price_str) return float(price_str) if price_str else 0.0 def _clean_sales(self, sales_str: str) -> int: """清洗销量字符串(提取数字)""" if not sales_str: return 0 sales_num = re.search(r"\d+", sales_str) return int(sales_num.group()) if sales_num else 0 def _parse_item(self, item_soup) -> Dict[str, str]: """解析单条商品数据""" # 提取商品ID link = item_soup.select_one("a.product-link")["href"] item_id = re.search(r"/item/(\d+)\.html", link).group(1) if link else "" # 提取核心字段 return { "item_id": item_id, "title": item_soup.select_one(".product-title")?.text.strip() or "", "main_image": item_soup.select_one(".product-img img")?.get("src") or "", "url": f"https://www.jumei.com{link}" if link.startswith("/") else link, "price": { "original": self._clean_price(item_soup.select_one(".original-price")?.text.strip() or ""), "current": self._clean_price(item_soup.select_one(".current-price")?.text.strip() or "") }, "sales": self._clean_sales(item_soup.select_one(".sales-count")?.text.strip() or ""), "stock_status": item_soup.select_one(".stock-status")?.text.strip() or "", "promotion_tag": item_soup.select_one(".promotion-tag")?.text.strip() or "" } def _parse_static_page(self, html: str) -> List[Dict]: """解析静态HTML页面的商品列表""" soup = BeautifulSoup(html, "lxml") item_list = soup.select("ul.products-list > li") return [self._parse_item(item) for item in item_list if item] def _fetch_ajax_page(self, params: Dict, headers: Dict, proxy: Dict) -> List[Dict]: """调用动态接口获取分页商品""" try: response = requests.get( url=self.ajax_url, params=params, headers=headers, proxies=proxy, timeout=10 ) response.raise_for_status() data = response.json() # 动态接口返回的HTML片段解析 html = data.get("data", {}).get("html", "") return self._parse_static_page(html) except Exception as e: print(f"动态接口获取失败: {str(e)}") return [] def item_search(self, keyword: str = "", cat: str = "", start_price: float = None, end_price: float = None, sort: str = "default", page_limit: int = 5, is_self: int = 0) -> Dict: """ 搜索聚美优品商品列表 :param keyword: 搜索关键词(与cat二选一) :param cat: 分类名称(如“美妆”,需在cat_id_map中存在)或分类ID :param start_price: 起始价格(元) :param end_price: 结束价格(元) :param sort: 排序方式(default/sale_desc/price_asc等) :param page_limit: 最大页数(避免过度爬取,默认5) :param is_self: 是否自营(0-全部,1-自营) :return: 标准化搜索结果 """ try: # 1. 参数预处理 if not keyword and not cat: return {"success": False, "error_msg": "关键词(keyword)和分类(cat)至少需提供一个"} # 转换分类名称为ID if cat in self.cat_id_map: cat_id = self.cat_id_map[cat] else: cat_id = cat # 若为ID则直接使用 # 编码关键词(支持中文) encoded_keyword = quote(keyword, encoding="utf-8") if keyword else "" all_items = [] current_page = 1 while current_page <= page_limit: # 构建参数 params = { "keyword": encoded_keyword, "sort": sort, "page": current_page, "is_self": is_self } if cat_id: params["cat"] = cat_id if start_price is not None: params["startPrice"] = start_price if end_price is not None: params["endPrice"] = end_price # 发送请求(带随机延迟) time.sleep(random.uniform(3, 6)) # 间隔3-6秒,避免反爬 headers = self._get_headers() proxy = self._get_proxy() # 分页≤5用静态页面,>5用动态接口(根据实际抓包调整) if current_page <= 5: response = requests.get( url=self.base_url, params=params, headers=headers, proxies=proxy, timeout=10 ) response.raise_for_status() items = self._parse_static_page(response.text) else: items = self._fetch_ajax_page(params, headers, proxy) if not items: break # 无数据,终止分页 all_items.extend(items) current_page += 1 return { "success": True, "total": len(all_items), "page_processed": current_page - 1, "items": all_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}使用示例
if name == "main":
# 代理池(替换为有效代理) PROXIES = [ "http://123.45.67.89:8888", "http://98.76.54.32:8080" ] # 初始化API客户端 search_api = JumeiSearchApi(proxy_pool=PROXIES) # 搜索“面膜”,价格50-200元,按销量降序,最多3页 result = search_api.item_search( keyword="面膜", start_price=50, end_price=200, sort="sale_desc", page_limit=3, is_self=0 # 包含非自营 ) if result["success"]: print(f"搜索成功:共找到 {result['total']} 件商品,处理 {result['page_processed']} 页") for i, item in enumerate(result["items"][:5]): # 打印前5条 print(f"\n商品 {i+1}:") print(f"标题:{item['title'][:50]}...") # 截断长标题 print(f"价格:原价 {item['price']['original']} 元 → 折扣价 {item['price']['current']} 元") print(f"销量:{item['sales']} 件 | 库存状态:{item['stock_status']}") if item['promotion_tag']: print(f"促销:{item['promotion_tag']}") print(f"详情页:{item['url']}") else: print(f"搜索失败:{result['error_msg']}(错误码:{result.get('code')})") 五、关键技术难点与解决方案动态分页数据解析
问题:聚美搜索结果分页超过一定页数(如第 5 页)后,数据通过 AJAX 接口动态加载,静态 HTML 中无内容。
解决方案:
抓包分析动态接口(如https://search.jumei.com/ajax/search),确定其参数与静态 URL 一致(keyword、page等);
区分分页逻辑:前 5 页用静态 HTML 解析,第 6 页及以后调用动态接口,统一处理返回的 HTML 片段;
示例动态接口响应处理:提取data.html字段(包含商品列表 HTML),复用静态解析函数_parse_static_page。
反爬机制对抗
问题:搜索接口高频访问易触发 IP 封锁(403 错误)、验证码或空白页面。
解决方案:
代理 IP 轮换:每 2-3 页切换一次代理,使用高匿代理(避免透明代理被识别),确保代理存活周期≥10 分钟;
请求间隔动态调整:正常响应时间隔 3-6 秒,若返回 403 则延长至 10-15 秒,并切换代理;
Cookie 池策略:通过多个浏览器会话获取不同 Cookie,随机携带(模拟多用户搜索行为);
验证码处理:若触发图形验证码,暂停当前代理,记录 IP 并加入黑名单,切换新代理后重试。
搜索结果去重
问题:同一商品可能在不同分页重复出现(如因排序算法波动)。
解决方案:
基于item_id去重:用集合存储已获取的item_id,过滤重复数据;
示例代码:
python
运行
unique_items = []
seen_ids = set()
for item in all_items:
if item["item_id"] not in seen_ids:
seen_ids.add(item["item_id"])
unique_items.append(item)
分类 ID 动态获取
问题:聚美分类体系可能调整(如新增 “男士护肤” 分类),静态映射表失效。
解决方案:
定期(每月)爬取首页分类导航,动态更新cat_id_map;
爬取逻辑:解析下的a标签,提取href中的 ID 与分类名称。
六、最佳实践与合规要点
系统架构设计采用 “分布式搜索采集” 架构,支持高并发与容错:
任务分发层:通过消息队列(如 RabbitMQ)分发搜索任务(关键词 + 分类组合);
采集层:多节点并行采集,每个节点绑定独立代理池,避免相互影响;
存储层:用 Redis 缓存热门搜索结果(15 分钟过期),MySQL 存储历史数据(用于趋势分析);
监控层:实时监控代理存活率、请求成功率、反爬触发次数,异常时自动告警。
性能优化策略
异步批量搜索:使用aiohttp并发处理多个关键词(如同时搜索 “面膜”“口红”),控制并发数≤5;
按需解析:仅提取业务所需字段(如列表页无需解析商品详情 HTML);
热点抑制:对同一关键词 + 条件的搜索请求,1 分钟内仅处理 1 次(返回缓存结果)。
合规性与风险控制
频率限制:单 IP 日搜索请求≤500 次,单关键词日搜索≤100 次,避免对聚美服务器造成压力;
数据使用边界:不得将搜索结果用于恶意比价、虚假宣传或商业售卖,需注明数据来源 “聚美优品”;
遵守 robots 协议:检查 https://search.jumei.com/robots.txt,不爬取禁止路径(如/search/*?*&is_self=1可能限制)。
反爬适应性调整
定期(每周)检查搜索页结构与动态接口变化,更新解析规则;
当反爬机制升级(如新增 JS 加密参数),快速切换至备用方案(如 Selenium 驱动浏览器渲染);
建立反爬特征库(如 403 页面特征、验证码页面特征),自动识别并触发应对策略。
七、总结
聚美优品item_search接口的对接核心在于多条件参数的正确映射、静态 + 动态数据的协同解析及高强度反爬对抗。开发者需重点关注:
搜索参数的灵活组合(关键词、分类、价格等),提升结果精准度;
动态分页接口的识别与调用(获取全量数据);
代理池与请求频率的精细化控制(避免触发反爬)。
通过本文的技术方案,可构建稳定的商品搜索系统,为比价、导购、市场分析等场景提供可靠数据支持。实际应用中,需根据聚美优品的反爬策略动态调整方案,平衡数据获取效率与合规性