Skip to main content
Glama

chineur

整个项目按最低价格(含运费)报价,新品与二手分开处理,并提供分组采购方案,以最大程度减少实际支付总额。

逐件商品买最便宜并不是正确答案:向 10 个商家买 10 件商品,就要付 10 次运费。购物车引擎会把购买集中到 2 或 3 个商家(计入免邮门槛),并显示与这种盲目合计之间的成本差额。

安装

python -m venv .venv && ./.venv/bin/pip install -e .
./.venv/bin/pytest          # 74 tests hors ligne, aucun réseau

Python 3.11 或更高版本。项目无需任何密钥即可运行:它在使用 LeDénicheur、AliExpress 和 Leboncoin 时不需要任何密钥。需要密钥的数据源(eBay、Mouser)则干脆保持关闭,list_sources() 会说明这一点。

Related MCP server: aws-calculator-assistant

密钥

不会从版本化文件中读取任何密钥。bin/chineur-mcp.sh 会加载本地的 .env 文件(若存在,并被 git 忽略),否则直接使用当前环境;这就允许通过密钥管理器用简单替换的方式注入密钥,值仅经由进程环境传递:

export EBAY_CERT_ID=$(votre-cli-de-coffre get password 'eBay - API Browse')

参数

作用

没有它时

EBAY_APP_ID / EBAY_CERT_ID

eBay Browse keyset

此 eBay 数据源被关闭

MOUSER_API_KEY

Mouser Search API key

Mouser 数据源被关闭

FLARESOLVERR_URL

遇到反爬墙时启用浏览器兜底

兜底关闭,其余功能正常

CHINEUR_VAR

状态目录(缓存、限额、cookies、日志)

使用 <dépôt>/var

用法

通过 MCP stdio 暴露(bin/chineur-mcp.sh),共有三个工具:

工具

作用

price_project(articles, zipcode?, used_threshold?, detail?)

一次调用中为整个清单报价

best_price(article, …)

单条商品场景,使用 detail="full" 返回详情

list_sources()

每个数据源的状态、剩余每小时限额以及当前发生的切断

每一条商品都允许指定数量或上限:字符串 "LD2410C x3""ESP32-S3 <=12€"

在命令行中:

python -c "
import asyncio; from chineur.models import Project
from chineur.search import Orchestrator; from chineur.report import render
async def m():
    o = Orchestrator(); print(render(await o.run(Project.build(['LD2410C x2','BME280'])), failures=o.failures))
asyncio.run(m())"

数据源及其价值

数据源

新品/二手

运费

备注

LeDénicheur

新品 + 二手

精确

聚合器:只一次请求即可引入 Amazon、Cdiscount、Fnac、Rue du Commerce、Back Market、Rakuten

AliExpress

新品

预计

唯一能稳定找到小众国产模块的现实数据源

Leboncoin

二手

预计

每小时最多 10 次搜索,不可增加

eBay

新品 + 二手

精确

官方 Browse API,目前所需组件 2026-08-06 起生效

Mouser

新品

预计

专业分销商,官方 API。它覆盖了 AliExpress 缺失的内容(焊料、吸锡带、电阻、晶体管、连接器)。 对国产模块没用:要么没有,要么 3 到 5 倍价格

已知限制(测试于 2026-08-05,且除非使用 Playwright (V2) 之外无法绕过):

  • AliExpress 不在搜索页面结果中给出卖家或运费,且通过纯 HTTP 打开产品页面时显示为空。因此合并购买只能在平台层面完成,并采用其免邮门槛。几次连续请求后,AliExpress 返回 HTTP 200 以及一份 2 KB 页面:程序会将其识别并视为封锁,而不是“没有报价”。

  • AliExpress 的上限大约是每 IP 链接尝试的 6 次请求,在无法确认 46 小时后于 2026-08-10 附带测得:6 个商品被正常返回(共 133 条报价),第 7 次被封锁,FlareSolverr 也撞上了同一堵墙。因此,17 个商品的清单无法通过一次批量爬取完成,即使等待时间加长并保持间隔也一样。这是结构性限制,也是必须用官方驻盟 API(portals.aliexpress.com)的原因,而不是通过调一个更长的时间间隔就能解决的问题。

  • 二手价格只是作为参考。 Leboncoin 的广告是自由文本:搜索结果可能是一整批货、损坏的零件或配件。新品和二手永远没有一起合并成一个统一价。

  • 运费在代码中标记为 ~ 时表示其为估算值,? 时表示未知数(见脚本 shipping.py)。任何包含估算运费的合计都会补表示为估算值。

匹配效率:真正的瓶颈

测试于 2026-08-08,使用真实需要的清单(17 个组件):我们当时口中的「找不到」并不是因为缺少数据源,而是因为商品名称本身的问题。通过统计 eBay 在启动结果过滤前返回的原始标题,我们可以区分出两个不同原因。

查询条件太长。 在 eBay 上搜索 « etain soudure 60/40 flux 0.8mm » 返回 0 个结果;搜索 « etain soudure » 返回 到 个结果。每多增加一个描述性单词都会让结果一步步收窄,直至没有结果。因此我引入了 reduced_queryguarded_search第二次尝试机制:当某个数据源没有返回任何结果时,会对聚焦到只有在失败时进行第二次更短的查询,并且计数不占用 数据源的最后一次配额。

过滤算法之前误配了一批好结果。 例如搜索“cables dupont assortiment”对比标题“Câble Dupont … Trésultat : 0.67,而适度为 0.70”——评分规则只做子串匹配,因此“复数形式”(multiple 形态) 让整个匹配失效。现在 _apparie 不加前缀的经过换进行匹配(取占其 60% 的公共前缀),且仅针对长度 ≥4 的单词,不会处理数字或短参考,因此 “2410” 和 “2420” 仍然会被视为两种不同传感器。

在同一列表上用同样清单测试,里,第二新版文字结果:eBay 覆盖数量从 10/17 提高到 14/17,完全找不到商品数从 5 减少到 3。剩下即使所有数据源也无法找到的三样东西是:带焊接盘的 PCB 板,Diese 2201M 以及 Sugon 8620DX 这类复制品,如 Sugon 8620DX

这代价是真实存在的、且需要付出。 扩大规模搜索范围后,结果会对应多扩展较宽的问题而不是你所问的问题:“ expansion-ESP32-WROOM-32 DevKit 38 针”会扩展到 “ESP32-ESP32 32”,并返回一个 30 招的型号。为什么要这样设计 任何来自于放大搜索范围的结果,因此都必须完整体,提供“搜索范围已扩展为 “…”” 这样的提示,并且提示要一直显示到最终呈现为止。

浏览器自动化翻墙(FlareSolverr)

这是项目目前最大的软肋: AliExpress 是国产模块的唯一真实数据源,而它在 5 到 9 次搜索之后大约就会锁定。README 中 erst von 这个节写为“不用 Playwright (V2) 不可绕过”。但它实,无需在项目端添加了一个额外条件:一个 FlareSolver 实例(真实 Chrome浏览器,运行 JavaScript e,并使用完整的浏览器指纹),因此可以通过配置 FLARESOLVERR_URL 来启用。未设置时,该翻墙功能则直接关闭。

实测于 2026-08-08,当时直连已被阻断、熔断器已打开:fr.aliexpress.com/w/wholesale-LD2410C.html 返回 HTTP 200,响应大小为 832 KB,无任何防爬特征,并且我们现有的 HTML 解析器从页面中解析出 60 件商品,价格为 EUR。解析逻辑完全没有改动。

现在将它与 AliExpress._items 中给这个爬虫连接:只要出现反爬页面,或者响应内容不完整,就会先尝试翻墙,如果最终调任会才抛出 Blocked。设计要点如下:

  • 这是一个保护机制,不会每次搜索都启用。 每次调用都会启动一个 Chrome 实例:响应效果和资源占用都较高。因此在真正发生不可失败时才进入。

  • 返回失败时返回 None,而不是 []。一个空列表会被当成“没有任何报价”,那样断路器就不会自动打开,然后连接器就会在下一次查询时再次去批访问,已封锁的数据源,会加剧限制。

  • 永不阻塞调用流程。 flaresolverr.fetch 抛不到它:否则,sign自己故障会掩盖真正的原因,并且能从外部看到页面性问题。设置 FLARESOLVERR_URL= 以禁用该翻墙。

  • 连续失败 2 次后放弃。 见下面所述限制过大——如果不这么考虑,后续每个商品都得白白多等待一整数十几十秒。

它的限制(实测于 2026-08-08)。 这个兜底方案并不是持久有效的翻墙替代,而是一种第二种额度。连续连续 8 次 AliExpress 搜索中,前 6 次搜索能拿到 60 个商品,每次 4 到 6 秒;第 7 次在等待 101 秒后返回 500 错误;第 8 次则直接返回反爬墙(208 KB,携带爬虫标记 RGV587)。站的 IP 出口地址不会改变,因此最终整个服务器都会被会诶一次识别。代码里有两种对应方案:把 maxTimeout 从 90 秒降到 45 秒,并且一种当反爬墙也拖着 HTTP 200 成功时会被统计为失败,否则这个暂停保护早就不会真正派礼。

因此,目前实际可用的 AliExpress 额度大约是 9 次提高到 15 次,而每页的报价结果从 20 个变成 60 个。这是净增加,但同时还不是無限。

防封提供保障

出口 IP 是唯一且住宅的——一旦被站点限制,完事彻底切断。整个模块的代码设计都是为了防止 USB接口无论如何都不要发生这种情况。

  • 每小时配额持久化在 SQLite(var/chineur.db)中:Leboncoin 10/小时、LeDénicheur 30/小时、AliExpress 20/小时、eBay 200/小时、Mouser 60/小时(API 配额,无 IP 风险)。

  • 按来源串行化的间隔,而不是简单的 sleep:编排器并行启动所有条目, 并发的延迟本来也会同时结束,请求仍会成突发地发出。每次启动都要等前一次完成。

  • 针对 403 / 验证码 / 页面截断的熔断器:1 小时,AliExpress 为 2 小时,其惩罚会持续到触发它的那次突发之后。

  • AliExpress 的阻断既不是配额问题,也不是速率问题。 2026-08-06 的三组测试记录,均从未被缓存:

    间隔

    传输方式

    被阻断前已服务条数

    6-12 s

    裸请求

    5(46 s 时阻断)

    6-12 s

    带 cookie 的会话

    6(56 s 时阻断)

    45-60 s

    带 cookie 的会话

    2(108 s 时阻断)

    2,5 s

    带 cookie 的会话,JSON 端点

    9(30 s 时阻断)

    放慢 降低 吞吐,所以节奏并不是杠杆:最快的吞吐来自快速运行。阻断持续约 40 分钟,且测量到两次;它由 FAIL_SYS_USER_VALIDATE / RGV587_ERROR::SM 表明,这是阿里巴巴要求验证的反爬机制。

  • 商品从内部端点 POST www.aliexpress.com/fn/search-pc/index 读取(就是搜索页自身调用的那个),请求载荷形式为 mods,会话事先已在首页上打开。它返回与 HTML 页面完全相同的商品对象,位于 .data.result.mods.itemList.content,只需 390 字节而不是 600。陷阱:响应中有多个块带有 content 键;其中搜索筛选的那一块出现在 itemList 之前。HTML 页面仍作为自动回退,但只有在响应不再具有预期形式时才这样:面对反爬墙时,从同一 IP 重试 HTML 只会浪费一次请求。

  • 持久会话transport.Session)为 AliExpress 保留:首页访问过一次,之后重放 cookie,cookie jar 放在 var/cookies/ 以便重启后依然可用;一旦页面返回被截断就丢弃。它对阻断没有产生可测量的改善;保留它只是因为这让流量更接近浏览器的流量。

  • 尚未定论的路径:如果某天内部端点关闭,就把该来源外包给联盟托管服务(omkar.cloud,每月 5 000 次免费请求),代价是第三方会看到我们的搜索。

  • 速率限制器之前查询缓存:重新计算一个项目不会再请求任何数据。默认 TTL 为 1 小时,但绝不短于熔断器的断开时间(Connector.effective_ttl,AliExpress 为 6 小时)。没有这个下限,一个比单个来源预算更大的项目永远无法完成:断开后的复起看到空缓存,重做同样的最初搜索,再次烧掉预算并再次被阻断,无限循环。 eBay 除外,它被排除在缓存之外,以便继续符合豁免(见下文)。

  • 超出 Leboncoin 配额的项目按价值影响降序的顺序处理,未处理的条目会在汇报中列出。

Token 经济

计算仍然留在 Python 中,只有最终结论返回:输出为紧凑的表格文本;每个商品给出一个最佳全新报价和一个二手报价;标题截断、URL 清理、备注合并;诊断放在 var/chineur.log。实测:10 个商品约为 650 个 token

测试

pytest              # 74 tests hors ligne, aucun réseau
pytest -m network   # 5 tests d'intégration réels (consomment du quota)

购物车引擎可以完全离线测试,包括容易踩坑的那些边缘情况(由合并触发的免邮门槛、单一卖家、毫无报价的商品、并列情况)。在 50 组随机报价集上验证了“优化后总价 ≤ 朴素总价”这一属性。

Mouser

只有一个变量:MOUSER_API_KEY。免费的密钥可在 mouser.com/api-hub(Search API 选项卡)自助获取。

密钥必须从 mouser.fr 创建。 关键词搜索不接受任何币种参数(已在 swagger api.mouser.com/api/docs/V1 中验证):币种取注册网站所属地区。在 mouser.com 上创建的密钥返回美元;连接器会直接丢弃这些报价,而不是用被人为汇率来转换——那样这个来源就会看起来只是空无。

Mouser 公布的配额:每次请求 50 个料,每分钟 30 个 /money,每天 1 000 个 /money。因此 limit_per_hour = 60,间隔 2 秒:这不是反爬,而是遵守配额。

三个底层的选择:

  • mouserPaysCustomsAndDuties: true — Mouser 从德克萨斯发货。如果没有这个标志,关税出现在后欠你的之后,一个在纸面上“更便宜”的价格在送达时却更贵。

  • searchOptions: InStock — 供货周期 12 星期的料号,在一个项目正在进行的价格对比里根本没有理由存在。

  • 价格阶梯按需求数量取值。 只用第 1 档会限高任何被动元件的批量采购。

两个会弄显比较的陷阱,都会在库存项目上标注:最小起订量(0.10 € 元/个,但按 100 个一袋出售,实际结账金额是 10 €)和价格格式(“0.095 €”是一个三位小数的价格;“$1,250”是一部二百五十一的价钱—一个冒失的解析器会在两个方向上错 1000 倍)。

运费:未经核实。 50 € 不含税的免额以及低于该免额的约 20 € 运费,都是从网络上比对(2026-08-07信息),而不是来自实际发票。因此它们住在 Shipping.py 中,并且标成 ESTIMATED,绝不是已确立事实。必须在第一笔真实订单时就修正:因为有例如,在未满免邮的区间,运费会吃掉收益,而正是这个阈值决定此来源是否有用。

eBay

EBAY_APP_ID(App ID)和 EBAY_CERT_ID(Cert ID),用于 Production 密钥集——唯一可用的一种。Sandbox 密钥集则配 EBAY_ENV=sandbox,它会切到 api.sandbox.ebay.com在 sandbox 下连接器仍处于关闭状态:库存是假的,价格会进入购物车,而没有任何提示。EBAY_SANDBOX_OK=1 会重新打开它,仅用于核实管道连通。

2026-08-06 已验证 在一个 Production key set 上:取到了 client_credentials token,真正的搜索 29 个对“cable hdmi 2.1 2m”的报价,运费精确从 shippingOptions 读出。

物品状态读取是在 conditionId 上,绝不在 condition 上:后者是 eBay 翻译的(在 EBAY_FR 上为“Neuf”“Occasion”“Ouvert (jamais utilisé)”);原本的英文键表完全不匹配,所以所有条目都被当成/used。编号为 7000(表示“拆机)”的物品会被排除:在 3 € 的价位,一个损坏的物品会在价格对比中胜出,而没有任何提示。

合规性(“Your Keyset is currently disabled”)。 eBay 只有在注册了账户遭删除通知服务或获豁免后,才会启用 Production keyset。本项目采用的是“我不保留 eBay 数据”豁免,代码让这句话从字面上成为真:EBay.cacheable = False 因此主要的没有 eBay 答复写入 var/chineur.db(它们包含卖家昵称)。有一个测试锁定了这一点。如果有一天我们把禁再把 eBay 放回缓存,这个豁免就变成过改为:到那时就需要反过来托管该通知端点。

许可

MIT,见 LICENSE

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    B
    quality
    B
    maintenance
    MCP server for finding, comparing, and ranking the cheapest real offers across eBay, Amazon, Craigslist, OfferUp, and Google Shopping, with tax estimation and exact-model filtering.
    6
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Build shareable AWS Pricing Calculator estimates programmatically via MCP tools, designed for creating MAP funding estimates from customer infrastructure descriptions.
    8
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    A hosted MCP server that routes natural-language shopping queries to independent merchant storefronts, returning normalized product and merchant results for AI agents and shopping assistants.
    MIT

View all related MCP servers

Related MCP Connectors

  • Hosted no-auth MCP for exact-spec packaging search, live price, stock, cart handoff, and no-match.

  • Real, buyable product and service offers — live pricing and trackable links for any MCP agent.

  • Routes natural-language shopping queries to merchant storefronts, returns normalized results.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GRSMael/chineur'

If you have feedback or need assistance with the MCP directory API, please join our Discord server