×

化工网 item_search 接口对接全攻略:从入门到精通

万邦科技Lex 万邦科技Lex 发表于2025-11-20 13:27:14 浏览24 评论0

抢沙发发表评论

                      注册账号免费测试化工网API数据接口

化工网(聚焦化工原料、精细化工、塑料橡胶等垂直领域的 B2B 电商平台,如化工网、盖德化工网、摩贝网等)的item_search接口(非官方命名)是按关键词 / 筛选条件批量获取化工商品列表的核心入口。数据覆盖化工产品的纯度、CAS 号、供应规格、批量报价、供应商资质等关键信息,对化工采购比价、供应链选型、市场行情监控等场景具有核心价值。由于化工网多为垂直领域站点,无统一公开 API,需通过页面解析 + 动态接口逆向实现对接。本文聚焦化工行业特性,系统讲解接口逻辑、参数设计、技术实现及反爬应对,助你构建稳定的化工商品搜索系统。

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

  1. 核心功能化工网item_search接口通过关键词、纯度、CAS 号、产地、供应方式等条件筛选商品,返回符合条件的列表数据,核心字段聚焦化工行业 B2B 特性:
    • 基础信息:商品 ID(productId)、标题(含纯度 / 型号 / 规格,如 “99.9% 无水乙醇 工业级 CAS64-17-5”)、主图 + 样品图、类目(如 “有机化工 > 醇类 > 无水乙醇”)、详情页 URL

    • 规格参数:化工核心参数(纯度、含量、CAS 号、分子式、分子量)、执行标准(如 “GB/T 678-2021”)、外观(如 “无色透明液体”)、包装规格(如 “200kg / 桶”“1000kg/IBC 桶”)

    • 价格信息:单价(如 “¥7500 / 吨”)、批量阶梯价(如 “1-5 吨 ¥7500 / 吨,10 + 吨 ¥7200 / 吨”)、含税标识、结算方式(如 “款到发货”“月结”)

    • 供应信息:现货 / 期货(如 “100 吨现货”“期货 7 天交货”)、起订量(如 “1 吨起订”)、供应范围(如 “全国配送”“华东地区自提”)、运输方式(如 “危化品专用车”)

    • 供应商信息:厂商名称(如 “山东 XX 化工集团”)、资质标签(如 “危化品经营许可证”“ISO9001 认证”“源头工厂”)、产能(如 “年产 5 万吨”)

    • 安全与合规:危险类别(如 “易燃液体”)、UN 编号、MSDS 报告(安全技术说明书)、危化品经营许可证编号

  2. 典型应用场景
    • 化工采购比价:搜索 “无水乙醇 99.9%”,按 “CAS64-17-5 + 价格≤7500 元 / 吨 + 10 吨起订” 筛选,对比不同供应商的批量折扣和运输成本

    • 供应链选型:搜索 “聚丙烯 PP 均聚级”,按 “熔指 2.0 + 产地江苏 + 源头工厂” 筛选,选择符合生产需求的原料供应商

    • 市场行情监控:跟踪 “98% 硫酸” 的价格波动和库存变化,辅助采购时机决策(如低价囤货)

    • 合规采购审核:提取供应商的危化品经营许可证、MSDS 报告,确保采购产品符合安全生产规范

  3. 接口特性
    • 强合规性:数据突出危化品资质、MSDS 报告等合规字段(化工行业核心要求)

    • 规格标准化:纯度、CAS 号、执行标准等参数具有行业统一标准,筛选条件结构化程度高

    • 动态加载:搜索结果、价格、库存通过 AJAX 动态加载(避免静态 HTML 爬取)

    • 反爬机制:包含 IP 限制、User-Agent 校验、登录态 Cookie 验证、部分站点带签名参数(sign

    • 多站点差异:不同化工网页面结构差异(如盖德化工网与摩贝网的参数布局不同),但核心字段(纯度、CAS 号)一致

  4. 主流化工网特性对比
    平台优势反爬强度核心接口类型适配场景
    盖德化工网数据全、供应商资质全中等静态 HTML+AJAX综合化工原料搜索
    摩贝网规格参数标准化、MSDS 全较高动态 API(带签名)精细化工、医药中间体搜索
    化工网(hc360)供应商多、价格透明较低静态 HTML工业级化工原料采购
    中国化工网危化品资质审核严格中等AJAX 动态列表危化品采购(如乙醇、硫酸)

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

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

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

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

    • 数据处理:re(正则提取价格 / 纯度)、json(解析动态接口响应)、urllib.parse(参数编码)

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

    • 核心库:

  2. 搜索 URL 结构与核心参数以主流化工网(盖德化工网为例)的搜索基础 URL:https://www.guidechem.com/search/,核心参数通过查询字符串传递(需 URL 编码):
    筛选条件参数名示例值说明
    关键词keyword无水乙醇 99.9% → 编码后为%E6%97%A0%E6%B0%B4%E4%B9%99%E9%86%87+99.9%25支持商品名、CAS 号(如 “64-17-5”)、分子式
    类目 IDcatId289(有机化工 > 醇类)类目 ID 需从化工网导航栏解析(如通过开发者工具查看)
    纯度区间purity99(≥99%)、99.9(≥99.9%)部分平台支持区间筛选(如purityFrom=99&purityTo=99.9
    CAS 号cas64-17-5(无水乙醇 CAS 号)精准筛选特定化工产品(唯一标识)
    价格区间(始)priceFrom7000最低单价(元,如 7000 元 / 吨)
    价格区间(终)priceTo8000最高单价(元)
    产地area山东江苏支持省份或城市筛选
    供应类型supplyType1(现货)、2(期货)筛选现货 / 期货商品
    起订量minOrder10最小起订量(吨 / 桶 / 千克)
    供应商类型sellerType1(工厂)、2(贸易商)优先选择工厂供应商(价格更低)
    排序方式sortprice-asc(价格升序)、sales-desc(销量降序)化工网常用排序:价格、销量、资质优先
    分页page1 2 ... 50页码,多数化工网最多支持 50 页(约 1500 条结果)
  3. 动态接口结构(以摩贝网为例)部分化工网(如摩贝网)的搜索结果通过动态 API 返回,核心接口格式:
    • 接口 URL:https://www.molbase.com/api/v1/products/search

    • 请求方式:GET/POST(部分站点用 POST 传递参数)

    • 核心参数:keyword(关键词)、page(页码)、cas(CAS 号)、sign(签名参数)、timestamp(时间戳)

    • 响应格式:JSON,包含商品列表、分页信息、参数详情

  4. 前置准备要点
    • 类目 ID 映射:从化工网导航栏解析(如 “有机化工> 醇类”→289);

    • CAS 号映射:维护常用化工产品的 CAS 号字典(如 “无水乙醇”→64-17-5),支持精准筛选;

    • 危化品类别映射:从筛选栏提取(如 “易燃液体”“腐蚀品”),适配合规筛选需求

    • Cookie 获取:通过化工网账号(企业账号更佳,可查看完整报价)登录,从浏览器开发者工具(Application→Cookies)复制cookie(含useridsessionId等字段),部分站点需登录查看危化品价格

    • 代理池搭建:化工网对 IP 访问频率限制严格,需使用高匿动态代理(支持切换 IP),优先选择与供应商产地一致的 IP(如爬取山东供应商数据用山东 IP)

    • 核心映射表构建

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

以 “搜索 99.9% 无水乙醇(CAS64-17-5),价格 7000-8000 元 / 吨,山东产地,工厂现货,10 吨起订,按价格升序排序” 为例,核心流程为参数组装→URL / 动态接口构建→请求发送→数据解析→分页遍历
  1. 参数组装与编码按筛选条件构建参数字典,对中文参数进行 URL 编码:
    python
    运行
    import urllib.parse
    
    params_dict = {
        "keyword": urllib.parse.quote("无水乙醇 99.9%", encoding="utf-8"),
        "cas": "64-17-5",  # 精准匹配CAS号
        "catId": "289",  # 有机化工>醇类类目ID
        "priceFrom": 7000,
        "priceTo": 8000,
        "area": urllib.parse.quote("山东", encoding="utf-8"),
        "supplyType": 1,  # 现货
        "sellerType": 1,  # 工厂
        "minOrder": 10,
        "sort": "price-asc",
        "page": 1}
  2. URL / 动态接口构建
    • 静态页面 URL(如盖德化工网):直接拼接参数生成 URL

      python
      运行
      base_url = "https://www.guidechem.com/search/"url = f"{base_url}?{urllib.parse.urlencode(params_dict)}"
    • 动态接口(如摩贝网):需携带签名参数和时间戳,示例:

      python
      运行
      api_url = "https://www.molbase.com/api/v1/products/search"timestamp = int(time.time() * 1000)sign = generate_sign(keyword="无水乙醇 99.9%", timestamp=timestamp, secret="xxx")  # 签名生成params = {
          "keyword": "无水乙醇 99.9%",
          "cas": "64-17-5",
          "page": 1,
          "timestamp": timestamp,
          "sign": sign}
  3. 请求发送(带反爬伪装)携带登录态 Cookie、随机 User-Agent 和代理 IP 发送请求,模拟真实用户行为:
    python
    运行
    import requestsfrom fake_useragent import UserAgent
    
    ua = UserAgent()headers = {
        "User-Agent": ua.random,
        "Referer": "https://www.guidechem.com/",
        "Cookie": "userid=xxx; sessionId=xxx; enterpriseId=xxx"  # 企业账号Cookie}proxy = {"http": "http://123.45.67.89:8888", "https": "https://123.45.67.89:8888"}  # 高匿代理response = requests.get(
        url=url,
        headers=headers,
        proxies=proxy,
        timeout=10)response.raise_for_status()  # 抛出HTTP错误
  4. 数据解析(结构化提取)根据响应类型(HTML/JSON)选择解析方式,核心字段提取逻辑:
    • HTML 解析(以盖德化工网为例)
      字段CSS 选择器 / XPath 示例解析逻辑
      商品标题.product-title a提取文本,去除多余空格
      纯度.purity-tag正则提取数字(如 “99.9%”→99.9)
      CAS 号.cas-code直接提取文本(如 “64-17-5”)
      单价.price-box .current-price正则提取数字(如 “¥7500 / 吨”→7500)
      包装规格.package-spec提取文本(如 “200kg / 桶”)
      供应商名称.seller-name a提取文本
      资质标签.qualification-tags span提取标签列表(如 “源头工厂”)
      现货状态.stock-tag提取文本(如 “现货 100 吨”)
    • JSON 解析(以摩贝网为例):商品列表数据在data.list中,核心字段提取示例:
      python
      运行
      import json
      
      data = json.loads(response.text)product_list = data.get("data", {}).get("list", [])for product in product_list:
          product_id = product.get("id")
          title = product.get("name")  # 商品标题
          purity = product.get("purity")  # 纯度
          cas = product.get("casNo")  # CAS号
          price = product.get("price")  # 单价
          package = product.get("packageSpec")  # 包装规格
  5. 分页处理
    • 分页终止条件:当前页商品数量 < 30(最后一页)、页码≥50(化工网最大页数)或响应中无数据

    • 分页间隔:每页请求间隔 4-6 秒(随机波动),且切换代理 IP(避免单 IP 被封禁)

    • 去重处理:基于productIdCAS号+供应商ID去重(部分商品可能在多页重复出现)

四、代码实现示例(Python)

以下是化工网item_search接口的完整实现(适配盖德化工网,支持静态页面解析 + 动态接口兼容),包含多条件筛选、数据结构化、分页遍历及反爬处理:
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, Tuple

class ChemicalItemSearchApi:
    def __init__(self, platform: str = "guidechem", proxy_pool: List[str] = None, cookie: str = ""):
        """
        初始化化工网搜索API
        :param platform: 化工网平台(guidechem=盖德化工网,molbase=摩贝网)
        :param proxy_pool: 代理池列表(如["http://ip:port", ...])
        :param cookie: 登录态Cookie(企业账号更佳)
        """
        self.platform = platform.lower()
        self.proxy_pool = proxy_pool
        self.cookie = cookie
        self.ua = UserAgent()
        # 平台基础配置(可扩展多平台)
        self.platform_config = {
            "guidechem": {
                "base_url": "https://www.guidechem.com/search/",
                "item_selector": ".product-item",  # 商品列表选择器
                "category_map": {  # 类目ID映射(简化版)
                    "有机化工-醇类": "289",
                    "无机化工-酸类": "301",
                    "塑料-聚丙烯": "402",
                    "危化品-易燃液体": "505"
                }
            },
            "molbase": {
                "base_url": "https://www.molbase.com/api/v1/products/search",
                "category_map": {}  # 摩贝网类目ID需单独解析
            }
        }
        self.config = self.platform_config.get(self.platform, self.platform_config["guidechem"])

    def _get_headers(self) -> Dict[str, str]:
        """生成随机请求头"""
        headers = {
            "User-Agent": self.ua.random,
            "Referer": self.config["base_url"].split("/search")[0],
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "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 _generate_sign(self, params: Dict) -> str:
        """生成动态接口签名(适配摩贝网等平台,简化版)"""
        # 真实签名需逆向平台JS逻辑,此处为示例占位
        if self.platform == "molbase":
            timestamp = params.get("timestamp", "")
            keyword = params.get("keyword", "")
            secret = "molbase_secret_key"  # 需从JS中提取真实密钥
            sign_str = f"{keyword}{timestamp}{secret}"
            return re.sub(r"[^\w]", "", sign_str).upper()  # 简化加密逻辑
        return ""

    def _clean_price(self, price_str: str) -> Tuple[float, str]:
        """清洗价格(提取数值和单位)"""
        if not price_str:
            return 0.0, ""
        # 匹配“¥7500/吨”“7200元/桶”等格式
        price_match = re.search(r"(\d+\.?\d*)", price_str)
        unit_match = re.search(r"(\w+)/(\w+)", price_str)  # 提取单位(如“元/吨”)
        price = float(price_match.group()) if price_match else 0.0
        unit = unit_match.group() if unit_match else ""
        return price, unit

    def _clean_purity(self, purity_str: str) -> float:
        """清洗纯度(提取数值,如“99.9%”→99.9)"""
        if not purity_str:
            return 0.0
        purity_match = re.search(r"(\d+\.?\d*)", purity_str)
        return float(purity_match.group()) if purity_match else 0.0

    def _clean_stock(self, stock_str: str) -> Tuple[int, str]:
        """清洗库存(提取数值和单位,如“现货100吨”→(100, "吨"))"""
        if not stock_str:
            return 0, ""
        stock_match = re.search(r"(\d+)", stock_str)
        unit_match = re.search(r"(\w+)", stock_str.replace("现货", ""))
        stock = int(stock_match.group()) if stock_match else 0
        unit = unit_match.group() if unit_match else ""
        return stock, unit

    def _parse_guidechem_product(self, item_soup) -> Dict:
        """解析盖德化工网单条商品数据"""
        # 提取资质标签
        qualification_tags = [tag.text.strip() for tag in item_soup.select(".qualification-tags span")]
        # 提取价格(单价+单位)
        price_str = item_soup.select_one(".price-box .current-price")?.text.strip() or ""
        price, price_unit = self._clean_price(price_str)
        # 提取库存
        stock_str = item_soup.select_one(".stock-tag")?.text.strip() or ""
        stock, stock_unit = self._clean_stock(stock_str)
        # 提取规格参数(纯度、CAS号、包装)
        purity_str = item_soup.select_one(".purity-tag")?.text.strip() or ""
        cas_str = item_soup.select_one(".cas-code")?.text.strip() or ""
        package_str = item_soup.select_one(".package-spec")?.text.strip() or ""

        return {
            "product_id": item_soup.get("data-id") or "",
            "title": item_soup.select_one(".product-title a")?.text.strip() or "",
            "url": item_soup.select_one(".product-title a")?.get("href") or "",
            "specs": {
                "purity": self._clean_purity(purity_str),  # 纯度(如99.9)
                "purity_str": purity_str,
                "cas": cas_str,  # CAS号
                "package": package_str,  # 包装规格(如200kg/桶)
                "appearance": item_soup.select_one(".appearance")?.text.strip() or ""  # 外观
            },
            "price": {
                "single": price,
                "single_str": price_str,
                "unit": price_unit,  # 价格单位(如元/吨)
                "tax": "含税" in (item_soup.select_one(".tax-tag")?.text or "")
            },
            "supply": {
                "stock": stock,
                "stock_str": stock_str,
                "stock_unit": stock_unit,
                "min_order": item_soup.select_one(".min-order")?.text.strip() or "1吨起订",
                "supply_type": "现货" if "现货" in stock_str else "期货",
                "delivery_time": item_soup.select_one(".delivery-time")?.text.strip() or "7天内"
            },
            "seller": {
                "name": item_soup.select_one(".seller-name a")?.text.strip() or "",
                "type": "工厂" if "源头工厂" in qualification_tags else "贸易商",
                "area": item_soup.select_one(".seller-area")?.text.strip() or "",
                "qualifications": qualification_tags,
                "capacity": item_soup.select_one(".capacity")?.text.strip() or ""  # 产能
            },
            "safety": {
                "hazard_class": item_soup.select_one(".hazard-class")?.text.strip() or "",  # 危险类别
                "msds": "有" if item_soup.select_one(".msds-tag") else "无"  # 是否有MSDS报告
            }
        }

    def _parse_product(self, item) -> Dict:
        """统一解析商品数据(适配多平台)"""
        if self.platform == "guidechem":
            return self._parse_guidechem_product(item)
        # 可扩展摩贝网等其他平台的解析逻辑
        return {}

    def item_search(self,
                   keyword: str = "",
                   category: str = "",
                   cas: str = "",
                   price_from: float = None,
                   price_to: float = None,
                   area: str = "",
                   supply_type: int = 1,  # 1=现货,2=期货
                   seller_type: int = 1,  # 1=工厂,2=贸易商
                   min_order: str = "1",
                   sort: str = "price-asc",
                   page_limit: int = 5) -> Dict:
        """
        化工网商品搜索
        :param keyword: 搜索关键词(如“无水乙醇”)
        :param category: 分类名称(如“有机化工-醇类”)或分类ID
        :param cas: CAS号(如“64-17-5”)
        :param price_from: 最低单价
        :param price_to: 最高单价
        :param area: 产地(如“山东”)
        :param supply_type: 供应类型(1=现货,2=期货)
        :param seller_type: 供应商类型(1=工厂,2=贸易商)
        :param min_order: 最小起订量(如“10”)
        :param sort: 排序方式(price-asc/price-desc/sales-desc)
        :param page_limit: 最大页数(默认5,最大50)
        :return: 标准化搜索结果
        """
        try:
            if not keyword and not cas:
                return {"success": False, "error_msg": "关键词(keyword)和CAS号(cas)至少需提供一个"}

            # 1. 参数预处理
            # 分类ID转换(从名称映射到ID)
            cat_id = self.config["category_map"].get(category, category) if category else ""
            # 编码中文参数
            encoded_keyword = urllib.parse.quote(keyword, encoding="utf-8") if keyword else ""
            encoded_area = urllib.parse.quote(area, encoding="utf-8") if area else ""

            all_products = []
            current_page = 1
            total_pages = 1

            while current_page <= page_limit and current_page <= 50:
                # 2. 构建请求参数
                if self.platform == "guidechem":
                    params = {
                        "keyword": encoded_keyword,
                        "catId": cat_id,
                        "cas": cas,
                        "priceFrom": price_from,
                        "priceTo": price_to,
                        "area": encoded_area,
                        "supplyType": supply_type,
                        "sellerType": seller_type,
                        "minOrder": min_order,
                        "sort": sort,
                        "page": current_page
                    }
                    # 过滤空值参数
                    params = {k: v for k, v in params.items() if v is not None and v != ""}
                    request_url = f"{self.config['base_url']}?{urllib.parse.urlencode(params)}"
                elif self.platform == "molbase":
                    timestamp = int(time.time() * 1000)
                    params = {
                        "keyword": keyword,
                        "cas": cas,
                        "page": current_page,
                        "supplyType": supply_type,
                        "sellerType": seller_type,
                        "timestamp": timestamp,
                        "sign": self._generate_sign({"keyword": keyword, "timestamp": timestamp})
                    }
                    request_url = self.config["base_url"]

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

                # 发送请求
                if self.platform == "guidechem":
                    response = requests.get(
                        url=request_url,
                        headers=headers,
                        proxies=proxy,
                        timeout=15
                    )
                else:  # molbase用POST请求
                    response = requests.post(
                        url=request_url,
                        json=params,
                        headers=headers,
                        proxies=proxy,
                        timeout=15
                    )
                response.raise_for_status()

                # 4. 解析响应数据
                if self.platform == "guidechem":
                    soup = BeautifulSoup(response.text, "lxml")
                    product_soup_list = soup.select(self.config["item_selector"])
                    if not product_soup_list:
                        print(f"第{current_page}页无商品,终止分页")
                        break
                    # 解析商品数据
                    parsed_products = [self._parse_product(item) for item in product_soup_list]
                else:  # molbase JSON响应
                    data = json.loads(response.text)
                    if not data.get("success", True):
                        print(f"第{current_page}页请求失败:{data.get('msg')}")
                        break
                    product_list = data.get("data", {}).get("list", [])
                    parsed_products = [self._parse_product(product) for product in product_list]

                all_products.extend(parsed_products)

                # 5. 获取总页数(仅第一页)
                if current_page == 1:
                    if self.platform == "guidechem":
                        total_page_str = soup.select_one(".total-page")?.text.strip()
                        total_pages = int(re.search(r"\d+", total_page_str).group()) if total_page_str else 1
                    else:
                        total_pages = data.get("data", {}).get("totalPage", 1)
                    total_pages = min(total_pages, page_limit, 50)  # 限制最大页数
                    print(f"共{total_pages}页商品,开始遍历...")

                # 6. 判断是否继续分页
                if current_page >= total_pages:
                    break

                current_page += 1

            # 7. 去重(基于productId或CAS+供应商名称)
            seen_keys = set()
            unique_products = []
            for product in all_products:
                if product["product_id"]:
                    key = product["product_id"]
                else:
                    key = f"{product['specs']['cas']}_{product['seller']['name']}"  # 备选去重键
                if key not in seen_keys:
                    seen_keys.add(key)
                    unique_products.append(product)

            return {
                "success": True,
                "total": len(unique_products),
                "total_pages": total_pages,
                "page_processed": current_page - 1,
                "products": unique_products,
                "platform": self.platform
            }

        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 = "userid=xxx; sessionId=xxx; enterpriseId=xxx"  # 替换为化工网登录Cookie

    # 初始化API客户端(默认盖德化工网)
    search_api = ChemicalItemSearchApi(
        platform="guidechem",
        proxy_pool=PROXIES,
        cookie=COOKIE
    )

    # 搜索配置:99.9%无水乙醇(CAS64-17-5),山东产地,工厂现货,10吨起订
    result = search_api.item_search(
        keyword="无水乙醇 99.9%",
        category="有机化工-醇类",
        cas="64-17-5",
        price_from=7000,
        price_to=8000,
        area="山东",
        supply_type=1,  # 现货
        seller_type=1,  # 工厂
        min_order="10",
        sort="price-asc",  # 价格升序
        page_limit=3
    )

    # 结果输出
    if result["success"]:
        print(f"搜索成功:共找到 {result['total']} 件商品,遍历 {result['page_processed']}/{result['total_pages']} 页")
        for i, product in enumerate(result["products"][:5]):  # 打印前5条
            print(f"\n商品 {i+1}:")
            print(f"标题:{product['title'][:60]}...")
            print(f"核心规格:纯度{product['specs']['purity']}% | CAS:{product['specs']['cas']} | 包装:{product['specs']['package']}")
            print(f"价格信息:¥{product['price']['single']}/{product['price']['unit']} | {'含税' if product['price']['tax'] else '不含税'}")
            print(f"供应信息:{product['supply']['stock_str']} | {product['supply']['min_order']} | {product['supply']['delivery_time']}交货")
            print(f"供应商:{product['seller']['name']}({product['seller']['type']}) | 产地:{product['seller']['area']}")
            print(f"资质合规:{', '.join(product['seller']['qualifications']) or '无'} | MSDS:{product['safety']['msds']} | 危险类别:{product['safety']['hazard_class']}")
            print(f"详情页:{product['url']}")
    else:
        print(f"搜索失败:{result['error_msg']}(错误码:{result.get('code')})")

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

  1. 化工规格参数的标准化解析
    • 通用化清洗逻辑:用正则提取参数数值(如纯度统一转换为浮点型 “99.9”),保留原始字符串供参考;

    • 参数映射表:维护化工行业核心参数映射(如 “含量”“纯度”“纯度等级” 统一映射为 “purity”);

    • 优先级提取:优先提取 CAS 号(唯一标识)、纯度、包装规格等核心参数,非核心参数(如外观)可选填。

    • 问题:化工产品参数(纯度、CAS 号、包装规格)格式多样(如纯度 “99.9%”“≥99.9%”“99.90%”),不同平台参数命名不一致(如 “含量”=“纯度”),难以统一结构化。

    • 解决方案

  2. 危化品合规信息提取
    • 标签化提取:通过 “危化品”“MSDS”“易燃液体” 等关键词标签快速定位合规信息;

    • 详情页补充:列表页仅提取基础合规标签,完整信息(如 MSDS 报告 URL)通过item_get接口从详情页获取;

    • 资质校验:维护危化品资质关键词库(如 “危化品经营许可证”“安全生产许可证”),自动识别供应商合规性。

    • 问题:危化品相关信息(危险类别、MSDS 报告、危化品经营许可证)分散在页面不同位置,部分站点需登录或跳转详情页查看,且格式不统一。

    • 解决方案

  3. 动态接口签名参数破解
    • JS 逆向:通过浏览器开发者工具(Sources→搜索sign)找到生成sign的 JS 函数,提取加密逻辑(如 MD5 + 固定盐值、SHA256 加密);

    • 模拟执行:用execjs直接调用淘宝原始 JS 代码生成sign,避免 Python 复现的兼容性问题;

    • 定期更新:化工网 JS 加密逻辑可能更新,建议每月检查一次,同步调整签名生成代码。

    • 问题:部分化工网(如摩贝网)的动态接口需携带sign参数(基于关键词、时间戳、密钥的加密结果),直接请求返回 403 或空数据。

    • 解决方案

  4. 反爬机制深度对抗
    • 代理策略:使用企业级高匿动态代理池,单 IP 单日请求量≤20 次,每页请求切换 IP,且代理 IP 需与供应商产地或登录地区一致;

    • 行为模拟:模拟真实用户操作路径(首页→分类页→搜索页),请求间隔随机(4-6 秒),避免固定频率;

    • Cookie 池:维护多个企业账号 Cookie,随机轮换使用,每个 Cookie 单日请求量≤15 次;

    • 异常处理:若触发 403 错误,自动切换代理和 Cookie,暂停请求 10-15 分钟后重试。

    • 问题:化工网反爬机制针对性强(如限制单 IP 日请求量≤30 次、检测非浏览器行为、登录态验证),容易触发 IP 封禁或 Cookie 失效。

    • 解决方案

  5. 批量阶梯价完整获取
    • 两步采集:第一步通过item_search获取商品 ID 和基础信息,第二步对重点商品(如销量前 50、工厂供应商)调用item_get接口获取完整阶梯价;

    • 阶梯价结构化:将阶梯价转换为 “采购量区间 - 价格” 映射表(如[{min:1, max:5, price:7500}, {min:10, max:∞, price:7200}]),支持批量采购成本自动计算。

    • 问题:化工网列表页仅显示基础单价和简单阶梯价摘要(如 “10 + 吨享优惠”),完整阶梯价(多采购量区间对应价格)需跳转详情页,影响采购成本计算。

    • 解决方案

  6. 多平台适配
    • 配置化设计:将不同平台的基础 URL、商品选择器、参数路径封装为配置字典,新增平台时仅需补充配置;

    • 抽象解析逻辑:定义统一的商品数据结构(如product_idspecsprice),不同平台的解析函数返回相同格式数据;

    • 优先级适配:优先适配数据量全、反爬宽松的平台(如盖德化工网),再扩展其他平台。

    • 问题:不同化工网页面结构差异大(如盖德化工网用静态 HTML,摩贝网用动态 JSON 接口),相同字段的选择器 / JSON 路径不同,难以统一对接。

    • 解决方案

六、最佳实践与合规要点

  1. 系统架构设计采用 “多平台适配 + 分布式采集” 架构,适配化工行业特性:
    • 配置层:存储各化工网的解析规则(基础 URL、选择器、参数映射)、代理池配置、Cookie 池配置;

    • 调度层:使用 Celery 分布式任务调度,按平台、关键词、地区分组调度,控制单任务并发数≤1;

    • 采集层:每个采集节点绑定独立代理和 Cookie,模拟真实用户行为路径,支持静态 HTML 和动态 API 两种采集模式;

    • 解析层:分平台解析 + 统一数据结构化,核心字段(CAS 号、纯度、价格)强制校验,非核心字段可选填;

    • 存储层:用 MySQL 存储结构化数据(商品信息、供应商信息),MongoDB 存储非结构化数据(MSDS 报告 URL、商品图片),Redis 缓存搜索结果(1 小时过期);

    • 监控层:实时监控响应状态码、数据完整度、代理存活率,异常时自动切换代理 / Cookie 并告警。

  2. 性能优化策略
    • 异步批量采集:使用aiohttp并发处理多关键词搜索(如同时搜索 “无水乙醇”“聚丙烯”“硫酸”),控制并发数≤3;

    • 按需采集:仅对核心商品(工厂供应商、现货、价格符合预期)调用item_get接口获取完整信息,非核心商品仅保留列表页基础数据;

    • 缓存复用:对相同关键词 + 筛选条件的搜索请求,30 分钟内直接返回缓存结果,减少重复请求;

    • 增量更新:定期(如每日凌晨)更新热门化工产品数据,非热门产品每周更新一次,降低服务器压力。

  3. 合规性与风险控制
    • 访问合规:遵守化工网robots.txt协议,不爬取禁止访问的路径(如用户中心、支付页面);单 IP / 账号请求频率控制在合理范围(模拟真实采购行为);

    • 数据使用边界:采集数据仅用于企业内部采购决策、供应链管理、市场调研,不得用于恶意竞价、虚假宣传、数据转售等商业用途;

    • 危化品合规:采集和使用危化品数据时,需遵守《危险化学品安全管理条例》,不泄露危化品经营许可证、MSDS 报告等敏感信息;

    • 法律风险:尊重化工网平台规则和供应商知识产权,不擅自公开商品价格、联系方式、产能等商业信息;使用企业账号登录,避免使用个人账号大量请求(易被封禁)。

七、总结

化工网item_search接口的对接核心在于化工行业数据的标准化解析危化品合规信息的精准提取多平台反爬机制的深度适配。开发者需重点关注:
  1. 核心参数(CAS 号、纯度、包装规格)的通用化清洗逻辑(确保数据一致性);

  2. 动态接口签名参数的逆向与适配(确保接口请求有效);

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

  4. 危化品合规信息的提取与校验(满足化工行业采购合规要求)。

通过本文的技术方案,可构建稳定的化工商品搜索系统,为化工采购、供应链管理、市场行情监控等场景提供可靠数据支持。实际应用中,需根据目标化工网的具体结构动态调整解析规则和反爬策略,平衡数据获取效率与合规性。
需要进一步了解主流化工网解析规则配置表化工产品 CAS 号字典动态接口签名逆向细节,可以告诉我,我会补充相关内容


群贤毕至

访客