洋码头 item_search 接口对接全攻略:从入门到精通

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

洋码头作为国内头部跨境电商平台,以 “海外直购” 为核心卖点,商品覆盖美妆、奢侈品、母婴、保健品等品类,其搜索功能(
item_search
接口,非官方命名)是获取跨境商品列表的核心入口。数据包含采购地、关税政策、物流时效等跨境特有字段,对跨境比价、消费趋势分析、选品决策等场景具有重要价值。由于平台无公开官方 API,开发者需通过页面解析实现搜索对接。本文系统讲解接口逻辑、参数解析、技术实现及反爬策略,助你构建稳定的跨境商品列表获取系统。

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

核心功能洋码头
item_search
接口通过关键词、分类、价格等条件筛选商品,返回符合条件的列表数据,核心字段聚焦跨境特性:

基础信息:商品 ID(
item_id
)、标题(含版本 / 规格)、主图、品牌、类目(如 “美妆护肤”“母婴用品”)、详情页 URL价格信息:售价(含税费)、原价、折扣(如 “黑五 8 折”)、运费政策(如 “满 399 元包直邮”)、关税说明跨境属性:采购地(如 “日本东京”)、物流类型(直邮 / 保税仓)、配送时效(如 “5-10 天到货”)、是否正品溯源商品特性:规格(如 “100ml”)、版本(如 “本土版”“国际版”)、适用人群(如 “孕妇可用”)交易数据:销量(如 “已售 500+”)、评价数、好评率、库存状态(如 “海外现货”)卖家信息:卖家名称、所在地(如 “韩国首尔”)、资质(如 “认证买手”)

典型应用场景

跨境比价工具:按 “兰蔻小黑瓶” 搜索,对比不同卖家的含税价、运费及采购地选品分析:筛选 “保税仓发货 + 月销 300+” 的母婴用品,优化跨境电商供应链促销监控:跟踪 “黑五” 期间 “奢侈品包袋” 类目的折扣力度与库存变化消费趋势研究:统计 “欧美彩妆” 的采购地分布、版本偏好(如 “美版” vs “欧版”)

接口特性

跨境专业性:筛选条件与数据字段深度结合跨境场景(如 “采购地筛选”“关税包含与否”)非官方性:依赖 HTML 解析,无公开 API,动态加载内容多(如筛选后结果、分页数据)反爬机制:包含 IP 限制、User-Agent 校验、Cookie 验证、动态参数(如
sign
)、请求频率监控分页结构:默认每页 20 条,最多支持 50 页(约 1000 条结果),分页参数通过 URL 或 AJAX 传递混合加载:基础列表数据静态嵌入 HTML,部分筛选结果通过带签名的 AJAX 接口动态加载

二、对接前置准备(参数与 URL 结构)

开发环境

开发语言:Python(推荐,生态丰富,适合处理复杂 HTML 与反爬)核心库:
网络请求:
requests
(同步)、
aiohttp
(异步批量搜索)页面解析:
BeautifulSoup
(静态 HTML)、
lxml
(XPath 提取列表数据)反爬工具:
fake_useragent
(随机 UA)、
proxy_pool
(代理 IP 池)、
execjs
(处理动态签名)数据处理:
re
(正则提取价格、采购地)、
urllib.parse
(URL 参数编码)

搜索 URL 结构与核心参数洋码头搜索页基础 URL 为:
https://www.yangmatou.com/search/{关键词}
,核心参数通过查询字符串传递:

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

兰蔻小黑瓶
 → 编码后为
%E5%85%B0%E8%94%BB%E5%B0%8F%E9%BB%91%E7%93%B6
支持商品名、品牌、规格
分类 ID
categoryId

101
(美妆护肤)、
203
(母婴用品)
分类 ID 需从首页分类导航解析获取
价格区间(始)
priceMin

500
最低价格(元,含税费)
价格区间(终)
priceMax

1000
最高价格(元,含税费)
采购地
purchaseArea

us
(美国)、
jp
(日本)
地区编码需抓包获取(如 “us” 对应美国)
物流类型
logistics

direct
(直邮)、
bonded
(保税仓)
筛选发货方式
排序方式
sort

sales
(销量降序)、
price_asc
(价格升序)
见 “排序参数表”
分页
page

1
 
2
 … 
50
页码,默认 1,最大 50

排序参数表洋码头搜索支持多种排序方式,对应
sort
参数值如下:

排序方式
sort
参数值
适用场景
综合推荐 空值 默认排序,平衡相关性与销量
销量降序
sales
筛选爆款商品(如 “已售 500+”)
价格升序
price_asc
低价商品筛选(如平价彩妆)
价格降序
price_desc
高端商品筛选(如奢侈品)
最新上架
new
新品监控(如 “刚上架的海外新品”)

分类 ID 与采购地编码获取

分类 ID:访问洋码头首页(
https://www.yangmatou.com
),通过开发者工具查看分类菜单的
href
(如
/category/101
中的
101
为美妆护肤分类 ID);采购地编码:选择 “采购地” 筛选后,URL 中
purchaseArea
参数值即为编码(如
purchaseArea=jp
对应日本)。

三、接口调用流程(基于页面解析)

以 “搜索兰蔻小黑瓶,价格 500-1000 元,采购地法国,按销量降序排序” 为例,流程为参数组装→URL 构建→请求发送→列表解析→分页遍历

URL 构建示例组合参数生成目标搜索 URL:

python



keyword = "兰蔻小黑瓶"
category_id = "101"  # 美妆护肤分类ID
price_min = 500
price_max = 1000
purchase_area = "fr"  # 法国编码
logistics = "direct"  # 直邮
sort = "sales"  # 销量降序
page = 1
# 关键词URL编码
encoded_keyword = urllib.parse.quote(keyword, encoding="utf-8")
# 构建URL
url = f"https://www.yangmatou.com/search/{encoded_keyword}?categoryId={category_id}&priceMin={price_min}&priceMax={price_max}&purchaseArea={purchase_area}&logistics={logistics}&sort={sort}&page={page}"

请求头与反爬伪装模拟浏览器请求头,需包含登录态 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.yangmatou.com/",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Cookie": "user_id=xxx; token=xxx; session_id=xxx"  # 登录后获取的Cookie
}

页面解析与数据提取搜索结果列表通常在 HTML 的
<div class="product-item">
标签内,每条商品信息包含以下核心字段:

字段 解析方式(CSS 选择器示例) 说明
商品 ID
a
标签的
href
中提取(如
/goods/123456

123456
唯一标识
标题
.product-title
的文本
如 “兰蔻小黑瓶精华 100ml 法版”
主图
.product-img img

src
属性
商品主图 URL
售价(含税)
.price-current
的文本(去除 “¥”)
如 “899.00”(元)
原价
.price-original
的文本(去除 “¥”)
如 “1099.00”(元)
采购地
.purchase-area
的文本
如 “法国・巴黎”
物流类型
.logistics-tag
的文本
如 “海外直邮”
月销量
.sales-count
的文本(提取数字)
如 “已售 520 件” 提取 “520”

动态筛选与签名参数处理部分高级筛选(如 “正品溯源”“卖家等级”)会触发 AJAX 请求,接口需携带动态
sign
参数:

动态接口示例:
https://api.yangmatou.com/search?keyword=xxx&sign=xxx

sign
参数生成逻辑与
item_get
接口类似(参数拼接 + 密钥加密),需通过逆向 JS 代码获取;处理方式:对简单筛选(关键词、价格、分类)优先使用 URL 参数,复杂筛选需调用带签名的动态接口。

分页处理

分页通过
page
参数控制,前 50 页为有效数据,超过则返回重复内容;终止条件:当前页商品数量 < 20(最后一页)或页码≥50;分页间隔:每页请求间隔 3-5 秒(随机波动),跨境平台对高频访问敏感,需严格控制频率。

四、代码实现示例(Python)

以下是
item_search
接口的完整实现,包含多条件筛选、分页遍历、数据解析及反爬处理(签名生成逻辑需根据实际 JS 逆向补充):



import requests
import time
import random
import re
import urllib.parse
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from typing import List, Dict
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class YangmatouSearchApi:
    def __init__(self, proxy_pool: List[str] = None, cookie: str = ""):
        self.base_url = "https://www.yangmatou.com/search/{keyword}"
        self.api_url = "https://api.yangmatou.com/search"  # 动态筛选接口
        self.ua = UserAgent()
        self.proxy_pool = proxy_pool  # 代理池列表,如["http://ip:port", ...]
        self.cookie = cookie  # 登录态Cookie
        self.secret_key = "xxx"  # 从JS逆向获取的签名密钥(需实际破解)
        # 分类ID映射(简化版)
        self.category_map = {
            "美妆护肤": "101",
            "母婴用品": "203",
            "奢侈品": "302",
            "保健品": "401"
        }
        # 采购地编码映射(简化版)
        self.area_map = {
            "美国": "us",
            "日本": "jp",
            "法国": "fr",
            "韩国": "kr"
        }
 
    def _get_headers(self) -> Dict[str, str]:
        """生成随机请求头"""
        headers = {
            "User-Agent": self.ua.random,
            "Referer": "https://www.yangmatou.com/",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
        }
        if self.cookie:
            headers["Cookie"] = self.cookie
        return headers
 
    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 _generate_sign(self, params: Dict[str, str]) -> str:
        """生成动态接口签名(需根据实际JS逆向逻辑实现)"""
        # 示例:按key排序后拼接+密钥,MD5加密
        sorted_params = sorted(params.items(), key=lambda x: x[0])
        sign_str = "&".join([f"{k}={v}" for k, v in sorted_params]) + self.secret_key
        return hashlib.md5(sign_str.encode()).hexdigest()
 
    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-title")["href"]
        item_id = re.search(r"/goods/(d+)", link).group(1) if link else ""
 
        # 提取版本信息(跨境核心字段)
        title = item_soup.select_one(".product-title")?.text.strip() or ""
        version = re.search(r"[美日法韩中]版", title).group() if re.search(r"[美日法韩中]版", title) else ""
 
        return {
            "item_id": item_id,
            "title": title,
            "main_image": item_soup.select_one(".product-img img")?.get("src") or "",
            "url": f"https://www.yangmatou.com{link}" if link.startswith("/") else link,
            "price": {
                "current": self._clean_price(item_soup.select_one(".price-current")?.text.strip() or ""),
                "original": self._clean_price(item_soup.select_one(".price-original")?.text.strip() or ""),
                "tax_included": "含税" in (item_soup.select_one(".price-tag")?.text or "")
            },
            "cross_border": {
                "purchase_area": item_soup.select_one(".purchase-area")?.text.strip() or "",
                "logistics": item_soup.select_one(".logistics-tag")?.text.strip() or "",
                "delivery_time": item_soup.select_one(".delivery-time")?.text.strip() or ""
            },
            "sales": {
                "monthly": self._clean_sales(item_soup.select_one(".sales-count")?.text.strip() or ""),
                "comment_count": self._clean_sales(item_soup.select_one(".comment-count")?.text.strip() or "")
            },
            "seller": {
                "name": item_soup.select_one(".seller-name")?.text.strip() or "",
                "level": item_soup.select_one(".seller-level")?.text.strip() or ""
            },
            "version": version  # 版本信息(如“法版”)
        }
 
    def _parse_page(self, html: str) -> List[Dict]:
        """解析页面的商品列表"""
        soup = BeautifulSoup(html, "lxml")
        # 商品列表容器(需根据实际页面结构调整)
        item_list = soup.select("div.product-item")
        return [self._parse_item(item) for item in item_list if item]
 
    def _get_total_pages(self, html: str) -> int:
        """获取总页数"""
        soup = BeautifulSoup(html, "lxml")
        page_box = soup.select_one(".pagination")
        if not page_box:
            return 1
        # 提取最后一页页码
        last_page = page_box.select("a")[-1].text.strip()
        return int(last_page) if last_page.isdigit() else 1
 
    def item_search(self, 
                   keyword: str = "", 
                   category: str = "", 
                   price_min: float = None, 
                   price_max: float = None, 
                   purchase_area: str = "", 
                   logistics: str = "", 
                   sort: str = "", 
                   page_limit: int = 5) -> Dict:
        """
        搜索洋码头商品列表
        :param keyword: 搜索关键词
        :param category: 分类名称(如“美妆护肤”)或分类ID
        :param price_min: 最低价格(元)
        :param price_max: 最高价格(元)
        :param purchase_area: 采购地名称(如“法国”)或编码(如“fr”)
        :param logistics: 物流类型(direct/bonded)
        :param sort: 排序方式(sales/price_asc等)
        :param page_limit: 最大页数(默认5)
        :return: 标准化搜索结果
        """
        try:
            # 1. 参数预处理
            if not keyword and not category:
                return {"success": False, "error_msg": "关键词(keyword)和分类(category)至少需提供一个"}
            # 转换分类名称为ID
            if category in self.category_map:
                cid = self.category_map[category]
            else:
                cid = category if category else ""
            # 转换采购地名称为编码
            if purchase_area in self.area_map:
                area_code = self.area_map[purchase_area]
            else:
                area_code = purchase_area if purchase_area else ""
            # 编码关键词
            encoded_keyword = urllib.parse.quote(keyword, encoding="utf-8") if keyword else ""
 
            all_items = []
            current_page = 1
 
            while current_page <= page_limit:
                # 构建参数
                params = {
                    "page": current_page
                }
                if cid:
                    params["categoryId"] = cid
                if price_min is not None:
                    params["priceMin"] = price_min
                if price_max is not None:
                    params["priceMax"] = price_max
                if area_code:
                    params["purchaseArea"] = area_code
                if logistics:
                    params["logistics"] = logistics
                if sort:
                    params["sort"] = sort
 
                # 构建URL(简单筛选用页面URL,复杂筛选用API)
                if encoded_keyword:
                    url = self.base_url.format(keyword=encoded_keyword)
                else:
                    url = f"https://www.yangmatou.com/category/{cid}"  # 无关键词时用分类页
 
                # 发送请求(带随机延迟)
                time.sleep(random.uniform(3, 5))  # 跨境平台延迟需更高
                headers = self._get_headers()
                proxy = self._get_proxy()
 
                response = requests.get(
                    url=url,
                    params=params,
                    headers=headers,
                    proxies=proxy,
                    timeout=10
                )
                response.raise_for_status()
                html = response.text
 
                # 解析当前页商品
                items = self._parse_page(html)
                if not items:
                    break  # 无数据,终止分页
 
                all_items.extend(items)
 
                # 获取总页数(仅第一页需要)
                if current_page == 1:
                    total_pages = self._get_total_pages(html)
                    # 修正最大页数(不超过page_limit和50)
                    total_pages = min(total_pages, page_limit, 50)
                    if total_pages < current_page:
                        break
 
                # 若当前页是最后一页,终止
                if current_page >= total_pages:
                    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,
                "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__":
    # 代理池(替换为有效代理)
    PROXIES = [
        "http://123.45.67.89:8888",
        "http://98.76.54.32:8080"
    ]
    # 登录态Cookie(从浏览器获取)
    COOKIE = "user_id=xxx; token=xxx; session_id=xxx"
 
    # 初始化API客户端(需补充secret_key,通过JS逆向获取)
    search_api = YangmatouSearchApi(proxy_pool=PROXIES, cookie=COOKIE)
 
    # 搜索“兰蔻小黑瓶”,分类“美妆护肤”,价格500-1000元,采购地法国,直邮,按销量降序,最多3页
    result = search_api.item_search(
        keyword="兰蔻小黑瓶",
        category="美妆护肤",
        price_min=500,
        price_max=1000,
        purchase_area="法国",
        logistics="direct",
        sort="sales",
        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']}({'含税' if item['price']['tax_included'] else '不含税'}) | 原价:¥{item['price']['original']}")
            print(f"跨境信息:{item['cross_border']['logistics']} | 采购地:{item['cross_border']['purchase_area']} | 时效:{item['cross_border']['delivery_time']}")
            print(f"版本:{item['version']} | 月销:{item['sales']['monthly']}件 | 评价:{item['sales']['comment_count']}条")
            print(f"卖家:{item['seller']['name']}(等级:{item['seller']['level']})")
            print(f"详情页:{item['url']}")
    else:
        print(f"搜索失败:{result['error_msg']}(错误码:{result.get('code')})")
五、关键技术难点与解决方案

跨境筛选条件映射(采购地、物流类型)

问题:采购地(如 “法国”)、物流类型(如 “直邮”)是跨境搜索的核心维度,但参数值为编码(如 “fr” 对应法国),需精准映射。解决方案
建立名称 – 编码映射表(如
area_map
中 “法国” 对应 “fr”),用户输入名称时自动转换为参数;定期抓包更新映射关系(平台可能调整编码),确保筛选条件生效;示例代码中通过
_parse_item
函数提取
purchase_area

logistics
字段,直接反映商品的跨境属性。

动态签名参数处理(复杂筛选场景)

问题:使用 “正品溯源”“卖家等级” 等高级筛选时,平台会通过带
sign
参数的 AJAX 接口返回结果,直接请求 URL 参数无效。解决方案
区分筛选复杂度:基础筛选(关键词、价格、分类)优先使用 URL 参数,避免触发动态接口;逆向签名逻辑:对必须使用动态接口的场景,通过 JS 逆向获取
sign
生成规则,用 Python 复现(如示例中
_generate_sign
函数);缓存签名参数:同一批筛选条件的
sign
参数短期内可复用,减少重复计算。

版本信息提取(跨境商品核心)

问题:版本(如 “法版”“美版”)是跨境商品的关键差异点(影响成分、价格),但常嵌入标题文本中(如 “兰蔻小黑瓶 法版”),提取难度大。解决方案
正则匹配标题中的版本关键词(
[美日法韩中]版
),精准提取版本信息;结合商品采购地辅助验证(如采购地为法国,版本大概率为 “法版”);示例代码中通过
re.search
从标题中提取版本,确保核心差异点不丢失。

反爬机制对抗

问题:洋码头对跨境数据保护严格,反爬机制包括 IP 封锁、签名验证、Cookie 时效限制、请求频率监控,高频访问易触发 403 错误。解决方案
代理 IP 策略:使用高匿代理池(优先选择海外节点,模拟真实用户地域),每 1-2 页切换 IP;请求频率控制:单 IP 每分钟请求≤1 次,两次请求间隔 3-5 秒(跨境商品决策周期长,符合用户行为);Cookie 池管理:维护多个登录态 Cookie(通过不同账号获取),随机携带以降低单一账号风险;动态参数伪装:在 URL 后添加随机时间戳(如
&t=1620000000
),避免请求缓存被识别。

六、最佳实践与合规要点

系统架构设计采用 “分布式低频率采集” 架构,适配跨境电商特性:

任务分发层:通过消息队列(如 RabbitMQ)分发搜索任务,控制单任务并发数≤1;采集层:多节点并行采集,每个节点绑定独立代理池(含海外节点),节点间请求间隔≥10 秒;存储层:用 Redis 缓存热门搜索结果(6 小时过期,跨境商品价格波动较慢),MySQL 存储历史数据(用于消费趋势分析);监控层:实时监控代理存活率、签名有效性,异常时通过企业微信告警。

性能优化策略

异步批量搜索:使用
aiohttp
并发处理多关键词(如 “兰蔻”“雅诗兰黛”),控制并发数≤1,适配低频率限制;按需解析:列表页优先提取
item_id
、价格、采购地等核心字段,详情信息通过后续
item_get
接口补充;热点抑制:对同一关键词 + 条件的搜索,2 小时内仅处理 1 次(返回缓存结果)。

合规性与风险控制

访问限制:单 IP 日搜索请求≤50 次,避免对平台服务器造成压力,符合跨境电商数据采集规范;数据使用边界:不得将数据用于虚假宣传、恶意比价或侵犯品牌知识产权,需注明数据来源 “洋码头”;法律风险规避:跨境商品数据涉及关税、采购地等敏感信息,使用时需遵守《海关法》《电子商务法》,不得篡改溯源信息。

七、总结

洋码头
item_search
接口的对接核心在于跨境筛选条件的精准映射(采购地、物流类型)、动态签名参数的灵活处理低频率高稳定性的采集策略。开发者需重点关注:

名称 – 编码映射表的维护(确保筛选条件生效);签名生成逻辑的逆向与复现(应对复杂筛选场景);代理池与请求频率的精细化控制(应对严格反爬)。

通过本文的技术方案,可构建稳定的跨境商品搜索系统,为跨境比价、选品分析等场景提供可靠数据支持。实际应用中,需根据平台最新反爬机制动态调整签名逻辑与代理策略,平衡数据获取效率与合规性

© 版权声明

相关文章

暂无评论

none
暂无评论...