×

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

万邦科技Lex 万邦科技Lex 发表于2025-11-21 15:37:38 浏览17 评论0

抢沙发发表评论

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

化工网(如盖德化工网、摩贝网、中国化工网等垂直 B2B 平台)的 item_get 接口(非官方命名)是获取单款化工产品完整详情的核心入口,数据覆盖纯度、CAS 号、批量阶梯价、MSDS 报告、供应商资质、危化品合规证明等关键信息,对化工采购决策、供应链审核、产品合规校验等场景具有不可替代的价值。由于化工网无统一公开 API,需通过详情页解析 + 动态接口逆向实现对接,且需重点适配化工行业的强合规性、规格标准化等特性。本文系统讲解接口逻辑、技术实现、行业痛点解决方案,助你构建稳定的化工产品详情获取系统。

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

1. 核心功能

通过商品 ID(productId)或详情页 URL,获取化工产品全维度信息,核心字段聚焦化工行业 B2B 特性与合规要求:
字段分类核心字段示例行业意义
基础信息商品 ID、标题(含纯度 / CAS 号,如 “99.9% 无水乙醇 CAS64-17-5 工业级”)、主图 / 样品图、类目快速定位产品,区分同分异构体 / 不同规格产品
规格参数纯度(99.9%)、CAS 号(64-17-5)、分子式(C₂H₆O)、分子量(46.07)、执行标准(GB/T 678-2021)、外观(无色透明液体)产品选型核心依据,确保符合生产工艺要求
包装与运输包装规格(200kg / 桶、1000kg/IBC 桶)、运输方式(危化品专用车)、运输条件(避光 / 密封)采购成本核算(含包装费、运费)
价格信息基础单价(¥7500 / 吨)、批量阶梯价(1-5 吨 ¥7500,10 + 吨 ¥7200)、含税标识、结算方式(款到发货)批量采购比价,降低采购成本
供应信息现货库存(100 吨)、起订量(1 吨起订)、生产周期(期货 7 天交货)、供应范围(全国配送)保障生产连续性,避免物料短缺
供应商信息厂商名称、资质(危化品经营许可证、ISO9001 认证)、产能(年产 5 万吨)、联系方式供应商准入审核,降低合作风险
合规与安全危险类别(易燃液体)、UN 编号(1170)、MSDS 报告(下载 URL)、应急处理措施满足安全生产规范,规避合规风险

2. 典型应用场景

  • 精准采购决策:获取 “99.9% 无水乙醇” 的完整阶梯价,根据生产需求(20 吨 / 月)计算最优采购成本(20 吨 ×7200=144000 元);

  • 供应商合规审核:提取供应商的危化品经营许可证编号、MSDS 报告完整性,确保采购产品符合《危险化学品安全管理条例》;

  • 产品替代选型:对比 “聚丙烯 PP 均聚级” 不同供应商的熔指(2.0/3.0)、灰分(≤0.03%)等参数,选择适配注塑工艺的原料;

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

3. 接口特性

  • 强合规性:危化品资质、MSDS 报告、危险类别等字段为核心必填项,需重点提取;

  • 规格标准化:CAS 号、执行标准、纯度等参数遵循行业统一标准,结构化程度高;

  • 数据深度:详情页包含列表页未展示的完整阶梯价、MSDS 报告、检测报告等深度信息;

  • 反爬机制:比item_search更严格(如登录态强制验证、详情页访问频率限制、部分站点带sign签名);

  • 多站点差异:静态 HTML(盖德化工网)与动态 API(摩贝网)并存,但核心合规字段(CAS 号、资质)一致。

4. 主流化工网详情页特性对比

平台详情页类型核心数据获取难度反爬强度合规信息完整性
盖德化工网静态 HTML + 部分 AJAX中等高(资质 + MSDS)
摩贝网动态 API 渲染中(需破解签名)较高极高(含检测报告)
化工网(hc360)纯静态 HTML极低中(仅基础资质)
中国化工网AJAX 动态加载中等高(危化品资质严格)

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

1. 开发环境

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

  • 核心库:

    • 网络请求:requests(同步)、aiohttp(异步批量获取)

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

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

    • 数据处理:re(正则提取纯度 / 价格)、json(解析动态接口)、pdfplumber(可选,解析 MSDS 报告 PDF)

2. 详情页 URL 结构

典型化工网商品详情页 URL 格式(商品 ID 为核心标识):
平台URL 格式商品 ID 位置示例
盖德化工网https://www.guidechem.com/product/{productId}.html末尾数字(如 1234567)https://www.guidechem.com/product/1234567.html
摩贝网https://www.molbase.com/product/{productId}/detail.html中间数字(如 87654321)https://www.molbase.com/product/87654321/detail.html
化工网(hc360)https://b2b.hc360.com/supplyself/{productId}.html末尾数字(如 987654)https://b2b.hc360.com/supplyself/987654.html

3. 前置准备要点

  • 登录态 Cookie:必须获取企业账号登录后的 Cookie(含useridsessionIdenterpriseId等字段),否则无法查看完整价格和资质;

  • 代理池搭建:需高匿动态代理(支持 IP 切换),单 IP 单日访问详情页≤20 次,避免封禁;

  • 核心映射表

    • 危化品类别映射(如 “易燃液体”→“3 类”);

    • 执行标准映射(如 “GB/T 678-2021”→“工业无水乙醇标准”);

    • 包装规格单位映射(如 “IBC 桶”→“1000kg / 桶”);

  • MSDS 报告处理:部分站点提供 MSDS 下载 URL(需登录态),需提前测试下载权限。

4. 页面结构分析(以盖德化工网为例)

通过浏览器开发者工具(F12)分析详情页核心数据位置:
  • 静态数据:标题、主图、基础规格(纯度、CAS 号)、供应商名称→嵌入主 HTML;

  • 动态数据:完整阶梯价、库存、MSDS 报告→通过 AJAX 接口加载(如https://www.guidechem.com/api/product/detail?pid={productId});

  • 合规信息:危化品资质、经营许可证→<div class="qualification-list">

  • 阶梯价<div class="ladder-price-table">(静态)或动态接口返回;

  • MSDS 报告<a class="msds-download" href="xxx.pdf">(需登录态)。

三、接口调用流程(静态 HTML + 动态 API)

以 “获取盖德化工网 1234567 号商品(99.9% 无水乙醇)详情” 为例,核心流程为URL 构建→详情页请求→静态数据解析→动态接口补充→合规信息提取→数据整合

1. URL 构建与请求头伪装

  • 目标 URL:https://www.guidechem.com/product/{productId}.html(替换productId=1234567);

  • 请求头:携带登录态 Cookie、随机 UA、真实 Referer(搜索页 / 类目页):

    python
    运行
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0 Safari/537.36",
        "Referer": "https://www.guidechem.com/search/?keyword=无水乙醇",
        "Cookie": "userid=xxx; sessionId=xxx; enterpriseId=xxx; loginType=enterprise"  # 企业账号Cookie}

2. 静态数据解析(基础信息 + 规格参数)

从主 HTML 提取核心静态字段,重点关注标准化参数:
python
运行
from bs4 import BeautifulSoupimport re# 主页面请求response = requests.get(url, headers=headers, proxies=proxy, timeout=10)soup = BeautifulSoup(response.text, "lxml")# 1. 基础信息base_info = {
    "product_id": productId,
    "title": soup.select_one("h1.product-title")?.text.strip() or "",
    "main_image": soup.select_one("div.main-img img")?.get("src") or "",
    "category": soup.select_one("div.breadcrumb a:last-of-type")?.text.strip() or "",
    "url": url}# 2. 核心规格参数(纯度、CAS号、执行标准)specs = {}# 从规格表提取(<table class="spec-table">)spec_table = soup.select_one("table.spec-table")if spec_table:
    for row in spec_table.select("tr"):
        th = row.select_one("th")?.text.strip()
        td = row.select_one("td")?.text.strip()
        if th and td:
            if "纯度" in th or "含量" in th:
                specs["purity"] = re.search(r"\d+\.?\d*", td).group() if re.search(r"\d+\.?\d*", td) else ""
                specs["purity_str"] = td            elif "CAS" in th:
                specs["cas"] = td.strip()
            elif "执行标准" in th:
                specs["standard"] = td.strip()
            elif "分子式" in th:
                specs["molecular_formula"] = td.strip()# 3. 供应商基础信息seller_info = {
    "name": soup.select_one("div.seller-name")?.text.strip() or "",
    "area": soup.select_one("div.seller-area")?.text.strip() or "",
    "type": "工厂" if "源头工厂" in soup.select_one("div.seller-tag")?.text else "贸易商"}

3. 动态接口补充(完整阶梯价 + 库存)

详情页核心数据(阶梯价、实时库存)通过 AJAX 接口加载,需提取接口 URL 并携带商品 ID:
  • 动态接口示例(盖德化工网):https://www.guidechem.com/api/product/detail?pid={productId}&t={timestamp}

  • 请求与解析代码:

    python
    运行
    import timeimport json# 动态接口请求timestamp = int(time.time() * 1000)api_url = f"https://www.guidechem.com/api/product/detail?pid={productId}&t={timestamp}"api_response = requests.get(api_url, headers=headers, proxies=proxy, timeout=10)api_data = json.loads(api_response.text)# 解析阶梯价(结构化为采购量-价格映射)ladder_prices = []if api_data.get("data", {}).get("ladderPrices"):
        for item in api_data["data"]["ladderPrices"]:
            ladder_prices.append({
                "min_quantity": item.get("minNum", 0),
                "max_quantity": item.get("maxNum", 99999),
                "price": item.get("price", 0),
                "unit": item.get("unit", "元/吨")
            })# 解析实时库存stock_info = {
        "total_stock": api_data.get("data", {}).get("stock", 0),
        "stock_unit": "吨",
        "is_in_stock": api_data.get("data", {}).get("isInStock", False)}

4. 合规信息提取(资质 + MSDS + 危险类别)

化工行业核心需求,需从静态 HTML 和动态接口联合提取:
python
运行
# 1. 供应商资质(危化品经营许可证、ISO认证)qualifications = []qual_list = soup.select("div.qualification-list .qual-item")for qual in qual_list:
    qual_name = qual.select_one(".qual-name")?.text.strip()
    qual_no = qual.select_one(".qual-no")?.text.strip()  # 许可证编号
    if qual_name:
        qualifications.append({
            "name": qual_name,
            "license_no": qual_no or ""
        })# 2. MSDS报告(下载URL)msds_info = {
    "has_msds": False,
    "download_url": ""}msds_a = soup.select_one("a.msds-download")if msds_a and msds_a.get("href"):
    msds_info["has_msds"] = True
    msds_info["download_url"] = msds_a.get("href")# 3. 危险类别与UN编号(从安全信息栏提取)safety_info = {
    "hazard_class": soup.select_one("div.hazard-class")?.text.strip() or "",
    "un_number": soup.select_one("div.un-number")?.text.strip() or "",
    "safety_notice": soup.select_one("div.safety-notice")?.text.strip() or ""}

5. 数据整合(标准化输出)

将静态数据、动态数据、合规信息整合为统一格式,便于后续使用:
python
运行
# 最终标准化数据final_data = {
    "base_info": base_info,
    "specs": specs,
    "price_info": {
        "base_price": api_data.get("data", {}).get("basePrice", 0),
        "base_unit": "元/吨",
        "ladder_prices": ladder_prices,
        "is_tax_included": api_data.get("data", {}).get("isTaxIncluded", False)
    },
    "supply_info": {
        "stock_info": stock_info,
        "min_order": api_data.get("data", {}).get("minOrder", 1),
        "delivery_time": api_data.get("data", {}).get("deliveryTime", "7天内"),
        "transport_method": soup.select_one("div.transport-method")?.text.strip() or "危化品专用车"
    },
    "seller_info": {
        **seller_info,
        "qualifications": qualifications,
        "capacity": soup.select_one("div.capacity")?.text.strip() or ""  # 产能
    },
    "safety_compliance": {
        **safety_info,
        **msds_info    },
    "update_time": time.strftime("%Y-%m-%d %H:%M:%S")}

四、代码实现示例(Python)

以下是化工网item_get接口完整实现(适配盖德化工网,支持静态 HTML + 动态 API 联合解析),包含反爬处理、合规信息提取、阶梯价结构化:
import requests
import time
import random
import re
import json
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
from typing import List, Dict, Tuple

class ChemicalItemGetApi:
    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": {
                "detail_url": "https://www.guidechem.com/product/{product_id}.html",
                "api_url": "https://www.guidechem.com/api/product/detail",
                "spec_table_selector": "table.spec-table",
                "qualification_selector": "div.qualification-list .qual-item"
            },
            "molbase": {
                "detail_url": "https://www.molbase.com/product/{product_id}/detail.html",
                "api_url": "https://www.molbase.com/api/v1/product/detail",
                "spec_table_selector": ".spec-item-list",
                "qualification_selector": ".cert-list .cert-item"
            }
        }
        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": "https://www.guidechem.com/search/",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "X-Requested-With": "XMLHttpRequest",
            "Sec-Fetch-Site": "same-origin",
            "Sec-Fetch-Mode": "navigate"
        }
        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 _parse_specs(self, soup) -> Dict:
        """解析核心规格参数(纯度、CAS号、执行标准等)"""
        specs = {
            "purity": "", "purity_str": "", "cas": "", "standard": "",
            "molecular_formula": "", "appearance": "", "package": ""
        }
        spec_table = soup.select_one(self.config["spec_table_selector"])
        if not spec_table:
            return specs

        for row in spec_table.select("tr"):
            th = row.select_one("th")?.text.strip().lower()
            td = row.select_one("td")?.text.strip()
            if not th or not td:
                continue
            # 适配不同平台的参数名称(如“含量”=“纯度”)
            if any(keyword in th for keyword in ["纯度", "含量", "纯度等级"]):
                specs["purity_str"] = td
                purity_match = re.search(r"\d+\.?\d*", td)
                specs["purity"] = purity_match.group() if purity_match else ""
            elif "cas" in th:
                specs["cas"] = td.replace("-", "").strip()  # 统一CAS号格式(去除空格)
            elif any(keyword in th for keyword in ["执行标准", "标准"]):
                specs["standard"] = td
            elif "分子式" in th:
                specs["molecular_formula"] = td
            elif "外观" in th:
                specs["appearance"] = td
            elif any(keyword in th for keyword in ["包装", "规格"]):
                specs["package"] = td

        return specs

    def _parse_ladder_prices(self, api_data: Dict) -> List[Dict]:
        """解析批量阶梯价(结构化)"""
        ladder_prices = []
        if self.platform == "guidechem":
            ladder_data = api_data.get("data", {}).get("ladderPrices", [])
            for item in ladder_data:
                ladder_prices.append({
                    "min_quantity": int(item.get("minNum", 0)),
                    "max_quantity": int(item.get("maxNum", 99999)),
                    "price": float(item.get("price", 0)),
                    "unit": item.get("unit", "元/吨")
                })
        elif self.platform == "molbase":
            ladder_data = api_data.get("data", {}).get("priceLadder", [])
            for item in ladder_data:
                ladder_prices.append({
                    "min_quantity": int(item.get("quantity", 0)),
                    "max_quantity": 99999,
                    "price": float(item.get("price", 0)),
                    "unit": item.get("unit", "元/kg")
                })
        # 按采购量升序排序
        ladder_prices.sort(key=lambda x: x["min_quantity"])
        return ladder_prices

    def _parse_qualifications(self, soup) -> List[Dict]:
        """解析供应商资质(危化品许可证、ISO认证等)"""
        qualifications = []
        qual_items = soup.select(self.config["qualification_selector"])
        for item in qual_items:
            qual_name = item.select_one(".qual-name, .cert-name")?.text.strip()
            qual_no = item.select_one(".qual-no, .cert-no")?.text.strip() or ""
            qual_expire = item.select_one(".qual-expire, .cert-expire")?.text.strip() or ""
            if qual_name:
                qualifications.append({
                    "name": qual_name,
                    "license_no": qual_no,
                    "expire_date": qual_expire
                })
        return qualifications

    def _parse_safety_compliance(self, soup) -> Dict:
        """解析合规安全信息(MSDS、危险类别、UN编号)"""
        safety_info = {
            "hazard_class": "",
            "un_number": "",
            "msds": {"has_msds": False, "download_url": ""},
            "safety_notice": ""
        }

        # 危险类别
        hazard_class = soup.select_one("div.hazard-class, .hazard-type")?.text.strip()
        if hazard_class:
            safety_info["hazard_class"] = hazard_class

        # UN编号
        un_number = soup.select_one("div.un-number, .un-code")?.text.strip()
        if un_number:
            safety_info["un_number"] = un_number

        # MSDS报告
        msds_a = soup.select_one("a.msds-download, .msds-link")
        if msds_a and msds_a.get("href"):
            msds_url = msds_a.get("href")
            # 补全相对URL
            if not msds_url.startswith("http"):
                msds_url = f"https://www.guidechem.com{msds_url}" if self.platform == "guidechem" else f"https://www.molbase.com{msds_url}"
            safety_info["msds"] = {
                "has_msds": True,
                "download_url": msds_url
            }

        # 安全说明
        safety_notice = soup.select_one("div.safety-notice, .safety-desc")?.text.strip()
        if safety_notice:
            safety_info["safety_notice"] = safety_notice

        return safety_info

    def _fetch_api_data(self, product_id: str, headers: Dict, proxy: Dict) -> Dict:
        """获取动态接口数据(阶梯价、库存)"""
        api_data = {}
        timestamp = int(time.time() * 1000)
        try:
            if self.platform == "guidechem":
                api_params = {"pid": product_id, "t": timestamp}
                response = requests.get(
                    self.config["api_url"],
                    params=api_params,
                    headers=headers,
                    proxies=proxy,
                    timeout=15
                )
                api_data = response.json()
            elif self.platform == "molbase":
                # 摩贝网需签名参数(简化版,真实场景需逆向JS)
                sign = self._generate_molbase_sign(product_id, timestamp)
                api_params = {"productId": product_id, "timestamp": timestamp, "sign": sign}
                response = requests.get(
                    self.config["api_url"],
                    params=api_params,
                    headers=headers,
                    proxies=proxy,
                    timeout=15
                )
                api_data = response.json()
        except Exception as e:
            print(f"动态接口请求失败:{str(e)}")
        return api_data

    def _generate_molbase_sign(self, product_id: str, timestamp: int) -> str:
        """生成摩贝网签名(简化版,真实需逆向JS)"""
        import hashlib
        secret = "molbase_product_detail_secret"  # 需从摩贝网JS提取真实密钥
        sign_str = f"{product_id}{timestamp}{secret}"
        return hashlib.md5(sign_str.encode()).hexdigest().upper()

    def item_get(self, product_id: str) -> Dict:
        """
        获取化工网商品详情
        :param product_id: 商品ID(如盖德化工网1234567)
        :return: 标准化商品详情数据
        """
        try:
            # 1. 构建详情页URL
            detail_url = self.config["detail_url"].format(product_id=product_id)
            headers = self._get_headers()
            proxy = self._get_proxy()

            # 2. 随机延迟(避免反爬)
            time.sleep(random.uniform(5, 8))

            # 3. 请求详情页主HTML
            response = requests.get(
                url=detail_url,
                headers=headers,
                proxies=proxy,
                timeout=15
            )
            response.raise_for_status()  # 抛出HTTP错误(403/404等)
            soup = BeautifulSoup(response.text, "lxml")

            # 4. 验证商品是否存在(标题为空则视为不存在)
            title = soup.select_one("h1.product-title")?.text.strip() or ""
            if not title:
                return {"success": False, "error_msg": "商品不存在或已下架", "code": 404}

            # 5. 解析静态数据
            base_info = {
                "product_id": product_id,
                "title": title,
                "url": detail_url,
                "main_image": soup.select_one("div.main-img img")?.get("src") or "",
                "category": soup.select_one("div.breadcrumb a:last-of-type")?.text.strip() or "",
                "update_time": time.strftime("%Y-%m-%d %H:%M:%S")
            }

            specs = self._parse_specs(soup)

            seller_info = {
                "name": soup.select_one("div.seller-name, .merchant-name")?.text.strip() or "",
                "area": soup.select_one("div.seller-area, .merchant-location")?.text.strip() or "",
                "type": "工厂" if any(tag in (soup.select_one("div.seller-tag")?.text or "") for tag in ["源头工厂", "生产厂家"]) else "贸易商",
                "qualifications": self._parse_qualifications(soup),
                "capacity": soup.select_one("div.capacity, .production-capacity")?.text.strip() or ""
            }

            safety_compliance = self._parse_safety_compliance(soup)

            # 6. 请求并解析动态接口数据(阶梯价、库存)
            api_data = self._fetch_api_data(product_id, headers, proxy)
            ladder_prices = self._parse_ladder_prices(api_data)

            # 库存信息
            stock_info = {
                "total_stock": 0,
                "stock_unit": "吨",
                "is_in_stock": False
            }
            if self.platform == "guidechem":
                stock_info["total_stock"] = int(api_data.get("data", {}).get("stock", 0))
                stock_info["is_in_stock"] = api_data.get("data", {}).get("isInStock", False)
                stock_info["stock_unit"] = "吨" if specs.get("package") and "吨" in specs["package"] else "kg"
            elif self.platform == "molbase":
                stock_info["total_stock"] = int(api_data.get("data", {}).get("stockQuantity", 0))
                stock_info["is_in_stock"] = stock_info["total_stock"] > 0
                stock_info["stock_unit"] = "kg"

            # 价格信息整合
            price_info = {
                "base_price": float(api_data.get("data", {}).get("basePrice", 0)) or float(re.search(r"\d+\.?\d*", soup.select_one(".base-price")?.text or "").group() if re.search(r"\d+\.?\d*", soup.select_one(".base-price")?.text or "") else 0),
                "base_unit": "元/吨" if stock_info["stock_unit"] == "吨" else "元/kg",
                "ladder_prices": ladder_prices,
                "is_tax_included": "含税" in (soup.select_one(".tax-tag")?.text or "") or api_data.get("data", {}).get("isTaxIncluded", False)
            }

            # 供应信息
            supply_info = {
                "stock_info": stock_info,
                "min_order": int(api_data.get("data", {}).get("minOrder", 1)) or int(re.search(r"\d+", soup.select_one(".min-order")?.text or "1").group()),
                "delivery_time": api_data.get("data", {}).get("deliveryTime", "") or soup.select_one(".delivery-time")?.text.strip() or "7天内",
                "transport_method": soup.select_one("div.transport-method, .transport-type")?.text.strip() or "危化品专用车",
                "supply_range": soup.select_one("div.supply-range")?.text.strip() or "全国配送"
            }

            # 7. 整合最终数据
            final_data = {
                "success": True,
                "data": {
                    "base_info": base_info,
                    "specs": specs,
                    "price_info": price_info,
                    "supply_info": supply_info,
                    "seller_info": seller_info,
                    "safety_compliance": safety_compliance
                }
            }

            return final_data

        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}
            if "404" in str(e):
                return {"success": False, "error_msg": "商品不存在", "code": 404}
            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; loginType=enterprise; ..."  # 企业账号Cookie
    PRODUCT_ID = "1234567"  # 盖德化工网商品ID(99.9%无水乙醇)

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

    # 获取商品详情
    result = item_api.item_get(product_id=PRODUCT_ID)

    # 结果输出
    if result["success"]:
        data = result["data"]
        print("=" * 80)
        print(f"商品标题:{data['base_info']['title']}")
        print(f"商品ID:{data['base_info']['product_id']} | 类目:{data['base_info']['category']}")
        print(f"详情页:{data['base_info']['url']}")
        print("-" * 80)
        print("核心规格:")
        print(f"  CAS号:{data['specs']['cas']} | 纯度:{data['specs']['purity']}% | 执行标准:{data['specs']['standard']}")
        print(f"  分子式:{data['specs']['molecular_formula']} | 外观:{data['specs']['appearance']}")
        print(f"  包装规格:{data['specs']['package']}")
        print("-" * 80)
        print("价格信息:")
        print(f"  基础单价:¥{data['price_info']['base_price']}/{data['price_info']['base_unit']} | {'含税' if data['price_info']['is_tax_included'] else '不含税'}")
        if data['price_info']['ladder_prices']:
            print("  批量阶梯价:")
            for ladder in data['price_info']['ladder_prices']:
                max_qty = "∞" if ladder['max_quantity'] == 99999 else ladder['max_quantity']
                print(f"    {ladder['min_quantity']}-{max_qty}{ladder['unit'].split('/')[-1]}:¥{ladder['price']}/{ladder['unit']}")
        print("-" * 80)
        print("供应信息:")
        print(f"  库存:{'现货' if data['supply_info']['stock_info']['is_in_stock'] else '期货'} | 总量:{data['supply_info']['stock_info']['total_stock']}{data['supply_info']['stock_info']['stock_unit']}")
        print(f"  起订量:{data['supply_info']['min_order']}{data['price_info']['base_unit'].split('/')[-1]} | 交货期:{data['supply_info']['delivery_time']}")
        print(f"  运输方式:{data['supply_info']['transport_method']} | 供应范围:{data['supply_info']['supply_range']}")
        print("-" * 80)
        print("供应商信息:")
        print(f"  名称:{data['seller_info']['name']} | 类型:{data['seller_info']['type']} | 产地:{data['seller_info']['area']}")
        print(f"  产能:{data['seller_info']['capacity']}")
        if data['seller_info']['qualifications']:
            print("  资质:")
            for qual in data['seller_info']['qualifications'][:3]:  # 显示前3个资质
                print(f"    - {qual['name']}(许可证号:{qual['license_no'] or '无'})")
        print("-" * 80)
        print("合规安全信息:")
        print(f"  危险类别:{data['safety_compliance']['hazard_class']} | UN编号:{data['safety_compliance']['un_number']}")
        print(f"  MSDS报告:{'有' if data['safety_compliance']['msds']['has_msds'] else '无'}")
        if data['safety_compliance']['msds']['has_msds']:
            print(f"  MSDS下载:{data['safety_compliance']['msds']['download_url']}")
        print(f"  安全说明:{data['safety_compliance']['safety_notice'][:100]}...")
        print("=" * 80)
    else:
        print(f"获取失败:{result['error_msg']}(错误码:{result.get('code')})")

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

1. 化工规格参数的标准化解析

  • 问题:不同平台参数命名不一致(如 “含量”=“纯度”)、格式多样(如纯度 “99.9%”“≥99.9%”“999‰”),难以统一结构化;

  • 解决方案

    • 关键词模糊匹配:通过 “纯度 / 含量 / 纯度等级” 等关键词统一映射为purity字段;

    • 格式归一化:用正则提取数值(如 “999‰”→“99.9%”),保留原始字符串(purity_str)供校验;

    • 核心参数强制校验:CAS 号、纯度、执行标准为必填项,缺失则标记为 “数据不完整”。

2. 动态接口签名参数破解(摩贝网等平台)

  • 问题:部分平台(如摩贝网)的详情页动态 API 需携带sign参数(基于商品 ID、时间戳、密钥的加密结果),直接请求返回 403;

  • 解决方案

    • JS 逆向:通过浏览器开发者工具(Sources→搜索sign)找到生成逻辑(如sign = md5(productId + timestamp + secret));

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

    • 定期更新:加密逻辑可能每月更新,需监控接口响应状态,及时调整签名生成代码。

3. 合规信息的完整提取

  • 问题:危化品资质、MSDS 报告分散在页面不同位置(如资质在供应商信息栏、MSDS 在安全信息栏),部分需登录后才能查看;

  • 解决方案

    • 多位置匹配:针对不同平台预设合规信息选择器(如盖德化工网div.qualification-list、摩贝网.cert-list);

    • MSDS 权限处理:确保 Cookie 包含下载权限(企业账号登录后通常可直接下载),提取 URL 后异步下载 PDF 归档;

    • 资质关键词库:维护化工行业核心资质关键词(如 “危化品经营许可证”“安全生产许可证”“ISO9001”),避免遗漏关键合规证明。

4. 反爬机制深度对抗

  • 问题:详情页反爬比搜索页更严格(单 IP 单日访问≤20 次、登录态强制验证、行为检测);

  • 解决方案

    • 代理策略:使用企业级高匿动态代理,单 IP 单日访问≤15 次,每次请求切换 IP;

    • 登录态管理:维护多个企业账号 Cookie 池,随机轮换使用,每个 Cookie 单日访问≤10 次;

    • 行为模拟:严格模拟真实用户路径(搜索页→列表页→详情页),请求间隔 5-8 秒,禁用Connection: close头;

    • 异常重试:触发 403 错误时,切换代理 + Cookie,暂停 10 分钟后重试,重试≤3 次。

5. 阶梯价结构化与采购成本计算

  • 问题:不同平台阶梯价格式差异大(如盖德化工网分 “minNum/maxNum”,摩贝网仅 “quantity”),需适配多格式并支持成本计算;

  • 解决方案

    • 统一结构化:将阶梯价转换为 “min_quantity/max_quantity/price/unit” 标准格式,无上限则设为 99999;

    • 成本计算工具:新增calculate_cost(quantity)方法,根据采购量自动匹配对应阶梯价(如采购 20 吨→匹配 “10 + 吨” 价格);

    • 示例代码:

      python
      运行
      def calculate_cost(self, quantity: int) -> Tuple[float, str]:
          """根据采购量计算成本"""
          for ladder in self.data["price_info"]["ladder_prices"]:
              if ladder["min_quantity"] ≤ quantity ≤ ladder["max_quantity"]:
                  return quantity * ladder["price"], ladder["unit"]
          # 超最大阶梯价按最高档计算
          if self.data["price_info"]["ladder_prices"]:
              max_ladder = self.data["price_info"]["ladder_prices"][-1]
              return quantity * max_ladder["price"], max_ladder["unit"]
          return quantity * self.data["price_info"]["base_price"], self.data["price_info"]["base_unit"]

6. 多平台适配

  • 问题:静态 HTML(盖德化工网)与动态 API(摩贝网)页面结构差异大,相同字段选择器不同;

  • 解决方案

    • 配置化设计:将不同平台的 URL 格式、选择器、接口参数封装为配置字典,新增平台仅需补充配置;

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

    • 优先级适配:优先适配数据完整、反爬宽松的平台(如盖德化工网),再扩展摩贝网等复杂平台。

六、最佳实践与合规要点

1. 系统架构设计

采用 “分布式采集 + 分层解析 + 合规校验” 架构,适配化工行业特性:
  • 采集层:多节点分布式部署,每个节点绑定独立代理和 Cookie,模拟真实用户路径;

  • 解析层:分静态解析(基础信息 + 规格)、动态解析(阶梯价 + 库存)、合规解析(资质 + MSDS)三层,确保数据完整;

  • 校验层:对核心字段(CAS 号、纯度、资质)进行合规校验,缺失则标记为 “异常商品”;

  • 存储层:MySQL 存储结构化数据(商品信息、供应商、价格),MongoDB 存储非结构化数据(MSDS PDF、检测报告),Redis 缓存热点商品(1 小时过期);

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

2. 性能优化策略

  • 异步批量获取:使用aiohttp并发处理多商品 ID(如同时获取 10 个商品详情),控制并发数≤3;

  • 按需采集:仅对重点商品(工厂供应商、现货、合规齐全)获取完整详情,非重点商品仅提取基础信息;

  • 缓存复用:热点商品(如每日访问≥10 次)缓存 1 小时,避免重复请求;

  • 增量更新:价格、库存等动态字段每 2 小时更新,规格、资质等静态字段每日更新。

3. 合规性与风险控制

  • 访问合规:遵守化工网robots.txt协议,不爬取用户中心、支付页面等敏感路径;单 IP / 账号请求频率控制在真实采购行为范围内;

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

  • 危化品数据保护:MSDS 报告、危化品经营许可证编号等敏感信息需加密存储,不得擅自公开;

  • 法律风险:使用企业账号登录,避免个人账号大量请求(易被封禁);尊重平台规则和供应商知识产权,不泄露商业机密(如产能、联系方式)。

七、总结

化工网item_get接口的对接核心在于化工行业数据的标准化解析危化品合规信息的完整提取严格反爬机制的深度适配。开发者需重点关注:
  1. 核心规格参数(CAS 号、纯度)的标准化清洗,确保数据一致性;

  2. 动态接口签名的逆向与适配,获取完整阶梯价和库存;

  3. 合规信息(资质、MSDS)的多位置提取,满足化工行业采购合规要求;

  4. 代理池与 Cookie 池的协同管理,应对严格反爬。

通过本文的技术方案,可构建稳定的化工商品详情获取系统,为化工采购、供应链审核、市场监控等场景提供可靠数据支持。实际应用中,需根据目标平台的结构更新动态调整解析规则,平衡数据获取效率与合规性。
需要进一步了解摩贝网签名逆向细节化工行业合规资质关键词库MSDS 报告 PDF 解析方法,可以告诉我,我会补充相关内容


群贤毕至

访客