×

搜好货 item_get 接口对接全攻略:从入门到精通

万邦科技Lex 万邦科技Lex 发表于2025-10-29 10:09:02 浏览159 评论0

抢沙发发表评论

           注册账号免费测试搜好货API数据接口

搜好货是国内知名的工业品 B2B 电商平台,聚焦机械、原材料、五金工具等工业品类,其商品详情数据(如批发价、规格参数、供应商资质、起订量等)是工业品采购商比价、供应商筛选、供应链分析的核心依据。由于搜好货无公开官方 API,开发者需通过页面解析或第三方服务实现商品详情(item_get)的获取。本文将系统讲解接口对接逻辑、技术实现、反爬应对及最佳实践,帮助开发者构建稳定的工业品详情数据获取系统。

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

  1. 核心功能搜好货item_get接口(非官方命名,泛指通过商品 ID 获取详情的工具)通过商品 ID(item_id)获取目标工业品的全量信息,核心字段包括:
    • 基础信息:商品 ID、标题、主图(多图)、类目(如 “机床”“钢材”)、详情页 URL、发布时间

    • 价格信息:批发价(wholesale_price)、市场价(market_price)、价格梯度(如 “1-10 台:¥12000 / 台;10 + 台:¥11000 / 台”)、起订量(min_order

    • 规格参数:技术参数(如 “功率:5.5kW”“材质:304 不锈钢”)、型号(如 “XK7132”)、尺寸(如 “1200×800mm”)

    • 供应商信息:公司名称、所在地(如 “山东济南”“广东佛山”)、注册资本、成立年限、资质认证(如 “ISO9001”)、联系方式

    • 交易信息:成交记录(如 “已售 200 台”)、支付方式(如 “对公转账”“在线支付”)、配送方式(如 “物流配送”“自提”)

    • 详情描述:商品详情图文(HTML)、售后服务(如 “一年质保”)、供货能力(如 “日产能 50 台”)

  2. 典型应用场景
    • 工业品采购商比价:获取同型号机床的批发价、起订量,对比不同供应商报价

    • 供应商筛选:通过注册资本、认证资质筛选优质供应商(如 “ISO 认证 + 成立 5 年以上”)

    • 供应链分析:统计 “不锈钢管” 类目的价格分布、规格覆盖率、核心产地占比

    • 市场监控:跟踪热门工业品类(如 “口罩机”“新能源设备”)的价格波动与库存变化

  3. 接口特性
    • 非官方性:无公开 API,依赖页面解析,受页面结构变更影响较大

    • B2B 属性:数据包含大量工业品批发场景字段(起订量、批量折扣、对公支付)

    • 反爬机制:包含 IP 限制(高频请求封锁)、User-Agent 校验、Cookie 验证(部分数据需登录态)

    • 静态为主:大部分数据(价格、规格、供应商信息)嵌入静态 HTML,动态加载内容较少

二、对接前置准备(环境与工具)

  1. 开发环境
    • 网络请求:requests(同步请求)、aiohttp(异步批量获取)

    • 页面解析:BeautifulSoup(HTML 静态解析)、lxml(XPath 提取,高效处理表格型规格参数)

    • 反爬工具:fake_useragent(随机 User-Agent)、proxy_pool(代理 IP 池管理)

    • 数据处理:re(正则提取价格梯度)、pandas(规格参数表格解析)

    • 开发语言:Python(推荐,生态丰富,适合快速解析静态页面)

    • 核心库:

  2. 商品 ID 与 URL 结构搜好货商品详情页 URL 格式为:https://www.912688.com/chanpin/{item_id}.html,其中item_id为商品唯一标识(纯数字,如1234567)。示例:某机床详情页 https://www.912688.com/chanpin/1234567.html,商品 ID 为1234567
  3. 页面结构分析(关键步骤)通过浏览器开发者工具(F12)分析详情页结构,核心数据位置:
    • 静态数据:标题、价格、规格参数、供应商信息等均在 HTML 标签中,其中规格参数多以表格(<table>)形式呈现;

    • 动态数据:成交记录、库存等少量数据通过 AJAX 接口加载(如https://www.912688.com/ajax/trade_records?item_id={item_id})。

  4. 合规性前提
    • 遵守搜好货robots.txthttps://www.912688.com/robots.txt),不爬取禁止路径(如用户中心、订单页);

    • 限制请求频率:单 IP 每分钟≤3 次,避免对服务器造成压力;

    • 数据用途限于非商业个人使用或已授权的企业服务,不得用于恶意竞争。

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

以 “获取某型号机床商品详情” 为例,核心流程为URL 构建请求发送静态数据解析动态数据补充数据结构化
  1. URL 与请求头构建
    • 目标 URL:https://www.912688.com/chanpin/{item_id}.html(替换item_id为实际值);

    • 请求头:模拟浏览器行为,关键字段包括User-AgentRefererCookie(提升可信度):

      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.912688.com/",
          "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
          "Cookie": "PHPSESSID=xxx; user_id=anonymous; Hm_lvt_xxx=xxx"  # 从浏览器获取匿名Cookie}
  2. 静态数据解析(HTML 提取)从静态 HTML 中提取核心信息,重点关注工业品特有字段(规格参数、技术指标等):
    字段解析方式(CSS 选择器 / XPath)示例值
    商品标题h1.product-title(CSS 选择器)“XK7132 数控铣床 高精度立式加工中心”
    主图列表div.product-gallery imgsrc属性["https://img.912688.com/xxx.jpg", ...]
    批发价div.price-box .wholesale-price的文本“12000.00 元 / 台”
    价格梯度div.price-gradients li的文本(多条)“1-10 台:¥12000;10 + 台:¥11000”
    起订量div.min-order的文本“1 台起订”
    规格参数table.spec-table tr(表格行,提取<th>和<td>){"功率": "5.5kW", "重量": "2000kg"}
    供应商名称div.company-name a的文本“济南某机床有限公司”
    公司资质div.certifications imgalt属性(认证标签)["ISO9001", "CE认证"]
  3. 动态数据补充(AJAX 接口)成交记录、库存等实时数据通过内部接口加载,需抓包定位并调用:
    • 动态接口示例:https://www.912688.com/ajax/trade_records?item_id={item_id}(返回成交记录);

    • 响应示例(简化版):

      json
      {
        "records": [
          {"buyer": "某工厂", "quantity": 5, "price": 12000, "time": "2024-10-01"},
          {"buyer": "某贸易公司", "quantity": 10, "price": 11000, "time": "2024-09-25"}
        ],
        "total_sold": 200  # 总销量}
  4. 数据整合与结构化合并静态与动态数据,形成标准化字典,适配工业品采购场景:
    python
    运行
    standardized_data = {
        "item_id": item_id,
        "title": title,
        "price": {
            "wholesale": wholesale_price,
            "market": market_price,
            "gradient": gradient_list,  # 价格梯度列表(含数量区间与对应价格)
            "min_order": min_order    },
        "specs": specs_dict,  # 规格参数键值对(如{"功率": "5.5kW"})
        "supplier": {
            "name": company_name,
            "location": location,
            "capital": registered_capital,  # 注册资本
            "established": established_year,  # 成立年份
            "certifications": certifications  # 资质认证列表
        },
        "trade": {
            "total_sold": total_sold,  # 总销量
            "records": trade_records,  # 成交记录列表
            "payment_methods": payment_methods  # 支付方式
        },
        "images": image_list,
        "detail_html": detail_html,  # 商品详情HTML
        "update_time": time.strftime("%Y-%m-%d %H:%M:%S")}

四、代码实现示例(Python)

以下是item_get接口的完整实现,包含静态 HTML 解析、动态接口调用、反爬处理及数据结构化:
import requests
import time
import random
import re
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from typing import Dict, List

class SouhaohuoItemApi:
    def __init__(self, proxy_pool: List[str] = None):
        self.base_url = "https://www.912688.com/chanpin/{item_id}.html"
        self.trade_api = "https://www.912688.com/ajax/trade_records?item_id={item_id}"  # 成交记录接口
        self.ua = UserAgent()
        self.proxy_pool = proxy_pool  # 代理池列表,如["http://ip:port", ...]

    def _get_headers(self) -> Dict[str, str]:
        """生成随机请求头"""
        return {
            "User-Agent": self.ua.random,
            "Referer": "https://www.912688.com/",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Cookie": "PHPSESSID=xxx; user_id=anonymous; Hm_lvt_xxx=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 _parse_price_gradient(self, gradient_texts: List[str]) -> List[Dict]:
        """解析价格梯度(如“1-10台:¥12000”)"""
        gradient_list = []
        for text in gradient_texts:
            # 匹配数量区间和价格
            match = re.match(r"(\d+)(?:-(\d+))?[台个件]:¥?(\d+(\.\d+)?)", text)
            if match:
                min_qty = int(match.group(1))
                max_qty = int(match.group(2)) if match.group(2) else None
                price = float(match.group(3))
                gradient_list.append({
                    "min_quantity": min_qty,
                    "max_quantity": max_qty,
                    "price": price
                })
        return gradient_list

    def _parse_specs(self, spec_table) -> Dict[str, str]:
        """解析规格参数表格(工业品核心字段)"""
        specs = {}
        if not spec_table:
            return specs
        for row in spec_table.select("tr"):
            th = row.select_one("th")
            td = row.select_one("td")
            if th and td:
                key = th.text.strip()
                value = td.text.strip()
                if key and value:
                    specs[key] = value
        return specs

    def _parse_static_data(self, html: str) -> Dict[str, str]:
        """解析静态HTML中的基础信息"""
        soup = BeautifulSoup(html, "lxml")
        # 提取价格梯度
        gradient_texts = [li.text.strip() for li in soup.select("div.price-gradients li")]
        gradient_list = self._parse_price_gradient(gradient_texts)
        # 提取规格参数(表格形式)
        spec_table = soup.select_one("table.spec-table")
        specs = self._parse_specs(spec_table)
        # 提取供应商资质
        certifications = [img.get("alt", "").strip() for img in soup.select("div.certifications img") if img.get("alt")]
        
        return {
            "title": soup.select_one("h1.product-title")?.text.strip() or "",
            "images": [img.get("src") for img in soup.select("div.product-gallery img") if img.get("src")],
            "price": {
                "wholesale": float(re.sub(r"[^\d.]", "", soup.select_one("div.price-box .wholesale-price")?.text or "0")),
                "market": float(re.sub(r"[^\d.]", "", soup.select_one("div.price-box .market-price")?.text or "0")),
                "gradient": gradient_list,
                "min_order": soup.select_one("div.min-order")?.text.strip() or ""
            },
            "specs": specs,
            "supplier": {
                "name": soup.select_one("div.company-name a")?.text.strip() or "",
                "location": soup.select_one("div.company-location")?.text.strip() or "",
                "capital": soup.select_one("div.registered-capital")?.text.strip() or "",
                "established": soup.select_one("div.established-year")?.text.strip() or "",
                "certifications": certifications
            },
            "payment_methods": [span.text.strip() for span in soup.select("div.payment-methods span")],
            "detail_html": str(soup.select_one("div.product-detail") or "")
        }

    def _fetch_trade_data(self, item_id: str, headers: Dict[str, str], proxy: Dict[str, str]) -> Dict:
        """调用动态接口获取成交记录"""
        try:
            url = self.trade_api.format(item_id=item_id)
            response = requests.get(url, headers=headers, proxies=proxy, timeout=10)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            print(f"成交记录接口获取失败: {str(e)}")
            return {"records": [], "total_sold": 0}

    def item_get(self, item_id: str, timeout: int = 10) -> Dict:
        """
        获取搜好货商品详情
        :param item_id: 商品ID(如1234567)
        :param timeout: 超时时间
        :return: 标准化商品数据
        """
        try:
            # 1. 构建URL并发送请求
            url = self.base_url.format(item_id=item_id)
            headers = self._get_headers()
            proxy = self._get_proxy()

            # 随机延迟,避免反爬
            time.sleep(random.uniform(2, 4))
            response = requests.get(
                url=url,
                headers=headers,
                proxies=proxy,
                timeout=timeout
            )
            response.raise_for_status()
            html = response.text

            # 2. 解析静态数据
            static_data = self._parse_static_data(html)
            if not static_data["title"]:
                return {"success": False, "error_msg": "未找到商品信息,可能item_id错误或商品已下架"}

            # 3. 获取并解析成交记录
            trade_data = self._fetch_trade_data(item_id, headers, proxy)

            # 4. 合并数据
            result = {
                "success": True,
                "data": {
                    "item_id": item_id,** static_data,
                    "trade": {
                        "total_sold": trade_data.get("total_sold", 0),
                        "records": trade_data.get("records", [])
                    },
                    "update_time": time.strftime("%Y-%m-%d %H:%M:%S")
                }
            }
            return result

        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客户端
    api = SouhaohuoItemApi(proxy_pool=PROXIES)

    # 获取商品详情(示例item_id)
    item_id = "1234567"  # 替换为实际商品ID
    result = api.item_get(item_id)

    if result["success"]:
        data = result["data"]
        print(f"商品标题: {data['title']}")
        print(f"供应商: {data['supplier']['name']} | 所在地: {data['supplier']['location']}")
        print(f"资质: {','.join(data['supplier']['certifications'])} | 注册资本: {data['supplier']['capital']}")
        print(f"批发价: {data['price']['wholesale']}元 | 市场价: {data['price']['market']}元 | 起订量: {data['price']['min_order']}")
        print("价格梯度:")
        for grad in data['price']['gradient']:
            max_qty = grad['max_quantity'] if grad['max_quantity'] else "∞"
            print(f"  {grad['min_quantity']}-{max_qty}台: {grad['price']}元/台")
        print(f"\n核心规格:")
        # 打印前5条规格参数(工业品规格较多)
        for i, (key, value) in enumerate(list(data['specs'].items())[:5]):
            print(f"  {key}: {value}")
        print(f"\n成交记录: 总销量{data['trade']['total_sold']}台 | 支付方式: {','.join(data['payment_methods'])}")
    else:
        print(f"获取失败: {result['error_msg']}(错误码: {result.get('code')})")

五、关键技术难点与解决方案

  1. 工业品规格参数解析(表格结构)
    • 通过BeautifulSoup解析表格行(<tr>),提取<th>(参数名)和<td>(参数值),构建键值对字典;

    • 对合并单元格(rowspan/colspan),通过递归或填充空值处理,确保参数完整性;

    • 示例代码中_parse_specs函数专门处理表格型规格,适配工业品参数多的特性。

    • 问题:搜好货作为工业品平台,规格参数多以复杂表格(<table>)形式呈现(如机床的功率、重量、尺寸等),字段多且结构不统一。

    • 解决方案

  2. 价格梯度提取(批量折扣规则)
    • 用正则表达式匹配数量区间(如1-1010+)和对应价格,提取min_quantity(最小数量)、max_quantity(最大数量)、price(单价);

    • 对 “10 + 台” 等无上限的区间,max_quantity设为None,表示 “≥10 台”;

    • 示例代码中_parse_price_gradient函数通过正则精准提取价格梯度,适配工业品批量采购场景。

    • 问题:工业品采购多为批量交易,价格梯度(如 “1-10 台:12000 元;10 + 台:11000 元”)是核心决策依据,文本格式多样。

    • 解决方案

  3. 反爬机制对抗
    • 代理 IP 轮换:使用高匿代理池,每 2-3 次请求切换 IP,优先选择存活时间≥10 分钟的代理;

    • 请求频率控制:单 IP 每分钟请求≤3 次,两次请求间隔 2-4 秒(随机波动),模拟采购商浏览节奏;

    • Cookie 池维护:通过多个浏览器会话获取匿名 Cookie(无需登录),随机携带以降低风险;

    • 异常处理:若返回 “请登录” 页面,自动切换 Cookie 并重试(部分数据需登录,但基础信息可匿名访问)。

    • 问题:工业品平台对异常访问较敏感,高频请求会触发 IP 封锁(403 错误)或要求登录验证。

    • 解决方案

  4. 供应商资质与可信度评估
    • 解析供应商信息区的认证标签(<div.certifications>),提取资质名称(如 “ISO9001”“CE 认证”);

    • 提取注册资本、成立年限等字段,辅助评估供应商可信度(示例代码中supplier字段包含相关信息);

    • 对模糊表述(如 “注册资本:100-500 万”),保留原始文本或按区间处理。

    • 问题:工业品采购对供应商资质(如 ISO 认证、注册资本)要求严格,需从页面提取并结构化。

    • 解决方案

六、最佳实践与合规要点

  1. 系统架构设计采用 “低频稳定采集” 架构,适配工业品平台特性:
    • 采集层:集成代理池、Cookie 池,控制单 IP 请求频率(≤3 次 / 分钟),避免触发反爬;

    • 解析层:分离静态数据(规格、价格)与动态数据(成交记录)解析逻辑,重点处理表格型规格参数;

    • 存储层:用 Redis 缓存热门工业品(2 小时过期,工业品价格变动较慢),MySQL 存储历史数据(用于价格趋势分析);

    • 监控层:实时监控请求成功率、反爬触发次数,异常时通过邮件告警。

  2. 性能优化策略
    • 异步批量获取:使用aiohttp并发处理多个item_id(控制并发数≤3),提升效率;

    • 按需解析:优先提取价格、规格、供应商资质等核心字段,详情 HTML 等非必要信息可选择性获取;

    • 增量更新:仅更新价格、库存有变化的商品(通过对比缓存的历史数据),减少无效请求。

  3. 合规性与风险控制
    • 频率限制:单 IP 日请求量≤300 次,避免对搜好货服务器造成压力;

    • 数据使用边界:不得将数据用于恶意比价、虚假宣传或商业售卖,需注明数据来源 “搜好货”;

    • 法律风险规避:若用于商业产品,建议通过搜好货商务合作渠道获取合法数据授权,避免侵权纠纷。

  4. 反爬适应性调整
    • 当发现大量 403 响应时,临时提高请求间隔至 5 秒,并检查代理池有效性;

    • 定期(每月)检查页面结构变化(工业品平台页面变更频率较低),更新解析规则;

    • 若静态解析失败,尝试通过动态接口补充核心数据(如价格、销量),确保基础功能可用。

七、总结

搜好货item_get接口的对接核心在于工业品特有字段的精准解析(表格型规格参数、批量价格梯度)与低频率高稳定性的采集策略。开发者需重点关注:
  1. 规格参数表格的解析逻辑(适配工业品多参数特性);

  2. 价格梯度的正则提取(支持批量采购决策);

  3. 代理池与请求频率的精细化控制(平衡效率与反爬风险)。

通过本文的技术方案,可构建稳定的商品详情获取系统,为工业品采购商比价、供应商筛选等场景提供可靠数据支持。实际应用中,需根据搜好货的反爬策略动态调整方案,平衡数据获取效率与合规性。


群贤毕至

访客