图片搜索API(拍立淘)精准获取同款商品

4 阅读5分钟

在电商选品、比价系统及供应链管理中,以图搜货是核心痛点。本文基于1688开放平台官方能力,详细解析 1688.item_search_img 接口的调用全流程。不同于网上的碎片化信息,本文提供经过企业级验证的Python代码,解决签名、时间戳、图片上传等核心难点,助你快速构建自动化选品工具。

一、 技术原理与核心能力

1688图片搜索API(俗称“拍立淘”)并非简单的爬虫,而是基于**计算机视觉(CV)**技术。

  • 技术栈:深度学习特征提取(CNN)+ 海量商品图库检索。

  • 搜索模式

    • 同款搜索 (search_type=1) :寻找完全相同或高度相似的商品(SKU级匹配)。
    • 相似搜索 (search_ type=0) :寻找风格、形状相似的商品。
  • 应用场景:自动化工单处理、竞品分析、一件代发选品、防侵权监测。

二、 接入门槛与权限申请(关键)

注意:此接口严格限制为企业用户。

  1. 账号要求:必须拥有企业认证的1688账号(需营业执照)。

  2. 应用创建:在1688开放平台控制台]创建应用,获取 app_keyapp_secret

  3. 接口授权:必须单独申请 1688.item_search_img 接口权限。

    • 技巧:在申请理由中填写“用于企业供应链数字化升级,辅助采购选品”,通过率极高。

三、 接口调用详解

请求地址https://api.1688.com/router/rest (基于网页[1]验证)
请求方法POST (推荐,支持大图Base64;也可用GET传URL)
协议:HTTPS

核心参数表:

参数名必选说明
method固定值:1688.item_search_img
app_key你的App Key
timestamp秒级时间戳,需与服务器时间误差<500ms
vAPI版本号,固定为 2.0
sign_method签名方式,如 md5
imgid图片公网URL 图片文件的Base64编码
search_type1=同款,0=相似
sort排序字段(_sale销量,_price价格)
page_size每页数量,最大50

四、 企业级 Python 实战代码

特性

  • 完美签名:严格遵循Open API 2.0签名规则,自动排序。
  • 高容错:处理网络超时、图片读取失败、JSON解析错误。
  • 灵活上传:支持本地图片自动转Base64,或直接传URL。
import requests
import hashlib
import time
import base64
import os

class OneBoundImageSearch:
    """
    1688图片搜索API封装
    支持本地图片文件路径或公网URL搜索
    """
    
    def __init__(self, app_key, app_secret):
        self.app_key = app_key
        self.app_secret = app_secret
        self.url = "https://api.1688.com/router/rest" # 官方路由地址

    def _generate_sign(self, params):
        """生成签名"""
        # 1. 参数名ASCII码升序排序
        sorted_params = sorted(params.items())
        # 2. 拼接字符串: secret+key1value1key2value2+secret
        param_str = ''.join(f'{k}{v}' for k, v in sorted_params)
        sign_str = f'{self.app_secret}{param_str}{self.app_secret}'
        # 3. MD5加密并转大写
        return hashlib.md5(sign_str.encode('utf-8')).hexdigest().upper()

    def _prepare_img_data(self, img_path_or_url):
        """
        智能识别输入:
        1. 如果是本地文件路径,读取并转Base64
        2. 如果是http开头,直接返回URL
        """
        if img_path_or_url.startswith('http://') or img_path_or_url.startswith('https://'):
            return img_path_or_url
        elif os.path.exists(img_path_or_url):
            with open(img_path_or_url, 'rb') as f:
                return base64.b64encode(f.read()).decode('utf-8')
        else:
            raise ValueError("图片路径或URL无效")

    def search(self, img_input, search_type=1, sort='_sale', page_size=20):
        """
        执行搜索
        :param img_input: 本地图片路径 或 图片URL
        """
        # 1. 构建基础参数
        params = {
            'method': '1688.item_search_img',
            'app_key': self.app_key,
            'timestamp': str(int(time.time())), # 秒级
            'v': '2.0',
            'format': 'json',
            'sign_method': 'md5',
            # --- 业务参数 ---
            'imgid': self._prepare_img_data(img_input),
            'search_type': str(search_type), # 1同款
            'sort': sort,
            'page_size': str(page_size)
        }

        # 2. 生成签名
        params['sign'] = self._generate_sign(params)

        # 3. 发送请求 (使用POST防止URL过长)
        try:
            response = requests.post(self.url, data=params, timeout=15)
            response.raise_for_status() # 检查HTTP错误
            
            result = response.json()
            
            # 4. 解析结果 (兼容不同版本的返回结构)
            # 处理成功情况
            if result.get('code') == '200' or result.get('success') is True:
                items = result.get('result', {}).get('items', []) or result.get('items', [])
                return {
                    'success': True,
                    'total': len(items),
                    'data': items[:10] # 只返回前10个高价值结果
                }
            else:
                return {
                    'success': False,
                    'msg': result.get('msg', '未知错误'),
                    'sub_msg': result.get('sub_msg', '')
                }

        except requests.exceptions.Timeout:
            return {'success': False, 'msg': '请求超时'}
        except requests.exceptions.RequestException as e:
            return {'success': False, 'msg': f'网络异常: {str(e)}'}
        except Exception as e:
            return {'success': False, 'msg': f'解析异常: {str(e)}'}

# --- 使用示例 ---
if __name__ == '__main__':
    # 1. 配置你的密钥
    SEARCHER = OneBoundImageSearch(
        app_key='your_app_key', 
        app_secret='your_app_secret'
    )

    # 2. 搜索 (支持本地图片路径 或 URL)
    # image_path = "C:/path/to/your/shoe.jpg"
    image_url = "https://example.com/product.jpg"
    
    res = SEARCHER.search(image_url, search_type=1, sort='_sale')

    if res['success']:
        print(f"🎉 搜索成功,找到 {res['total']} 个结果:\n")
        for i, item in enumerate(res['data'], 1):
            print(f"{i}. [{item.get('title', '无标题')}]")
            print(f"   💰 价格: {item.get('price')}")
            print(f"   📈 销量: {item.get('sales', 0)}")
            print(f"   🔗 链接: {item.get('detailUrl')}\n")
    else:
        print(f" 搜索失败: {res['msg']}")

五、 避坑指南(常见错误码解析)

根据网页[1]及实战经验,以下是高频问题:

  1. code: 15 / Invalid signature (签名错误)

    • 原因:参数排序错误,或者拼接时漏了 app_secret
    • 解决:确保 sign 生成时,参数是按字母排序的,且拼接格式为 secret+key1value1...+secret
  2. code: 20 / Service currently unavailable (服务不可用)

    • 原因:QPS(每秒查询率)超限,或应用被封禁。
    • 解决:检查控制台配额,增加限流逻辑(如 time.sleep(1))。
  3. code: 401 / Invalid app key (Key无效)

    • 原因app_key 错误,或未通过实名/企业认证。
  4. 图片下载失败 / 图片格式错误

    • 原因:传入的图片URL无法被1688服务器访问(内网地址),或图片损坏。
    • 解决:尽量使用高质量公网图床链接,或直接上传Base64。

六、 总结

通过上述代码,开发者可以轻松将“以图搜同款”能力集成到ERP、CRM或独立站系统中。相比于传统的爬虫,API方式更稳定、数据更结构化。

核心建议

  • 图片预处理:在上传前,使用OpenCV裁剪背景,能显著提高同款匹配率。
  • 缓存机制:对搜索过的图片MD5做缓存,避免重复调用产生费用。
  • 合规性:严禁将获取的数据用于非授权的商业爬虫,遵守《1688平台API使用协议》。