×

电子元件 item_search - 按关键字搜索商品接口深度分析及 Python 实现

万邦科技Lex 万邦科技Lex 发表于2025-09-28 15:04:48 浏览209 评论0

抢沙发发表评论

        注册账号免费测试电子元件API数据接口

电子元件的搜索接口与普通消费品存在显著差异,其核心在于参数化筛选技术指标匹配。电子元件(如电阻、电容、芯片、传感器等)的搜索不仅需要匹配关键词,更需要精确筛选电气参数、物理特性、认证标准等专业维度。本文将针对电子元件的行业特性,深入分析 item_search 接口的设计逻辑、参数体系及 Python 实现方案。

一、电子元件 item_search 接口核心特性分析

电子元件的搜索需求具有强技术性、高精准度、多维度筛选特点,其 item_search 接口在基础电商搜索能力之上,必须满足工程师、采购人员对参数匹配的专业需求。

1. 接口定位与行业价值

电子元件 item_search 接口是连接 “采购需求” 与 “商品数据” 的桥梁,核心价值体现在:
  • 参数化精准匹配:支持按电气参数(如电阻值、容差、耐压)、物理参数(如封装尺寸、引脚数)筛选,避免 “关键词匹配但参数不符” 的无效结果;

  • 技术指标分层筛选:允许按精度等级、温度范围、可靠性等级等技术指标分层过滤,满足不同场景(消费电子 / 工业级 / 军工级)需求;

  • 供应链属性筛选:支持按库存批次、最小起订量(MOQ)、交期、认证标准(RoHS/CE)等供应链属性筛选,直接服务采购决策;

  • 替代型号推荐:针对停产型号,返回兼容替代型号,解决电子元件 “型号淘汰快” 的行业痛点;

  • 数据标准化输出:将不同厂商的参数命名统一化(如 “封装” 统一对应 “package”),便于系统集成和批量分析。

2. 接口权限与调用限制

电子元件属于工业品类,其搜索接口权限区分更细致,需匹配不同用户的采购规模和技术需求:
限制类型个人级(DIY / 维修)企业级(中小批量)工业级(大批量 / 供应链)
权限申请实名认证企业资质审核供应链资质 + 采购证明
调用频率3 次 / 分钟20 次 / 分钟60 次 / 分钟
参数筛选维度基础 10 项进阶 25 项全量 40 + 项
数据返回量100 条 / 关键词1000 条 / 关键词5000 条 / 关键词
替代型号推荐不支持基础支持全量支持(含引脚兼容分析)
价格数据精度零售价批发价 + 阶梯价协议价 + 库存成本分析
缓存时效30 分钟15 分钟5 分钟

3. 核心参数解析(含电子元件专属参数)

电子元件搜索接口的参数体系远复杂于普通商品,除基础关键词外,需重点关注技术参数筛选条件

必选参数

参数名类型说明示例
appKeyString应用唯一标识"elec_search_9527"
signString签名(按平台算法生成)"F8E7D6C5B4A3210FEDCBA9876"
timestampString时间戳(yyyyMMddHHmmss)"20240610091530"
keywordString搜索关键词(支持型号、规格、功能描述,如 “0402 10KΩ 电阻”)"STM32F103C8T6 单片机"

电子元件专属筛选参数(部分)

参数名类型说明示例
categoryIdString元件分类 ID(如电阻 = 101,电容 = 102,芯片 = 201)"201"(芯片类)
brandIdString品牌 ID(如 TI=1001,ST=1002,国巨 = 1003)"1002"(ST 品牌)
paramFilterString电气参数筛选(JSON 格式,支持多参数组合){"resistance":"10KΩ","tolerance":"±1%"}
physicalFilterString物理参数筛选(JSON 格式){"package":"0402","pinCount":"48"}
envFilterString环境参数筛选(JSON 格式){"operatingTemp":"-40~85℃"}
certFilterString认证筛选(逗号分隔)"RoHS,CE,AEC-Q100"
stockFilterString库存筛选(minQty: 最小库存,batchAvailable: 是否有批次){"minQty":1000,"batchAvailable":true}
priceFrom/priceToFloat价格区间(元 / 个)0.1 / 1.0
sortString排序方式(支持技术维度排序)"precision desc"(精度从高到低)

二、返回数据结构与行业专属字段解析

电子元件搜索接口返回数据需兼顾技术参数完整性采购决策相关性,其结构分为通用信息层、技术参数层、供应链层三个层级:

1. 数据结构总览

{
  "sn_responseContent": {
    "sn_body": {
      "searchResult": {
        "totalCount": 128,  // 总商品数
        "pageNo": 1,        // 当前页码
        "pageSize": 20,     // 每页条数
        "productList": [    // 商品列表
          {
            // 1. 通用信息层
            "productCode": "ELEC20240600159",
            "productName": "STMicroelectronics STM32F103C8T6 32位单片机",
            "brandName": "STMicroelectronics",
            "price": "18.50",  // 单价
            "stockStatus": "有库存",
            
            // 2. 技术参数层(电子元件核心)
            "techParams": {
              "baseParams": {
                "model": "STM32F103C8T6",  // 原厂型号
                "core": "ARM Cortex-M3",   // 内核
                "frequency": "72MHz",      // 主频
                "flash": "64KB",           // 闪存
                "ram": "20KB"              // 内存
              },
              "physicalParams": {
                "package": "LQFP48",       // 封装
                "pinCount": 48,            // 引脚数
                "dimensions": "7x7mm"      // 尺寸
              },
              "environmentalParams": {
                "operatingTemp": "-40℃ ~ 85℃",  // 工作温度
                "storageTemp": "-55℃ ~ 125℃"    // 存储温度
              },
              "certifications": ["RoHS", "CE", "REACH"]  // 认证
            },
            
            // 3. 供应链层
            "supplyChain": {
              "moq": 10,                  // 最小起订量
              "leadTime": "3-5天",        // 交期
              "batchCount": 3,            // 库存批次数量
              "supplierType": "授权分销商"  // 供应商类型
            },
            
            // 4. 替代型号信息
            "alternativeModels": [
              {"model": "STM32F103CBT6", "similarity": 95},  // 相似度95%
              {"model": "GD32F103C8T6", "similarity": 90}    // 国产替代
            ]
          },
          // 更多商品...
        ],
        // 筛选条件统计(用于前端筛选项展示)
        "filterStats": {
          "brandStats": [{"brandId": "1002", "brandName": "ST", "count": 35}, ...],
          "packageStats": [{"value": "LQFP48", "count": 28}, ...],
          "priceRangeStats": [{"range": "10-20", "count": 56}, ...]
        }
      }
    },
    "sn_head": {"returnCode": "0000", "returnMessage": "success"}
  }}

2. 核心字段解读(行业专属)

字段层级关键字段行业意义与应用场景
技术参数层model原厂型号(电子元件的唯一标识,如 “STM32F103C8T6” 与 “STM32F103CBT6” 仅一字之差但参数不同)
技术参数层package封装类型(直接决定物理兼容性,如 “LQFP48” 与 “TQFP48” 封装的芯片无法通用)
技术参数层operatingTemp工作温度(区分民用级(0~70℃)、工业级(-40~85℃)、军工级(-55~125℃),应用场景严格区分)
供应链层moq最小起订量(电子元件多为批量采购,如 MOQ=1000 表示至少采购 1000 个)
供应链层supplierType供应商类型(“原厂”“授权分销商”“贸易商” 影响正品保障,工业级采购优先选授权分销商)
替代型号信息alternativeModels解决型号停产问题(如 STM32 系列缺货时,推荐国产替代型号 GD32)

三、Python 实现方案(适配电子元件搜索特性)

针对电子元件的参数化搜索、技术指标筛选、批量数据分析需求,以下实现包含接口调用、参数校验、结果分析等功能,并特别优化了技术参数的解析与匹配逻辑:
import requests
import time
import hashlib
import json
import logging
import pandas as pd
import matplotlib.pyplot as plt
import re
from datetime import datetime
from typing import Dict, Optional, List, Tuple
from collections import defaultdict

# 配置日志(工业场景需详细记录搜索参数与结果,便于追溯)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - [电子元件搜索] - %(message)s",
    handlers=[logging.FileHandler("elec_search_api.log"), logging.StreamHandler()]
)

# 配置中文显示
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False

class ElecItemSearch:
    """电子元件 item_search 接口封装类,支持参数化搜索与技术指标分析"""
    
    def __init__(self, app_key: str, app_secret: str, auth_level: str = "enterprise"):
        """
        初始化电子元件搜索客户端
        :param app_key: 应用标识
        :param app_secret: 应用密钥
        :param auth_level: 权限等级(personal/enterprise/industrial)
        """
        self.app_key = app_key
        self.app_secret = app_secret
        self.auth_level = auth_level
        self.api_url = "https://open.elec-platform.com/api/http/elecSearch"
        
        # 频率控制(按权限等级)
        self.rate_limit = {
            "personal": 3,
            "enterprise": 20,
            "industrial": 60
        }.get(auth_level, 3)  # 次/分钟
        self.call_timestamps = []
        
        # 支持的技术参数筛选类型
        self.supported_params = {
            "electrical": ["resistance", "capacitance", "voltage", "current", "frequency"],
            "physical": ["package", "pinCount", "dimensions", "weight"],
            "environmental": ["operatingTemp", "storageTemp", "humidity"]
        }
        
        # 支持的排序方式
        self.supported_sorts = [
            "price asc", "price desc",          # 价格排序
            "stock desc",                       # 库存排序
            "precision desc",                   # 精度排序(电子元件专属)
            "popularity desc",                  #  popularity
            "updateTime desc"                   # 更新时间
        ]
    
    def _generate_sign(self, params: Dict) -> str:
        """生成签名(电子元件平台标准MD5签名)"""
        # 参数按ASCII升序排序
        sorted_params = sorted(params.items(), key=lambda x: x[0])
        # 拼接为key=value&key=value格式
        param_str = "&".join([f"{k}={v}" for k, v in sorted_params])
        # 拼接appSecret并加密
        sign_str = f"{param_str}&appSecret={self.app_secret}"
        md5 = hashlib.md5()
        md5.update(sign_str.encode('utf-8'))
        return md5.hexdigest().upper()
    
    def _check_rate_limit(self) -> None:
        """检查调用频率,确保不超限"""
        current_time = time.time()
        # 保留1分钟内的调用记录
        self.call_timestamps = [t for t in self.call_timestamps if current_time - t < 60]
        
        if len(self.call_timestamps) >= self.rate_limit:
            # 计算需要等待的时间
            oldest_time = self.call_timestamps[0]
            sleep_time = 60 - (current_time - oldest_time) + 0.2
            logging.warning(f"调用频率超限,等待 {sleep_time:.1f} 秒(权限等级:{self.auth_level})")
            time.sleep(sleep_time)
            # 清理过期记录
            self.call_timestamps = [t for t in self.call_timestamps if time.time() - t < 60]
        
        # 记录本次调用时间
        self.call_timestamps.append(current_time)
    
    def _validate_filters(self, filters: Dict) -> Tuple[bool, str]:
        """验证筛选参数的合法性"""
        # 验证排序方式
        if "sort" in filters and filters["sort"] not in self.supported_sorts:
            return False, f"不支持的排序方式: {filters['sort']},支持: {', '.join(self.supported_sorts)}"
        
        # 验证参数筛选格式
        for param_type in ["paramFilter", "physicalFilter", "envFilter"]:
            if param_type in filters and filters[param_type]:
                try:
                    # 检查是否为合法JSON
                    filter_json = json.loads(filters[param_type])
                    # 检查参数是否在支持的列表中
                    param_category = param_type.replace("Filter", "")
                    if param_category in self.supported_params:
                        for key in filter_json:
                            if key not in self.supported_params[param_category]:
                                return False, f"不支持的{param_category}参数: {key},支持: {', '.join(self.supported_params[param_category])}"
                except json.JSONDecodeError:
                    return False, f"{param_type}格式错误,需为JSON字符串"
        
        return True, "验证通过"
    
    def search_elec_items(self, keyword: str, 
                         page_no: int = 1, page_size: int = 20, 
                         filters: Optional[Dict] = None) -> Optional[Dict]:
        """
        搜索电子元件商品
        :param keyword: 搜索关键词(型号、规格、功能描述)
        :param page_no: 页码
        :param page_size: 每页数量(10-50)
        :param filters: 筛选条件(含技术参数筛选)
        :return: 搜索结果
        """
        # 基础参数验证
        if not (2 <= len(keyword) <= 50):
            logging.error(f"关键词长度必须在2-50之间,当前: {len(keyword)}")
            return None
        
        if not (10 <= page_size <= 50):
            logging.error(f"page_size必须在10-50之间,当前: {page_size}")
            return None
        
        # 构建基础参数
        base_params = {
            "appKey": self.app_key,
            "timestamp": datetime.now().strftime("%Y%m%d%H%M%S"),
            "method": "elec.product.search",
            "version": "v2.1",
            "keyword": keyword,
            "pageNo": str(page_no),
            "pageSize": str(page_size)
        }
        
        # 处理筛选参数
        if filters and isinstance(filters, Dict):
            # 验证筛选参数
            valid, msg = self._validate_filters(filters)
            if not valid:
                logging.error(f"筛选参数错误: {msg}")
                return None
            
            # 合并筛选参数(转换为字符串类型)
            for k, v in filters.items():
                if v is not None:
                    base_params[k] = json.dumps(v) if isinstance(v, (dict, list)) else str(v)
        
        # 生成签名
        sign = self._generate_sign(base_params)
        base_params["sign"] = sign
        
        # 检查频率限制
        self._check_rate_limit()
        
        try:
            # 发送请求
            response = requests.get(self.api_url, params=base_params, timeout=15)
            response.raise_for_status()
            
            # 解析响应
            result = response.json()
            
            # 处理错误响应
            if "sn_responseContent" not in result:
                logging.error(f"响应格式错误: {result}")
                return None
                
            response_content = result["sn_responseContent"]
            if "sn_error" in response_content:
                error = response_content["sn_error"]
                logging.error(f"API错误: {error.get('error_msg')} (错误码: {error.get('error_code')})")
                return None
                
            # 提取搜索结果
            search_data = response_content.get("sn_body", {}).get("searchResult", {})
            if not search_data.get("productList"):
                logging.info(f"关键词 '{keyword}' 未找到匹配商品")
                return None
                
            logging.info(f"搜索成功: 关键词 '{keyword}',第{page_no}页,共{search_data.get('totalCount')}个商品")
            return search_data
            
        except requests.exceptions.RequestException as e:
            logging.error(f"请求异常: {str(e)}")
            return None
        except json.JSONDecodeError:
            logging.error(f"响应解析失败: {response.text[:200]}...")
            return None
    
    def batch_search(self, keyword: str, max_pages: int = 5, 
                    page_size: int = 50, filters: Optional[Dict] = None) -> Tuple[List[Dict], Dict]:
        """
        批量获取多页搜索结果
        :param keyword: 搜索关键词
        :param max_pages: 最大页数
        :param page_size: 每页数量
        :param filters: 筛选条件
        :return: 商品列表和元信息
        """
        all_items = []
        meta_info = {
            "keyword": keyword,
            "total_items": 0,
            "total_pages": 0,
            "filters": filters,
            "fetch_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
        page = 1
        
        while page <= max_pages:
            logging.info(f"获取关键词 '{keyword}' 第 {page}/{max_pages} 页...")
            result = self.search_elec_items(keyword, page, page_size, filters)
            
            if not result:
                break
                
            # 提取商品数据
            items = result.get("productList", [])
            if not items:
                logging.info("当前页无数据,停止获取")
                break
                
            all_items.extend(items)
            
            # 记录元信息(第一页)
            if page == 1:
                meta_info["total_items"] = result.get("totalCount", 0)
                meta_info["total_pages"] = min(max_pages, 
                                              (meta_info["total_items"] + page_size - 1) // page_size)
                meta_info["filter_stats"] = result.get("filterStats", {})  # 筛选条件统计
                
            page += 1
        
        logging.info(f"批量搜索完成,共获取 {len(all_items)} 个商品")
        return all_items, meta_info
    
    def analyze_technical_params(self, items: List[Dict]) -> Dict:
        """分析搜索结果中的技术参数分布(电子元件核心分析)"""
        if not items:
            return {"error": "无商品数据可分析"}
        
        # 1. 封装类型分布
        package_dist = defaultdict(int)
        # 2. 品牌分布
        brand_dist = defaultdict(int)
        # 3. 工作温度范围分布(区分民用/工业/军工级)
        temp_grade_dist = defaultdict(int)
        temp_grade_map = {
            "民用级": lambda t: "0℃~70℃" in t,
            "工业级": lambda t: "-40℃~85℃" in t,
            "军工级": lambda t: "-55℃~125℃" in t,
            "其他": lambda t: True  # 匹配所有未命中的情况
        }
        # 4. 价格分布(按元件类型区分,避免不同类型价格不可比)
        price_by_category = defaultdict(list)
        # 5. 最小起订量分布
        moq_dist = defaultdict(int)
        
        for item in items:
            # 封装分布
            package = item.get("techParams", {}).get("physicalParams", {}).get("package", "未知")
            package_dist[package] += 1
            
            # 品牌分布
            brand = item.get("brandName", "未知品牌")
            brand_dist[brand] += 1
            
            # 工作温度等级分布
            operating_temp = item.get("techParams", {}).get("environmentalParams", {}).get("operatingTemp", "")
            for grade, check in temp_grade_map.items():
                if check(operating_temp):
                    temp_grade_dist[grade] += 1
                    break
            
            # 价格按类目分布
            category = item.get("categoryName", "未知类目")
            try:
                price = float(item.get("price", 0))
                price_by_category[category].append(price)
            except (ValueError, TypeError):
                pass
            
            # 最小起订量分布
            moq = item.get("supplyChain", {}).get("moq", 0)
            try:
                moq_val = int(moq)
                if moq_val <= 10:
                    moq_dist["1-10"] += 1
                elif moq_val <= 100:
                    moq_dist["11-100"] += 1
                elif moq_val <= 1000:
                    moq_dist["101-1000"] += 1
                else:
                    moq_dist[">1000"] += 1
            except (ValueError, TypeError):
                moq_dist["未知"] += 1
        
        # 计算价格统计值
        price_stats = {}
        for category, prices in price_by_category.items():
            if prices:
                price_stats[category] = {
                    "count": len(prices),
                    "min": round(min(prices), 2),
                    "max": round(max(prices), 2),
                    "avg": round(sum(prices)/len(prices), 2)
                }
        
        return {
            "package_distribution": dict(package_dist),
            "brand_distribution": dict(brand_dist),
            "temperature_grade_distribution": dict(temp_grade_dist),
            "price_statistics_by_category": price_stats,
            "moq_distribution": dict(moq_dist),
            "analysis_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }
    
    def find_alternatives(self, items: List[Dict], target_model: str) -> List[Dict]:
        """
        从搜索结果中查找目标型号的替代型号
        :param items: 商品列表
        :param target_model: 目标型号
        :return: 替代型号列表(按相似度排序)
        """
        alternatives = []
        
        for item in items:
            # 检查商品自身是否为目标型号
            current_model = item.get("techParams", {}).get("baseParams", {}).get("model", "")
            if current_model == target_model:
                # 提取该商品的替代型号推荐
                alt_models = item.get("alternativeModels", [])
                for alt in alt_models:
                    # 补充替代型号的商品信息
                    alt_info = {
                        "model": alt.get("model"),
                        "similarity": alt.get("similarity", 0),
                        "brand": item.get("brandName"),
                        "price": item.get("price"),
                        "package": item.get("techParams", {}).get("physicalParams", {}).get("package")
                    }
                    alternatives.append(alt_info)
        
        # 按相似度降序排序
        return sorted(alternatives, key=lambda x: x["similarity"], reverse=True)
    
    def visualize_analysis(self, tech_analysis: Dict, output_dir: str = ".") -> None:
        """可视化技术参数分析结果"""
        if "error" in tech_analysis:
            logging.warning(tech_analysis["error"])
            return
        
        # 1. 封装类型分布饼图
        if tech_analysis["package_distribution"]:
            plt.figure(figsize=(10, 8))
            package_data = tech_analysis["package_distribution"]
            # 只显示占比>3%的封装,其余归为"其他"
            others = 0
            filtered = {}
            total = sum(package_data.values())
            for pkg, count in package_data.items():
                ratio = count / total
                if ratio > 0.03:
                    filtered[pkg] = count
                else:
                    others += count
            if others > 0:
                filtered["其他"] = others
            
            labels = list(filtered.keys())
            sizes = list(filtered.values())
            
            plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
            plt.title('封装类型分布')
            plt.axis('equal')
            plt.tight_layout()
            plt.savefig(f"{output_dir}/elec_package_dist.png")
            plt.close()
            logging.info(f"封装分布图表已保存至 {output_dir}/elec_package_dist.png")
        
        # 2. 品牌分布条形图(前10)
        if tech_analysis["brand_distribution"]:
            plt.figure(figsize=(12, 6))
            brand_data = sorted(tech_analysis["brand_distribution"].items(), 
                              key=lambda x: x[1], reverse=True)[:10]
            brands, counts = zip(*brand_data)
            
            plt.bar(brands, counts, color='skyblue')
            plt.title('品牌分布(前10)')
            plt.xlabel('品牌')
            plt.ylabel('商品数量')
            plt.xticks(rotation=45)
            
            for i, v in enumerate(counts):
                plt.text(i, v + 0.5, str(v), ha='center')
            
            plt.tight_layout()
            plt.savefig(f"{output_dir}/elec_brand_dist.png")
            plt.close()
            logging.info(f"品牌分布图表已保存至 {output_dir}/elec_brand_dist.png")
        
        # 3. 工作温度等级分布饼图
        if tech_analysis["temperature_grade_distribution"]:
            plt.figure(figsize=(8, 8))
            temp_data = tech_analysis["temperature_grade_distribution"]
            
            labels = list(temp_data.keys())
            sizes = list(temp_data.values())
            
            plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
            plt.title('工作温度等级分布')
            plt.axis('equal')
            plt.tight_layout()
            plt.savefig(f"{output_dir}/elec_temp_grade_dist.png")
            plt.close()
            logging.info(f"温度等级分布图表已保存至 {output_dir}/elec_temp_grade_dist.png")
        
        # 4. 类目价格统计柱状图
        if tech_analysis["price_statistics_by_category"]:
            plt.figure(figsize=(12, 6))
            category_data = tech_analysis["price_statistics_by_category"]
            # 只显示有足够数据的类目
            valid_categories = {k: v for k, v in category_data.items() if v["count"] >= 5}
            if valid_categories:
                categories = list(valid_categories.keys())
                avg_prices = [v["avg"] for v in valid_categories.values()]
                
                plt.bar(categories, avg_prices, color='orange')
                plt.title('各类目平均价格')
                plt.xlabel('商品类目')
                plt.ylabel('平均价格(元)')
                plt.xticks(rotation=45)
                
                for i, v in enumerate(avg_prices):
                    plt.text(i, v + 0.1, f"¥{v}", ha='center')
                
                plt.tight_layout()
                plt.savefig(f"{output_dir}/elec_category_price.png")
                plt.close()
                logging.info(f"类目价格图表已保存至 {output_dir}/elec_category_price.png")
    
    def export_to_excel(self, items: List[Dict], tech_analysis: Dict, 
                       meta_info: Dict, filename: str) -> None:
        """导出搜索结果与分析数据到Excel"""
        if not items and not tech_analysis:
            logging.warning("无数据可导出")
            return
            
        try:
            with pd.ExcelWriter(filename) as writer:
                # 1. 搜索元信息
                pd.DataFrame([meta_info]).to_excel(writer, sheet_name='搜索信息', index=False)
                
                # 2. 商品列表(含技术参数)
                if items:
                    item_data = []
                    for item in items:
                        tech_params = item.get("techParams", {})
                        supply_chain = item.get("supplyChain", {})
                        
                        data = {
                            "商品ID": item.get("productCode"),
                            "商品名称": item.get("productName"),
                            "品牌": item.get("brandName"),
                            "型号": tech_params.get("baseParams", {}).get("model", ""),
                            "封装": tech_params.get("physicalParams", {}).get("package", ""),
                            "工作温度": tech_params.get("environmentalParams", {}).get("operatingTemp", ""),
                            "认证": ",".join(tech_params.get("certifications", [])),
                            "单价(元)": item.get("price"),
                            "最小起订量": supply_chain.get("moq", ""),
                            "交期": supply_chain.get("leadTime", ""),
                            "库存状态": item.get("stockStatus")
                        }
                        item_data.append(data)
                    
                    df_items = pd.DataFrame(item_data)
                    df_items.to_excel(writer, sheet_name='商品列表', index=False)
                
                # 3. 技术参数分析结果
                if tech_analysis and "error" not in tech_analysis:
                    # 封装分布
                    df_package = pd.DataFrame(
                        list(tech_analysis["package_distribution"].items()),
                        columns=["封装类型", "数量"]
                    )
                    df_package.to_excel(writer, sheet_name='封装分布', index=False)
                    
                    # 品牌分布
                    df_brand = pd.DataFrame(
                        list(tech_analysis["brand_distribution"].items()),
                        columns=["品牌", "数量"]
                    )
                    df_brand.to_excel(writer, sheet_name='品牌分布', index=False)
                    
                    # 温度等级分布
                    df_temp = pd.DataFrame(
                        list(tech_analysis["temperature_grade_distribution"].items()),
                        columns=["温度等级", "数量"]
                    )
                    df_temp.to_excel(writer, sheet_name='温度等级分布', index=False)
            
            logging.info(f"数据已导出至 {filename}")
        except Exception as e:
            logging.error(f"导出Excel失败: {str(e)}")


# 示例调用
if __name__ == "__main__":
    # 替换为实际参数
    APP_KEY = "your_elec_appkey"
    APP_SECRET = "your_elec_appsecret"
    KEYWORD = "STM32F103 单片机"  # 搜索关键词(型号+品类)
    
    # 初始化客户端(企业级权限)
    elec_search = ElecItemSearch(APP_KEY, APP_SECRET, auth_level="enterprise")
    
    # 1. 定义技术筛选条件(搜索STM32F103系列单片机,LQFP48封装,工业级温度)
    filters = {
        "sort": "price asc",  # 按价格升序
        "physicalFilter": json.dumps({"package": "LQFP48", "pinCount": 48}),  # 物理参数筛选
        "envFilter": json.dumps({"operatingTemp": "-40~85℃"}),  # 环境参数筛选
        "certFilter": "RoHS,CE",  # 认证筛选
        "priceFrom": 10,  # 价格下限
        "priceTo": 50,    # 价格上限
        "stockFilter": json.dumps({"minQty": 100})  # 库存筛选(至少100个)
    }
    
    # 2. 批量搜索(获取前3页结果)
    print("=== 电子元件搜索 ===")
    items, meta_info = elec_search.batch_search(
        keyword=KEYWORD,
        max_pages=3,
        page_size=50,
        filters=filters
    )
    
    if items:
        print(f"搜索关键词: {KEYWORD}")
        print(f"获取商品数量: {len(items)}")
        print(f"总商品数量: {meta_info['total_items']}")
        print(f"主要封装类型: {', '.join([k for k, v in meta_info['filter_stats'].get('packageStats', [])[:3]])}")
    
    # 3. 技术参数分析
    print("\n=== 技术参数分析 ===")
    if items:
        tech_analysis = elec_search.analyze_technical_params(items)
        
        print("封装类型分布(前3):")
        top_packages = sorted(tech_analysis["package_distribution"].items(), 
                            key=lambda x: x[1], reverse=True)[:3]
        for pkg, count in top_packages:
            print(f"  {pkg}: {count}个商品")
        
        print("\n品牌分布(前3):")
        top_brands = sorted(tech_analysis["brand_distribution"].items(), 
                          key=lambda x: x[1], reverse=True)[:3]
        for brand, count in top_brands:
            print(f"  {brand}: {count}个商品")
        
        print("\n温度等级分布:")
        for grade, count in tech_analysis["temperature_grade_distribution"].items():
            print(f"  {grade}: {count}个商品")
        
        print("\n最小起订量分布:")
        for range_, count in tech_analysis["moq_distribution"].items():
            print(f"  {range_}个: {count}个商品")
        
        # 4. 查找替代型号(以STM32F103C8T6为例)
        print("\n=== 替代型号推荐 ===")
        alternatives = elec_search.find_alternatives(items, "STM32F103C8T6")
        if alternatives:
            for i, alt in enumerate(alternatives[:5], 1):
                print(f"{i}. 型号: {alt['model']}, 相似度: {alt['similarity']}%, 价格: ¥{alt['price']}")
        else:
            print("未找到替代型号")
        
        # 5. 可视化分析结果
        elec_search.visualize_analysis(tech_analysis)
        
        # 6. 导出数据到Excel
        elec_search.export_to_excel(
            items, tech_analysis, meta_info, 
            f"电子元件搜索_{KEYWORD.replace(' ', '_')}_分析.xlsx"
        )
    else:
        print("未获取到商品数据,无法进行分析")


四、接口调用注意事项与行业适配建议

1. 常见错误及解决方案

错误码说明解决方案
1001无效的 appKey检查 appKey 是否与权限等级匹配(工业级接口需专用 appKey)
1003权限不足若需调用企业级参数(如批次查询),需升级权限并提供企业资质
2005参数格式错误技术参数筛选(paramFilter 等)需严格使用 JSON 格式,且参数名必须匹配平台定义
2006关键词过于宽泛电子元件搜索需精确(如 “电阻” 需补充 “0402 10KΩ”),否则返回结果超限
3002数据获取失败电子元件数据分散在多个供应商系统,可重试 1-2 次,仍失败则更换关键词
4003认证参数无效certFilter 中的认证名称需与平台标准一致(如 “RoHS” 不可简写为 “ROHS”)

2. 行业适配优化建议

  • 参数组合策略:搜索时优先使用 “型号 + 封装” 组合(如 “STM32F103C8T6 LQFP48”),比单独使用型号或功能词精度提升 80%;

  • 缓存分层设计:通用参数(如封装、品牌)缓存 30 分钟,价格和库存数据缓存 5 分钟,避免频繁调用;

  • 替代型号逻辑:除接口返回的替代型号外,可自行实现 “同封装 + 引脚兼容 + 功能相似” 的替代算法;

  • 批量搜索控制:多关键词搜索时,按 “品类→品牌→型号” 层级递进,避免同时发起大量请求;

  • 数据校验机制:对返回的技术参数进行格式校验(如电阻值必须包含 “Ω”),过滤异常数据;

  • 采购决策辅助:结合 MOQ 和阶梯价计算总成本(如 MOQ=1000 时的单位成本可能低于小批量采购)。

五、典型应用场景

  1. BOM 表采购匹配:导入电子物料清单(BOM),自动搜索并匹配各元件的供应商、价格、库存,生成采购方案;

  2. 替代型号查找:针对停产或缺货型号(如某些老旧芯片),快速找到引脚兼容、功能相似的替代型号;

  3. 成本优化分析:对比相同参数不同品牌的价格差异(如国产电阻 vs 进口电阻),在满足技术要求的前提下降低成本;

  4. 供应链风险评估:分析关键元件的供应商分布、库存稳定性、交期,评估断供风险;

  5. 新品选型调研:搜索同类型新元件(如新一代传感器),对比技术参数和价格,辅助选型决策。

电子元件的 item_search 接口是工业供应链数字化的关键入口,其价值不仅在于 “找到商品”,更在于 “精准匹配技术需求” 并 “优化采购决策”。在实现时需充分理解电子元件的行业特性,兼顾参数精度与供应链效率,才能构建真正满足工程师和采购人员需求的搜索系统。


群贤毕至

访客