×

京东工业 item_get 接口对接全攻略:从入门到精通

万邦科技Lex 万邦科技Lex 发表于2025-11-18 10:38:31 浏览35 评论0

抢沙发发表评论

                        注册账号免费测试京东工业API数据接口

京东工业(聚焦工业用品采购的 B2B 电商平台)的商品详情数据(如规格参数、批量价格、库存状态、供应商信息等)对企业采购决策、供应链管理、竞品分析等场景具有重要价值。由于平台无公开官方 API,开发者需通过页面解析或逆向工程实现商品详情(item_get)的获取。本文系统讲解接口逻辑、技术实现、工业场景适配及反爬应对,帮助构建稳定的京东工业商品详情获取系统。

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

  1. 核心功能京东工业item_get接口(非官方命名)通过商品 ID(productId)获取全量商品信息,核心字段聚焦工业 B2B 特性:
    • 基础信息:商品 ID、标题(含型号 / 规格,如 “M12×50mm 高强度螺栓 8.8 级”)、主图 + 细节图 + 参数表、类目(如 “紧固件 > 螺栓”)、详情页 URL

    • 价格信息:单价(如 “¥1.5 / 个”)、批量阶梯价(如 “100-999 个 ¥1.4 / 个,1000 + 个 ¥1.2 / 个”)、含税价(如 “含税 ¥1.7 / 个”)、折扣信息(如 “满 1000 减 50”)

    • 规格参数:工业参数(如螺栓的材质、强度等级、尺寸公差)、包装规格(如 “100 个 / 盒”)、重量(如 “0.5kg / 个”)

    • 库存与配送:总库存(如 “10000 个”)、区域库存(如 “北京仓有货”)、起订量(如 “最小起订 10 个”)、配送时效(如 “24 小时达”)

    • 供应商信息:供应商名称(如 “XX 工业五金有限公司”)、资质(如 “ISO9001 认证”)、服务(如 “7 天无理由退换”)

    • 采购数据:成交记录(如 “近 30 天成交 5000 个”)、评价数、好评率(如 “98% 好评”)

  2. 典型应用场景
    • 企业采购系统:对接接口获取螺栓的批量价格和库存,自动生成采购单(如 “采购 500 个,单价 ¥1.4 / 个”)

    • 供应链管理:监控关键工业部件(如轴承)的库存波动,低于阈值时触发补货提醒

    • 竞品分析:对比同型号工业用品的价格、供应商资质及批量折扣,优化采购成本

    • 资质审核:提取供应商的认证信息(如 ISO9001),辅助供应商准入审核

  3. 接口特性
    • B2B 属性:数据突出批量价格、起订量、含税价等企业采购核心字段

    • 工业专业性:规格参数聚焦工业标准(如材质、精度等级、执行标准)

    • 动态性强:库存(实时消耗)、批量价格(阶梯调整)依赖 AJAX 动态加载

    • 反爬机制:包含 IP 限制、User-Agent 校验、Cookie 验证、部分接口带签名参数(sign

    • 权限控制:部分高级信息(如供应商联系方式)需登录企业账号可见

二、对接前置准备(环境与 URL 结构)

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

    • 页面解析:BeautifulSoup(静态 HTML)、lxml(XPath 提取复杂结构)

    • 反爬工具:fake_useragent(随机 UA)、proxy_pool(代理 IP 池)、execjs(解析签名参数)

    • 数据处理:re(正则提取价格、库存)、json(解析动态接口响应)

    • 开发语言:Python(推荐 3.8+)

    • 核心库:

  2. 商品 ID 与 URL 结构京东工业商品详情页 URL 格式为:https://b.jd.com/item/{productId}.html,其中productId为商品唯一标识(纯数字,如100012345678)。示例:某螺栓商品详情页 https://b.jd.com/item/100012345678.html,商品 ID 为100012345678
  3. 页面结构分析通过浏览器开发者工具(F12)分析详情页,核心数据位置:
    • 静态数据:标题、主图、基础价格、供应商名称等嵌入主页面 HTML(如<h1 class="sku-name"> <div class="price">);

    • 动态数据:批量阶梯价、实时库存、工业参数表通过 AJAX 接口加载(如https://b.jd.com/industrial/product/detailAjax);

    • 规格数据:多规格商品(如不同尺寸的螺栓)的选项在<div class="spec-items">,需关联接口获取对应价格和库存。

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

以 “获取某 M12 螺栓商品详情” 为例,核心流程为URL 构建主页面解析动态接口调用规格与价格关联数据结构化
  1. URL 与请求头构建
    • 目标 URL:https://b.jd.com/item/{productId}.html(替换productId为实际值);

    • 请求头:需包含登录态Cookie(企业账号,获取完整价格和库存)、动态User-AgentReferer

      python
      运行
      headers = {
          "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
          "Referer": "https://b.jd.com/",
          "Cookie": "session-id=xxx; user-key=xxx; pin=xxx"  # 企业账号登录后获取}
  2. 主页面静态数据解析从主页面 HTML 提取基础信息,重点关注工业 B2B 特有字段:
    字段解析方式(CSS 选择器 / XPath)示例值
    商品标题h1.sku-name(CSS 选择器)“M12×50mm 高强度螺栓 8.8 级”
    图片列表div.swiper-wrapper imgsrc(主图)、div.detail-img img(细节图)["https://img-b.jd.com/xxx.jpg", ...]
    单价div.price .p-price的文本“¥1.5 / 个”
    含税标识div.tax-tag的文本“含税”
    供应商名称div.seller-name的文本“XX 工业五金有限公司”
    规格选项div.spec-items .spec-itemdata-value(如尺寸)["M12×50mm", "M12×60mm"]
    起订量div.min-order的文本“最小起订 10 个”
    服务标签div.service-tags span的文本“7 天无理由正品保障”
  3. 动态数据补充(核心 API 接口)京东工业核心数据通过内部 API 接口加载,需提取接口参数并模拟请求:
    • 请求示例:

      python
      运行
      params = {
          "productId": productId,
          "callback": "jQuery1124012345678901234567_1234567890123",
          "_": int(time.time() * 1000)}
    • 详情接口https://b.jd.com/industrial/product/detailAjax,参数包含productIdcallback_(时间戳)等;

    • 响应为 JSONP 格式,提取核心字段(简化版):

      json
      {
        "price": {
          "singlePrice": 1.5,  # 单价    "ladderPrices": [    # 阶梯价      {"count": 100, "price": 1.4},
            {"count": 1000, "price": 1.2}
          ],
          "taxPrice": 1.7      # 含税价  },
        "stock": {
          "totalStock": 10000,  # 总库存    "warehouseStock": {"北京仓": 5000, "上海仓": 3000}  # 区域库存  },
        "params": {            # 工业参数    "材质": "高强度钢",
          "强度等级": "8.8级",
          "执行标准": "GB/T 5782-2016"
        },
        "trade": {
          "monthSales": 5000,   # 近30天销量    "commentCount": 200   # 评价数  }}
  4. JSONP 响应解析动态接口返回 JSONP 格式(如jQuery123(...)),需提取其中的 JSON 数据:
    python
    运行
    import re
    json_str = re.search(r"jQuery\d+_\d+\((\{.*\})\)", response.text).group(1)data = json.loads(json_str)
  5. 规格与价格关联(多规格商品)多规格工业商品(如不同尺寸的螺栓)的价格和库存独立,需通过规格 ID 关联:
    • 从静态页面提取规格选项及对应specId(隐藏在data-sku属性中);

    • 调用规格详情接口(https://b.jd.com/industrial/product/detailAjax?productId={specId})获取对应规格的价格和库存;

    • 示例:specId=100012345679对应 “M12×60mm”,单价 ¥1.8 / 个,库存 8000 个。

四、代码实现示例(Python)

以下是item_get接口的完整实现,包含动态接口调用、阶梯价解析、规格关联及反爬处理:
import requests
import time
import random
import re
import json
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from typing import Dict, List

class JdIndustrialItemApi:
    def __init__(self, proxy_pool: List[str] = None, cookie: str = ""):
        self.base_url = "https://b.jd.com/item/{product_id}.html"
        self.api_url = "https://b.jd.com/industrial/product/detailAjax"
        self.ua = UserAgent()
        self.proxy_pool = proxy_pool  # 代理池列表
        self.cookie = cookie  # 企业账号登录态Cookie

    def _get_headers(self) -> Dict[str, str]:
        """生成随机请求头"""
        headers = {
            "User-Agent": self.ua.random,
            "Referer": "https://b.jd.com/",
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "X-Requested-With": "XMLHttpRequest"
        }
        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 _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 _parse_jsonp(self, jsonp_str: str) -> Dict:
        """解析JSONP响应为JSON"""
        try:
            json_str = re.search(r"jQuery\d+_\d+\((\{.*\})\)", jsonp_str).group(1)
            return json.loads(json_str)
        except Exception as e:
            print(f"JSONP解析失败: {str(e)}")
            return {}

    def _parse_static_data(self, html: str) -> Dict:
        """解析主页面静态数据"""
        soup = BeautifulSoup(html, "lxml")
        
        # 提取规格选项
        specs = []
        for spec_item in soup.select("div.spec-items .spec-item"):
            spec_name = spec_item.select_one(".spec-title")?.text.strip() or "规格"
            spec_values = [
                {
                    "name": option.text.strip(),
                    "spec_id": option.get("data-sku") or "",  # 规格ID(多规格时使用)
                    "selected": "selected" in option.get("class", [])
                }
                for option in spec_item.select(".spec-value a")
            ]
            if spec_values:
                specs.append({
                    "name": spec_name,
                    "values": spec_values
                })
        
        return {
            "title": soup.select_one("h1.sku-name")?.text.strip() or "",
            "images": {
                "main": [img.get("src") for img in soup.select("div.swiper-wrapper img") if img.get("src")],
                "detail": [img.get("src") for img in soup.select("div.detail-img img") if img.get("src")]
            },
            "price": {
                "single_str": soup.select_one("div.price .p-price")?.text.strip() or "",
                "tax_tag": soup.select_one("div.tax-tag")?.text.strip() or ""
            },
            "supplier": {
                "name": soup.select_one("div.seller-name")?.text.strip() or "",
                "qualification": [q.text.strip() for q in soup.select("div.qualification-tags span")]
            },
            "purchase": {
                "min_order": soup.select_one("div.min-order")?.text.strip() or "",
                "service_tags": [s.text.strip() for s in soup.select("div.service-tags span")]
            },
            "specs": specs  # 规格选项(如尺寸、型号)
        }

    def _fetch_api_data(self, product_id: str, spec_id: str = "", headers: Dict[str, str], proxy: Dict[str, str]) -> Dict:
        """调用动态API接口获取核心数据"""
        api_data = {"price": {}, "stock": {}, "params": {}, "trade": {}}
        try:
            # 多规格时使用spec_id,否则用product_id
            target_id = spec_id if spec_id else product_id
            timestamp = int(time.time() * 1000)
            # 生成随机callback参数(模拟前端)
            callback = f"jQuery11240{random.randint(1000000000000, 9999999999999)}_{timestamp}"
            params = {
                "productId": target_id,
                "callback": callback,
                "_": timestamp
            }

            response = requests.get(
                self.api_url,
                params=params,
                headers=headers,
                proxies=proxy,
                timeout=10
            )
            # 解析JSONP响应
            data = self._parse_jsonp(response.text)
            if not data:
                return api_data

            # 解析价格信息(含阶梯价)
            price_info = data.get("price", {})
            api_data["price"] = {
                "singlePrice": price_info.get("singlePrice", 0),
                "ladderPrices": price_info.get("ladderPrices", []),  # 阶梯价列表
                "taxPrice": price_info.get("taxPrice", 0)
            }

            # 解析库存信息
            stock_info = data.get("stock", {})
            api_data["stock"] = {
                "totalStock": stock_info.get("totalStock", 0),
                "warehouseStock": stock_info.get("warehouseStock", {})  # 区域库存
            }

            # 解析工业参数
            api_data["params"] = data.get("params", {})

            # 解析交易数据
            trade_info = data.get("trade", {})
            api_data["trade"] = {
                "monthSales": trade_info.get("monthSales", 0),
                "commentCount": trade_info.get("commentCount", 0),
                "goodRate": trade_info.get("goodRate", 0)
            }

        except Exception as e:
            print(f"API数据获取失败: {str(e)}")
        return api_data

    def _merge_multi_specs(self, static_specs: List[Dict], product_id: str, headers: Dict[str, str], proxy: Dict[str, str]) -> List[Dict]:
        """合并多规格商品的价格和库存"""
        merged_specs = []
        for spec_group in static_specs:
            spec_name = spec_group["name"]
            merged_values = []
            for spec in spec_group["values"]:
                spec_id = spec["spec_id"]
                if not spec_id:
                    merged_values.append(spec)
                    continue
                # 调用规格对应的API接口
                spec_data = self._fetch_api_data(product_id, spec_id, headers, proxy)
                merged_values.append({
                    **spec,** spec_data["price"],
                    "stock": spec_data["stock"]
                })
            merged_specs.append({
                "name": spec_name,
                "values": merged_values
            })
        return merged_specs

    def item_get(self, product_id: str, timeout: int = 10) -> Dict:
        """
        获取京东工业商品详情
        :param product_id: 商品ID(如100012345678)
        :param timeout: 超时时间
        :return: 标准化商品数据
        """
        try:
            # 1. 主页面请求
            url = self.base_url.format(product_id=product_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()
            main_html = response.text

            # 2. 解析主页面数据
            static_data = self._parse_static_data(main_html)
            if not static_data["title"]:
                return {"success": False, "error_msg": "商品不存在或已下架"}

            # 3. 获取API核心数据(默认规格)
            api_data = self._fetch_api_data(product_id, "", headers, proxy)

            # 4. 处理多规格商品(若有)
            merged_specs = static_data["specs"]
            if static_data["specs"] and any(len(s["values"]) > 1 for s in static_data["specs"]):
                merged_specs = self._merge_multi_specs(static_data["specs"], product_id, headers, proxy)

            # 5. 整合结果
            result = {
                "success": True,
                "data": {
                    "product_id": product_id,
                    **static_data,** api_data,  # 合并price/stock/params/trade
                    "specs": merged_specs,  # 多规格已合并价格库存
                    "url": url,
                    "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}
            if "401" in str(e):
                return {"success": False, "error_msg": "Cookie无效,请使用企业账号重新登录", "code": 401}
            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"
    ]
    # 企业账号登录态Cookie(从浏览器获取)
    COOKIE = "session-id=xxx; user-key=xxx; pin=xxx; enterpriseId=xxx"

    # 初始化API客户端
    api = JdIndustrialItemApi(proxy_pool=PROXIES, cookie=COOKIE)

    # 获取商品详情(示例product_id)
    product_id = "100012345678"  # 螺栓商品ID
    result = api.item_get(product_id)

    if result["success"]:
        data = result["data"]
        print(f"商品标题: {data['title']}")
        print(f"价格信息: 单价¥{data['price']['singlePrice']}/个 | 含税价¥{data['price']['taxPrice']}/个")
        if data['price']['ladderPrices']:
            print("阶梯价:")
            for ladder in data['price']['ladderPrices']:
                print(f"  采购{ladder['count']}个及以上: ¥{ladder['price']}/个")
        print(f"库存信息: 总库存{data['stock']['totalStock']}个")
        if data['stock']['warehouseStock']:
            print("区域库存:")
            for warehouse, stock in data['stock']['warehouseStock'].items():
                print(f"  {warehouse}: {stock}个")
        print(f"工业参数: 材质={data['params'].get('材质')} | 强度等级={data['params'].get('强度等级')} | 执行标准={data['params'].get('执行标准')}")
        print(f"采购信息: {data['purchase']['min_order']} | 服务: {', '.join(data['purchase']['service_tags'])}")
        print(f"供应商: {data['supplier']['name']} | 资质: {', '.join(data['supplier']['qualification'])}")
        print(f"交易数据: 近30天成交{data['trade']['monthSales']}个 | 评价{data['trade']['commentCount']}条 | 好评率{data['trade']['goodRate']}%")
        if data['specs']:
            print(f"规格选项:")
            for spec_group in data['specs'][:1]:  # 第一个规格组(如尺寸)
                print(f"  {spec_group['name']}:")
                for spec in spec_group['values'][:2]:
                    print(f"    {spec['name']}: 单价¥{spec['singlePrice']}/个 | 库存{spec['stock']['totalStock']}个")
        print(f"详情页: {data['url']}")
    else:
        print(f"获取失败: {result['error_msg']}(错误码: {result.get('code')})")

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

  1. 批量阶梯价解析
    • 从动态接口提取ladderPrices字段,按采购量升序排序;

    • 补充 “起订量 - 阶梯价” 映射关系(如 “10-99 个” 对应单价 1.5 元);

    • 示例代码中_fetch_api_data函数解析阶梯价,支持后续采购成本计算。

    • 问题:京东工业商品普遍采用阶梯定价(采购量越大单价越低),数据格式为嵌套列表(如[{"count":100, "price":1.4}, ...]),需结构化提取并关联采购量。

    • 解决方案

  2. 工业参数结构化
    • 从动态接口params字段提取键值对(如{"材质": "高强度钢"});

    • 对特殊参数(如执行标准)进行标准化处理(如 “GB/T 5782-2016” 保留完整编号);

    • 示例代码中直接复用接口返回的结构化参数,确保专业性。

    • 问题:工业商品参数(如材质、强度等级)专业术语多,格式混乱(部分为表格,部分为文本),需统一结构化。

    • 解决方案

  3. JSONP 响应处理
    • 用正则表达式匹配 JSONP 中的 JSON 主体(re.search(r"jQuery\d+_\d+\((\{.*\})\)", response.text));

    • 去除回调函数名后解析为 JSON 对象;

    • 处理特殊字符(如转义符、换行符),确保解析不报错。

    • 问题:核心接口返回 JSONP 格式(含回调函数名),无法直接解析为 JSON。

    • 解决方案

  4. 反爬机制对抗
    • 代理 IP 池:使用企业级高匿代理,每 1 次请求切换 IP,避免单一 IP 被标记;

    • 请求频率控制:单 IP 每分钟请求≤1 次,两次请求间隔 2-4 秒,模拟企业采购人员浏览节奏;

    • Cookie 池管理:维护多个企业账号 Cookie(不同行业 / 地区),随机携带以降低风险;

    • 动态参数模拟callback参数按前端规则生成(如jQuery11240+随机数+时间戳),避免固定值被识别。

    • 问题:京东工业对 B2B 采购场景的反爬限制严格,高频请求会触发 IP 封锁或 Cookie 失效。

    • 解决方案

六、最佳实践与合规要点

  1. 系统架构设计采用 “低频率精准采集” 架构,适配京东工业 B2B 场景特性:
    • 采集层:单节点单小时请求≤30 次,绑定企业账号 Cookie 和专用代理;

    • 解析层:重点处理阶梯价和工业参数,确保采购决策数据准确性;

    • 存储层:用 Redis 缓存价格和库存(2 小时过期),MySQL 存储供应商资质和参数表(长期有效);

    • 监控层:实时监控 Cookie 有效性、代理存活率,异常时自动切换账号和代理。

  2. 性能优化策略
    • 按需采集:优先获取价格、库存等高频变动字段,参数和供应商信息定时更新(如每日 1 次);

    • 批量任务调度:集中在非高峰时段(如凌晨)执行批量采集,降低反爬触发概率;

    • 缓存复用:对同一商品的重复请求,直接返回缓存结果(缓存时效根据库存波动频率调整)。

  3. 合规性与风险控制
    • 访问限制:单账号日请求量≤100 次,避免对平台服务器造成压力,符合 robots 协议;

    • 数据使用边界:不得将商品数据用于恶意竞价、虚假采购,需注明来源 “京东工业”;

    • 企业信息保护:供应商资质和联系方式受商业保护,不得擅自泄露或商用。

七、总结

京东工业item_get接口的对接核心在于批量阶梯价的精准解析工业参数的结构化提取B2B 场景的反爬适配。开发者需重点关注:
  1. 阶梯价与采购量的关联逻辑(支撑企业批量采购决策);

  2. JSONP 响应的解析处理(确保动态数据可获取);

  3. 企业账号 Cookie 与代理池的协同管理(应对严格反爬)。

通过本文的技术方案,可构建稳定的商品详情获取系统,为企业采购、供应链管理等场景提供可靠数据支持。实际应用中,需根据平台最新接口格式动态调整解析规则,平衡数据获取效率与合规性。
需要进一步了解京东工业阶梯价计算逻辑企业 Cookie 获取方法,可以告诉我,我会补充相关内容


群贤毕至

访客