×

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

万邦科技Lex 万邦科技Lex 发表于2025-11-19 09:45:30 浏览30 评论0

抢沙发发表评论

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

京东工业(专注工业用品 B2B 采购的电商平台)的工业品搜索功能(item_industries接口,非官方命名)是获取特定品类或关键词工业品列表的核心入口,数据包含批量价格、规格参数、供应商资质等关键信息,对企业采购选品、供应链比价、竞品分析等场景具有重要价值。由于平台无公开官方 API,开发者需通过页面解析或逆向工程实现搜索对接。本文系统讲解接口逻辑、参数解析、技术实现及工业场景适配策略,助你构建稳定的京东工业商品列表获取系统。

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

  1. 核心功能京东工业item_industries接口通过关键词、分类、规格、价格等条件筛选工业品,返回符合条件的列表数据,核心字段聚焦工业 B2B 特性:
    • 基础信息:商品 ID(productId)、标题(含型号 / 规格,如 “M10×40mm 不锈钢螺栓 304 材质”)、主图、类目(如 “紧固件 > 螺栓 > 不锈钢螺栓”)、详情页 URL

    • 价格信息:单价(如 “¥0.8 / 个”)、批量阶梯价摘要(如 “1000 + 个 ¥0.6 / 个”)、含税标识(如 “含税”)、折扣信息(如 “企业专享 95 折”)

    • 规格参数:核心工业参数(如 “材质:304 不锈钢”“强度等级:A2-70”)、包装规格(如 “500 个 / 箱”)

    • 库存与采购:总库存(如 “50000 个”)、起订量(如 “最小起订 100 个”)、配送时效(如 “北京 24 小时达”)

    • 供应商信息:供应商名称(如 “XX 工业五金供应链有限公司”)、资质标签(如 “ISO9001 认证”“京东自营”)

    • 采购热度:近 30 天成交量(如 “成交 20000 个”)、好评率(如 “99% 好评”)

  2. 典型应用场景
    • 企业采购系统:搜索 “不锈钢螺栓”,按 “M10 规格 + 304 材质 + 价格≤1 元 / 个” 筛选,生成采购清单

    • 供应链比价:对比同规格轴承的不同供应商报价、批量折扣及配送时效,选择最优供应商

    • 品类分析:统计 “气动工具” 类目下的商品数量、价格分布及 Top 供应商,辅助市场调研

    • 库存预警:监控高频采购工业品(如 “六角螺母”)的库存变化,低于阈值时自动提醒

  3. 接口特性
    • 工业专业性:筛选条件与数据字段深度结合工业标准(如材质、精度等级、执行标准)

    • B2B 属性:突出批量价格、起订量、含税价等企业采购核心要素

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

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

    • 分页结构:默认每页 30 条,最多支持 50 页(约 1500 条结果),分页参数为page

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

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

    • 页面解析:BeautifulSoup(静态 HTML)、lxml(XPath 提取列表数据)

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

    • 数据处理:re(正则提取价格、库存)、urllib.parse(URL 参数编码)

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

    • 核心库:

  2. 搜索 URL 结构与核心参数京东工业搜索页基础 URL 为:https://b.jd.com/search/,核心参数通过查询字符串传递:
    筛选条件参数名示例值说明
    关键词keyword不锈钢螺栓 → 编码后为%E4%B8%8D%E9%94%88%E9%92%A2%E8%9E%BA%E6%A0%93支持商品名、型号(如 “M10”)、材质(如 “304”)
    分类 IDcat131946(紧固件)、131950(螺栓)分类 ID 需从导航栏解析获取(如 “紧固件> 螺栓” 对应131946_131950
    价格区间(始)priceFrom0.5最低单价(元)
    价格区间(终)priceTo1.0最高单价(元)
    品牌 / 供应商vendorId1000000123(京东自营)供应商 ID 需从筛选栏解析获取
    工业参数params{"材质":"304不锈钢","规格":"M10"}需 JSON 序列化并 URL 编码(如%7B%22%E6%9D%90%E8%B4%A8%22%3A%22304%E4%B8%8D%E9%94%88%E9%92%A2%22%7D
    起订量minOrder100最小起订量(个 / 件)
    排序方式sortprice-asc(价格升序)见 “排序参数表”
    分页page1 2 ... 50页码,默认 1,最大 50
  3. 排序参数表京东工业搜索支持工业采购场景常用排序方式,对应sort参数值如下:
    排序方式sort参数值适用场景
    综合推荐空值默认排序,平衡相关性与采购热度
    价格升序price-asc低成本采购筛选(如标准件螺栓)
    价格降序price-desc高端工业品筛选(如精密轴承)
    销量降序sales-desc爆款工业品筛选(如通用规格螺母)
    好评优先好评率-desc品质优先筛选(如高压阀门)
    最新上架new-desc新品跟踪(如新型号气动工具)
  4. 分类 ID 与工业参数映射
    • 分类 ID:访问京东工业分类页(https://b.jd.com/category/),通过开发者工具查看类目链接的href(如/category/131946_131950.html131946_131950为 “紧固件> 螺栓” 的 ID);

    • 工业参数:从搜索页左侧筛选栏提取(如 “材质” 可选 “304 不锈钢”“45# 钢”,对应params参数的键值对)。

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

以 “搜索 M10 规格 304 不锈钢螺栓,价格 0.5-1 元 / 个,起订量≤100 个,按销量降序排序” 为例,流程为参数组装→URL 构建→请求发送→列表解析→分页遍历
  1. URL 构建示例组合参数生成目标搜索 URL:
    python
    运行
    keyword = "不锈钢螺栓"cat = "131946_131950"  # 紧固件>螺栓分类IDpriceFrom = 0.5priceTo = 1.0# 工业参数:材质=304不锈钢,规格=M10(需JSON序列化并编码)import json
    params = json.dumps({"材质": "304不锈钢", "规格": "M10"})encoded_params = urllib.parse.quote(params)minOrder = 100sort = "sales-desc"  # 销量降序page = 1# 关键词URL编码encoded_keyword = urllib.parse.quote(keyword, encoding="utf-8")# 构建URLurl = f"https://b.jd.com/search/?keyword={encoded_keyword}&cat={cat}&priceFrom={priceFrom}&priceTo={priceTo}&params={encoded_params}&minOrder={minOrder}&sort={sort}&page={page}"
  2. 请求头与反爬伪装模拟企业采购人员浏览器行为,需包含登录态 Cookie(企业账号):
    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/",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Cookie": "session-id=xxx; user-key=xxx; pin=xxx; enterpriseId=xxx"  # 企业账号登录后获取}
  3. 页面解析与数据提取搜索结果列表通常在 HTML 的<div class="product-list">标签内,每条商品信息包含以下核心字段:
    字段解析方式(CSS 选择器示例)说明
    商品 IDdiv标签的data-sku属性提取100012345678
    标题.product-title的文本如 “M10×40mm 304 不锈钢螺栓 全螺纹”
    主图.product-img imgsrc属性商品主图 URL
    单价.product-price的文本(提取数值)如 “¥0.8 / 个” 提取 “0.8”
    阶梯价摘要.ladder-price的文本如 “1000 + 个 ¥0.6 / 个”
    含税标识.tax-tag的文本如 “含税”
    规格参数.product-params的文本如 “材质:304 不锈钢规格:M10×40mm”
    库存状态.stock-text的文本如 “有货 50000 个”
    起订量.min-order的文本如 “起订 100 个”
    供应商.vendor-name的文本如 “XX 工业供应链有限公司”
    资质标签.qualification-tags span的文本如 “ISO9001京东自营”
    采购热度.sales-volume的文本如 “30 天成交 20000 个”
  4. 动态数据补充(部分场景)部分高级筛选(如 “配送时效”“库存区域”)或分页数据通过 AJAX 接口加载,需单独调用:
    • 动态列表接口示例:https://b.jd.com/search/searchAjax,参数包含keywordpageparams等;

    • 响应为 JSON 格式,包含商品 ID、价格、库存等结构化数据(需处理sign签名,逻辑与详情接口类似)。

  5. 分页处理
    • 分页通过page参数控制,前 50 页为有效数据,超过则返回重复或空结果;

    • 终止条件:当前页商品数量 < 30(最后一页)或页码≥50;

    • 分页间隔:每页请求间隔 3-5 秒(随机波动),避免触发反爬(京东工业对 B2B 场景的访问频率限制严格)。

四、代码实现示例(Python)

以下是item_industries接口的完整实现,包含多条件筛选、工业参数处理、分页遍历及反爬处理:
import requests
import time
import random
import re
import json
import urllib.parse
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from typing import List, Dict

class JdIndustrialSearchApi:
    def __init__(self, proxy_pool: List[str] = None, cookie: str = ""):
        self.base_url = "https://b.jd.com/search/"
        self.ua = UserAgent()
        self.proxy_pool = proxy_pool  # 代理池列表,如["http://ip:port", ...]
        self.cookie = cookie  # 企业账号登录态Cookie
        # 分类ID映射(简化版)
        self.category_map = {
            "紧固件-螺栓": "131946_131950",
            "气动工具-风批": "131960_131962",
            "轴承-深沟球轴承": "131970_131971"
        }
        # 供应商ID映射(简化版)
        self.vendor_map = {
            "京东自营": "1000000123",
            "XX工业供应链": "1000000456"
        }

    def _get_headers(self) -> Dict[str, str]:
        """生成随机请求头"""
        headers = {
            "User-Agent": self.ua.random,
            "Referer": "https://b.jd.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 _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_stock(self, stock_str: str) -> int:
        """清洗库存(提取数字)"""
        if not stock_str:
            return 0
        stock_num = re.search(r"\d+", stock_str)
        return int(stock_num.group()) if stock_num else 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
        product_id = item_soup.get("data-sku") or ""
        
        # 提取规格参数(转换为字典)
        params_text = item_soup.select_one(".product-params")?.text.strip() or ""
        params = {}
        if params_text:
            for param in params_text.split("|"):
                if ":" in param:
                    key, value = param.split(":", 1)
                    params[key.strip()] = value.strip()
        
        # 提取资质标签
        qualification_tags = [tag.text.strip() for tag in item_soup.select(".qualification-tags span")]

        return {
            "product_id": product_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://b.jd.com/item/{product_id}.html" if product_id else "",
            "price": {
                "single": self._clean_price(item_soup.select_one(".product-price")?.text or ""),
                "single_str": item_soup.select_one(".product-price")?.text.strip() or "",
                "ladder_str": item_soup.select_one(".ladder-price")?.text.strip() or "",
                "tax": "含税" in (item_soup.select_one(".tax-tag")?.text or "")
            },
            "params": params,  # 工业参数(如材质、规格)
            "stock": {
                "stock_str": item_soup.select_one(".stock-text")?.text.strip() or "",
                "total": self._clean_stock(item_soup.select_one(".stock-text")?.text or "")
            },
            "purchase": {
                "min_order": item_soup.select_one(".min-order")?.text.strip() or "",
                "delivery": item_soup.select_one(".delivery-time")?.text.strip() or ""
            },
            "vendor": {
                "name": item_soup.select_one(".vendor-name")?.text.strip() or "",
                "qualifications": qualification_tags
            },
            "sales": {
                "volume": self._clean_sales(item_soup.select_one(".sales-volume")?.text or ""),
                "volume_str": item_soup.select_one(".sales-volume")?.text.strip() or "",
                "good_rate": item_soup.select_one(".good-rate")?.text.strip() or ""
            }
        }

    def _parse_page(self, html: str) -> List[Dict]:
        """解析页面的商品列表"""
        soup = BeautifulSoup(html, "lxml")
        # 商品列表容器(需根据实际页面结构调整)
        item_list = soup.select("div.product-list .product-item")
        return [self._parse_item(item) for item in item_list if item.get("data-sku")]

    def _get_total_pages(self, html: str) -> int:
        """获取总页数"""
        soup = BeautifulSoup(html, "lxml")
        page_box = soup.select_one(".pager-box")
        if not page_box:
            return 1
        # 提取最后一页页码
        last_page = page_box.select("a.page")[-1].text.strip() if page_box.select("a.page") else "1"
        return int(last_page) if last_page.isdigit() else 1

    def item_industries(self, 
                       keyword: str = "", 
                       category: str = "", 
                       price_from: float = None, 
                       price_to: float = None, 
                       params: Dict = None, 
                       vendor: str = "", 
                       min_order: int = None, 
                       sort: str = "", 
                       page_limit: int = 5) -> Dict:
        """
        搜索京东工业工业品列表
        :param keyword: 搜索关键词
        :param category: 分类名称(如“紧固件-螺栓”)或分类ID
        :param price_from: 最低单价(元)
        :param price_to: 最高单价(元)
        :param params: 工业参数字典(如{"材质": "304不锈钢", "规格": "M10"})
        :param vendor: 供应商名称(如“京东自营”)或供应商ID
        :param min_order: 最大起订量(个/件)
        :param sort: 排序方式(sales-desc/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:
                cat_id = self.category_map[category]
            else:
                cat_id = category if category else ""
            # 转换供应商名称为ID
            vendor_id = self.vendor_map.get(vendor, "") if vendor else ""
            # 编码关键词
            encoded_keyword = urllib.parse.quote(keyword, encoding="utf-8") if keyword else ""
            # 处理工业参数(JSON序列化并编码)
            encoded_params = ""
            if params and isinstance(params, dict):
                encoded_params = urllib.parse.quote(json.dumps(params, ensure_ascii=False))

            all_items = []
            current_page = 1

            while current_page <= page_limit:
                # 构建参数
                params_dict = {
                    "page": current_page
                }
                if encoded_keyword:
                    params_dict["keyword"] = encoded_keyword
                if cat_id:
                    params_dict["cat"] = cat_id
                if price_from is not None:
                    params_dict["priceFrom"] = price_from
                if price_to is not None:
                    params_dict["priceTo"] = price_to
                if encoded_params:
                    params_dict["params"] = encoded_params
                if vendor_id:
                    params_dict["vendorId"] = vendor_id
                if min_order is not None:
                    params_dict["minOrder"] = min_order
                if sort:
                    params_dict["sort"] = sort

                # 发送请求(带随机延迟)
                time.sleep(random.uniform(3, 5))  # 控制频率,避免反爬
                headers = self._get_headers()
                proxy = self._get_proxy()

                response = requests.get(
                    url=self.base_url,
                    params=params_dict,
                    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

            # 去重(基于product_id)
            seen_ids = set()
            unique_items = []
            for item in all_items:
                if item["product_id"] not in seen_ids:
                    seen_ids.add(item["product_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}
            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客户端
    search_api = JdIndustrialSearchApi(proxy_pool=PROXIES, cookie=COOKIE)

    # 搜索“不锈钢螺栓”,分类“紧固件-螺栓”,价格0.5-1元,参数{材质:304不锈钢,规格:M10},起订量≤100,销量降序,最多3页
    result = search_api.item_industries(
        keyword="不锈钢螺栓",
        category="紧固件-螺栓",
        price_from=0.5,
        price_to=1.0,
        params={"材质": "304不锈钢", "规格": "M10"},
        min_order=100,
        sort="sales-desc",
        page_limit=3
    )

    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']['single_str']} | 阶梯价:{item['price']['ladder_str'] or '无'} | {'含税' if item['price']['tax'] else '不含税'}")
            print(f"规格参数:{'; '.join([f'{k}:{v}' for k, v in item['params'].items()])}")
            print(f"库存销量:{item['stock']['stock_str']} | 30天成交{item['sales']['volume']}个 | 好评率{item['sales']['good_rate']}")
            print(f"采购信息:{item['purchase']['min_order']} | 配送:{item['purchase']['delivery']}")
            print(f"供应商:{item['vendor']['name']} | 资质:{', '.join(item['vendor']['qualifications'])}")
            print(f"详情页:{item['url']}")
    else:
        print(f"搜索失败:{result['error_msg']}(错误码:{result.get('code')})")

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

  1. 工业参数筛选的结构化处理
    • 封装参数处理逻辑,自动将字典(如{"材质": "304不锈钢"})转换为 JSON 字符串并编码;

    • 提供常用参数映射表(如 “材质” 对应筛选栏的选项值),避免拼写错误;

    • 示例代码中item_industries函数对params参数的处理,确保格式符合接口要求。

    • 问题:工业参数(如材质、规格、精度等级)格式多样,需以键值对形式传入params参数,且需经过 JSON 序列化和 URL 编码,易出现格式错误。

    • 解决方案

  2. 批量阶梯价的提取与解读
    • 列表页提取阶梯价摘要作为参考;

    • 对重点商品,通过后续item_get接口获取完整阶梯价数据(如ladderPrices列表);

    • 示例代码中_parse_item函数提取ladder_str字段,同时预留与详情接口的联动逻辑。

    • 问题:列表页的阶梯价通常以摘要形式展示(如 “1000 + 个 ¥0.6 / 个”),缺乏完整阶梯区间(如 “100-999 个” 对应价格),影响采购决策。

    • 解决方案

  3. 动态接口签名处理(若需)
    • 从京东工业前端 JS 中提取sign生成逻辑(通常基于keywordpagetimestamp的混合加密);

    • execjs调用 JS 代码生成sign参数,或用 Python 复现加密算法;

    • 示例代码可扩展动态接口调用逻辑,适配复杂筛选场景。

    • 问题:部分高级筛选(如按 “配送区域” 筛选)依赖动态接口searchAjax,需携带sign参数(通过 JS 加密生成)。

    • 解决方案

  4. 反爬机制对抗
    • 代理 IP 策略:使用企业级高匿代理池,每 1 页切换 1 个 IP,优先选择与企业账号地区一致的 IP;

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

    • Cookie 池管理:维护多个企业账号 Cookie(不同行业 / 采购量),随机轮换使用,避免单一账号被标记;

    • 参数随机性User-Agent每次请求随机生成,Referer设置为京东工业首页或分类页,降低异常识别概率。

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

    • 解决方案

六、最佳实践与合规要点

  1. 系统架构设计采用 “精准化低频率采集” 架构,适配京东工业 B2B 场景特性:
    • 任务调度层:按品类和关键词优先级分发搜索任务,控制单任务并发数≤1;

    • 采集层:每个任务绑定独立代理和 Cookie,按工业参数维度分组采集(如按 “材质”“规格” 拆分);

    • 存储层:用 Redis 缓存搜索结果(1 小时过期,价格库存波动快),MySQL 存储供应商资质和参数字典(长期有效);

    • 监控层:实时监控代理存活率、Cookie 有效性及数据完整度,异常时自动切换资源并告警。

  2. 性能优化策略
    • 异步批量搜索:使用aiohttp并发处理多关键词(如 “不锈钢螺栓”“六角螺母”),控制并发数≤1(因反爬限制);

    • 按需解析:列表页优先提取product_id、单价、库存等核心字段,详细参数和阶梯价通过item_get接口补充;

    • 热点缓存:对高频搜索的工业品(如通用规格紧固件)缩短缓存时间(30 分钟),低频品类延长至 2 小时。

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

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

    • 商业信息保护:供应商资质、联系方式等商业信息受法律保护,不得擅自泄露或用于非采购场景。

七、总结

京东工业item_industries接口的对接核心在于工业参数的结构化筛选B2B 采购字段的精准提取低频率高稳定性的采集策略。开发者需重点关注:
  1. 工业参数params的格式处理(确保筛选条件生效);

  2. 批量阶梯价与起订量的关联逻辑(支撑企业采购决策);

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

通过本文的技术方案,可构建稳定的工业品列表获取系统,为企业采购选品、供应链管理等场景提供可靠数据支持。实际应用中,需根据平台最新页面结构动态调整解析规则,平衡数据获取效率与合规性。
需要进一步了解京东工业分类 ID 完整映射表动态接口签名生成细节,可以告诉我,我会补充相关内容


群贤毕至

访客