1. 项目概述一个帮你追踪德国超市折扣的智能工具如果你在德国生活或者对德国超市的折扣信息感兴趣那你肯定对每周更新的超市宣传册不陌生。Aldi、Lidl、REWE这些超市每周都会推出新的“Prospekte”里面藏着各种打折商品。但问题来了手动翻看几十页的PDF或者在不同超市的App里来回切换只为了找到一瓶最便宜的可乐这效率实在太低了。今天要聊的这个项目openclaw-supermarket-deals就是为了解决这个痛点而生的。它是一个基于OpenClaw框架的技能通过调用Marktguru的公开数据接口自动帮你搜索、比对全德国各大超市的当前折扣信息并按照“每升欧元”的价格进行智能排序让你一眼就能看到性价比最高的商品。简单来说它就是一个命令行工具你告诉它“我想找附近最便宜的牛奶”或者“这周哪个超市的啤酒在打折”它就能从海量的超市宣传册数据里把相关信息挖出来整理成清晰的表格给你。它的核心价值在于自动化和比价。你不用再当“人肉爬虫”它替你完成了最繁琐的数据收集和初步筛选工作。这个工具特别适合两类人一是精打细算、希望最大化购物性价比的普通消费者二是那些需要批量监控特定商品价格波动的开发者或研究者比如想分析某个品牌促销策略的人。这个项目的巧妙之处在于它本身并不复杂只是一个“数据抓取器”。它负责从Marktguru这个聚合了德国超市折扣信息的网站上获取最原始的数据。而真正的“智能”部分比如如何理解你的模糊指令、如何过滤掉不相关的结果、如何通过Telegram定时推送给你则交给了更上层的“智能体”来处理。这种设计让这个技能变得非常灵活和可复用你可以把它嵌入到任何你喜欢的自动化工作流中。2. 核心原理与工作流程拆解要理解这个工具怎么用首先得明白它背后是怎么运作的。整个过程可以拆解成几个清晰的步骤每一步都解决了一个具体的技术或逻辑问题。2.1 数据来源Marktguru API的逆向工程项目的所有数据都来自Marktguru.de。这是一个德国本地的比价网站它聚合了几乎所有主流超市的每周宣传册信息。通常要使用一个网站的API你需要注册账号、申请API密钥甚至可能付费。但Marktguru的网页端是对公众免费开放的这意味着它的数据接口在某种程度上也是可访问的。这个项目采用了一种在开发者中很常见的技术思路运行时动态提取API密钥。它没有硬编码一个固定的密钥而是在每次运行或每隔一段时间时模拟浏览器访问Marktguru的主页从网页的源代码或网络请求中提取出当前有效的API认证信息如token、签名等。这样做有几个好处零成本、零注册用户无需任何前置操作开箱即用。高可用性即使Marktguru定期更换了API密钥只要网页端的访问逻辑不变这个工具就能自动获取到新的密钥保证了长期可用性。规避法律风险相比于直接爬取网页内容通过公开的API接口获取结构化数据通常更符合网站的服务条款当然具体仍需参考Marktguru的使用政策。注意这种“动态提取密钥”的方式依赖于Marktguru网站前端的实现细节。如果未来某天Marktguru彻底改变了其前端架构或加密方式这个工具可能需要相应调整。因此项目的维护者需要关注源站的变化。提取到的API密钥会被缓存在用户本地的一个文件~/.supermarket-deals/keys.json中默认有效期是6小时。这意味着在6小时内重复搜索工具会直接使用缓存的密钥而不用再次访问网页从而提升了响应速度并减少了对方服务器的负载。2.2 搜索与排序逻辑从模糊查询到精确比价当你输入一个搜索词比如“Cola Zero”工具会调用Marktguru的搜索接口并附上你的邮政编码。接口会返回所有匹配该关键词的、在你指定区域有效的促销商品。但这里有一个非常关键且容易踩坑的细节德国超市的促销条目命名往往非常笼统。例如一个促销可能只写“Fanta/Sprite/Mezzo Mix versch. Sorten”各种口味的芬达、雪碧、美汁源混合装。这个条目里根本没有出现“Cola”或“Coke”但实际上这种“混合口味”的促销包很可能包含了可口可乐旗下的所有无糖变种包括“Coca-Cola Zero”。如果你只用“Coke Zero”作为关键词进行精确搜索就会完美错过这个折扣。因此项目文档里强烈推荐使用宽泛搜索策略。你应该搜索“Cola”而不是“Coca-Cola Zero”。这样所有包含“Cola”字眼以及那些名为“versch. Sorten”各种口味但实际包含可乐的促销条目都会被抓取回来。接下来就是工具的“智能”所在按“每升欧元”排序。超市促销价签上通常只显示总价比如“1.29欧元”。但商品规格不同一瓶1.5升卖1.29欧元和一瓶2升卖1.29欧元单价天差地别。工具会自动解析商品描述中的容量信息升‘l’、毫升‘ml’等计算出每升单价EUR/L并按照这个单价从低到高进行排序。这才是真正意义上的“比价”让你一眼就能看出哪个是“真便宜”哪个只是“看起来便宜”。2.3 与智能体的分工为什么它被设计成“笨”的这是本项目架构设计上最值得称道的一点。supermarket-deals这个技能本身被刻意设计成一个功能单一、输出原始的“笨”数据抓取器。它的任务就是根据输入的关键词和邮编返回一堆结构化的促销数据。而更高级的“智能”任务则交给了调用它的“智能体”。这个智能体可以是OpenClaw框架中的其他模块也可以是你自己写的脚本甚至是一个简单的cron定时任务加上一些文本处理命令。智能体的任务包括语义过滤从“Cola”的宽泛结果中精准挑出你真正想要的“Coca-Cola Zero”或“无糖可乐”条目并排除掉“Powerade”或“Fuze Tea”等无关结果。通知与格式化将筛选后的结果通过Telegram Bot发送给你或者整理成每周邮件简报。个性化逻辑比如只关注单价低于某个阈值如0.5 EUR/L的“神价”或者特别高亮你常去的某家超市的折扣。这种“瘦核心胖外围”的设计带来了巨大的灵活性。这个抓取折扣的技能可以作为一个通用数据源被复用于无数个不同的场景和个性化需求中而不需要每次都在技能内部写死逻辑。如果你想把它集成到你已有的家庭自动化系统里也会非常容易。3. 从零开始环境准备与安装部署了解了原理我们来看看如何亲手把它搭建起来。整个过程并不复杂但有一些细节需要注意。3.1 基础环境搭建首先你需要一个能够运行Node.js的环境。这个项目要求Node.js版本在18或以上。我推荐使用nvm来管理Node.js版本这样可以轻松地在不同项目间切换。# 安装nvm如果尚未安装 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash # 重新加载shell配置 source ~/.bashrc # 安装并使用Node.js 18或更高版本 nvm install 18 nvm use 18验证安装是否成功node --version # 应输出 v18.x.x 或更高 npm --version # 应输出对应版本号接下来是获取项目代码。目前该项目主要通过GitHub仓库分发。# 克隆项目仓库到本地 git clone https://github.com/benmillerat/openclaw-supermarket-deals.git # 进入项目目录 cd openclaw-supermarket-deals3.2 依赖安装与项目构建进入项目目录后你会发现这是一个标准的Node.js项目依赖管理通过package.json文件定义。# 安装项目所需的所有依赖包 npm install这个命令会根据package.json中的定义下载所有必需的库比如用于发送HTTP请求的axios、用于解析HTML的cheerio用于提取API密钥、用于处理命令行参数的commander等。安装过程通常很快如果遇到网络问题可以考虑配置npm镜像源。安装完依赖后项目还需要一步构建build操作。这是因为项目源码可能是用TypeScript等需要编译的语言写的或者有一些资源需要预处理。查看package.json中的scripts部分可以看到构建命令。# 执行构建生成可直接运行的代码通常在dist目录下 npm run build执行成功后你应该会在项目根目录下看到一个dist文件夹里面包含了编译、打包好的JavaScript文件。至此核心程序就准备好了。3.3 初始配置告诉工具你的位置和偏好工具安装好了但在第一次使用前最好先进行一些基本配置主要是设置默认的邮政编码和关注的超市。这样以后每次搜索就不用重复输入这些信息了。配置通过命令行工具完成所有配置会保存在你用户主目录下的一个隐藏文件里~/.supermarket-deals/config.json。设置默认邮政编码这是最重要的配置。工具需要知道你的位置才能返回你周边超市的促销信息。德国的邮政编码是5位数字。node dist/index.js config set zip 85540请将85540替换成你所在区域的实际邮政编码。如果你不知道可以在Google地图上搜索你的地址获取。设置默认关注的超市德国超市品牌很多你可能只对其中几家感兴趣。你可以设置一个默认列表。node dist/index.js config set stores Lidl,REWE,EDEKA,ALDI SÜD,ALDI NORD,Kaufland这个列表是一个用逗号分隔的字符串。注意超市名称必须与Marktguru内部使用的名称一致上述例子是几个主流超市的常见名称。你可以后续通过搜索来观察结果中Store字段的具体写法再回头调整这个配置。查看当前配置任何时候你都可以查看已经保存的配置。node dist/index.js config这会输出一个JSON格式的配置内容确认你的设置已生效。实操心得在设置超市列表时我建议初期先保持默认或设置成包含所有主流超市。运行几次搜索后观察结果中哪些超市频繁出现且折扣符合你的预期再逐步精简列表。有些区域性小超市可能不在Marktguru的收录范围内即使设置了也不会出现结果。4. 核心功能实战搜索、过滤与结果解析配置完成后我们就可以开始真正的搜索了。这是工具最核心的功能所有的价值都通过search命令体现出来。4.1 基础搜索与参数详解最基本的搜索命令格式如下node dist/index.js search 查询词 [--zip 邮编] [--stores 超市列表] [--limit 结果数] [--json]查询词这是你要搜索的商品名称。强烈建议使用德语关键词因为数据源是德语的。搜索“Milch”比搜索“Milk”效果要好得多。你可以输入多个词工具会合并处理并去重。--zip指定本次搜索的邮政编码。如果省略则使用配置文件中设置的默认值。--stores指定本次搜索只关注哪些超市。格式同样是逗号分隔的字符串。如果省略则使用配置文件中的默认列表。--limit限制返回结果的数量默认是20条最多可以设置为100条。对于日常使用20条通常足够了它能展示出性价比最高的前20个选择。--json这是一个非常重要的选项。如果加上这个标志工具将不会输出给人看的表格而是输出结构化的JSON数据。这个格式是为后续的自动化处理比如由智能体解析准备的。让我们看几个具体的例子示例1寻找附近最便宜的可乐node dist/index.js search Cola --zip 10115这个命令会在柏林米特区邮编10115附近搜索所有名称中包含“Cola”的促销商品并按每升单价排序输出。示例2只查看Aldi和Lidl的意面折扣node dist/index.js search Pasta Nudeln --zip 80331 --stores ALDI SÜD,Lidl --limit 10这里用了两个搜索词“Pasta”和“Nudeln”德语的“面条”以提高命中率。同时将结果限定在Aldi Süd和Lidl两家超市并且只显示前10个最便宜的结果。示例3为自动化脚本准备数据node dist/index.js search Kaffee --zip 20095 --json coffee_deals.json这个命令将搜索汉堡市中心邮编20095的咖啡折扣并将JSON格式的结果输出并保存到coffee_deals.json文件中。这个文件可以被其他程序轻松读取和处理。4.2 结果解读与高级过滤思路工具默认会输出一个格式清晰的表格如下所示Description | Store | Size | Price | EUR/L | Valid | URL -------------------------------------------------------------------------------------------------------------------------------- Coca-Cola zero 2-l-Flasche zzgl. Pfand 0.25 | ALDI SÜD | 2l | 1.29 EUR | 0.65 EUR/L | 2026-03-12–2026-03-14 | https://www.marktguru.de/offers/21916812 Fanta/Sprite/Mezzo Mix versch. Sorten. 1,25 l | Lidl | 1.25l | 0.99 EUR | 0.79 EUR/L | 2026-03-01–2026-03-07 | https://www.marktguru.de/offers/21894391我们来逐一解读每一列的含义Description商品描述。这是从超市宣传册上直接抓取的文字可能比较冗长或包含附加信息如“zzgl. Pfand 0.25”表示需另付0.25欧元押金。Store超市名称。注意名称可能包含空格和特殊字符如“ALDI SÜD”。Size商品规格/容量。工具会尽力从描述中解析出升l、毫升ml、克g等单位。这是计算单价的基础。Price促销总价。EUR/L核心指标计算出的每升或每公斤/每单位价格。此列已按升序排列排在第一行的就是当前搜索条件下单价最低的商品。Valid促销有效期。德国超市促销通常以周为单位从周四开始到下周三结束或者有特定的短促日期。URL该促销在Marktguru网站上的详细页面链接。你可以点进去查看商品图片、更详细的条款以及附近有哪些具体分店参与活动。仅仅看表格还不够我们需要结合一些策略来用好这些信息关注“EUR/L”列这是跨品牌、跨规格比价的唯一公平标准。不要被“总价低”迷惑一定要看单价。仔细阅读“Description”特别是注意“versch. Sorten”各种口味、“zzgl. Pfand”另付押金、“im Angebot”特价中等关键词。这些信息会影响你的实际购买决策。善用“Valid”日期促销是有时效性的。你可以根据有效期来规划你的购物时间。如果看到心仪的商品促销期只剩一两天就得抓紧了。“URL”是你的朋友对于不确定的商品点开链接查看详情页总是没错的可以确认具体的品牌、口味和参与活动的门店。4.3 构建自动化比价工作流这个工具真正的威力在于自动化。你可以把它设置成一个定时任务让它定期为你扫描关注的商品并通过通知推送给你。一个经典的自动化模式是使用Linux/macOS的cron定时任务或者Windows的任务计划程序。假设你每周四早上想看看新的可乐折扣德国超市宣传册通常在周四更新可以这样设置首先创建一个脚本文件比如check_cola_deals.sh#!/bin/bash cd /path/to/your/supermarket-deals # 执行搜索并将JSON结果输出到文件 node dist/index.js search Cola --zip YOUR_ZIP_CODE --json /tmp/cola_deals.json # 然后你可以用Python、Node.js或其他脚本解析/tmp/cola_deals.json # 例如使用简单的jq命令提取前3个最便宜的结果 echo 本周可乐折扣TOP 3: jq -r .[0:3] | .[] | \(.description) | 单价: \(.pricePerL) EUR/L | 超市: \(.store) | 链接: \(.url) /tmp/cola_deals.json # 最后可以将这个echo的结果通过邮件、Telegram Bot等方式发送给自己然后使用crontab -e命令编辑定时任务添加一行0 9 * * 4 /bin/bash /path/to/your/check_cola_deals.sh这表示每周四早上9点运行一次这个脚本。更高级的玩法是将其集成到OpenClaw智能体中。你可以在智能体的配置中将这个技能定义为一个数据源然后编写智能体的推理逻辑“用户想要可乐折扣但排除姜汁啤酒。找到单价低于0.7 EUR/L的并且超市是用户常去的三家之一。将结果格式化为一条友好的消息通过Telegram发送。” 这样你就拥有了一个完全个性化、智能化的家庭购物助手。5. 常见问题、排查技巧与进阶思考在实际使用中你可能会遇到一些问题。这里我整理了一些常见的情况和解决方法其中不少是我自己踩过的坑。5.1 搜索无结果或结果异常这是新手最常遇到的问题。通常有以下几个原因问题现象可能原因排查与解决方法搜索后返回“No results found”或空表。1.邮编错误输入的邮编不在德国或格式不对。2.关键词太冷门或拼写错误商品名用了英语或拼写不准确。3.促销周期未更新当前时间点如周三深夜可能处于新旧宣传册交替的空窗期。4.超市未覆盖你设置的超市列表中的超市在你提供的邮编区域本周恰好没有相关商品的促销或者该超市的分店未向Marktguru提供数据。1.验证邮编在Google地图上确认邮编的有效性。2.使用更宽泛、更德语化的关键词用“Joghurt”代替“Yogurt”用“Wasser”代替“Water”。尝试搜索品类词如“Käse”奶酪而非具体品牌。3.了解促销节奏德国超市促销册大多在周一和周四更新。在这两个日期之后搜索数据会更全。也可以直接访问Marktguru网站手动搜索相同邮编和关键词确认数据源本身是否有信息。4.扩大搜索范围暂时移除--stores限制或尝试使用附近大城市的邮编如汉堡的20095、慕尼黑的80331进行“健全性检查”。如果大邮编有结果而你的邮编没有说明你那片区域可能数据覆盖不全。结果中出现了完全不相关的商品。关键词匹配过于宽泛。例如搜索“Apfel”苹果可能也会匹配到“Apfelsaft”苹果汁或“Apfelkuchen”苹果蛋糕。这是工具数据源的限制目前技能本身没有语义理解能力。解决方法是依赖后续的智能体过滤。在自动化脚本中你可以编写规则只保留描述中精确包含“Apfel”且不包含“Saft”或“Kuchen”的条目。EUR/L价格计算异常如极高或为0。工具从商品描述文本中解析容量失败。例如描述是“Coca-Cola 6x1.5l”或容量单位是“Stück”个而非体积/重量单位。查看该条目的Size列。如果显示为N/A或奇怪的值说明解析失败。此时需要手动点开URL链接查看详情或忽略该条结果。复杂的多件装、非标品如一盆花的价格比较本身就困难。5.2 性能与稳定性优化如果你打算高频次使用或将其用于自动化以下几点需要注意尊重数据源工具内部已经对API密钥做了6小时的缓存避免频繁请求Marktguru首页。在编写自己的定时脚本时不要设置过短的执行间隔如每分钟一次。建议以小时或天为单位。频繁的请求可能会触发对方的反爬机制导致IP被暂时限制。处理网络波动你的脚本应该具备基本的错误处理能力。例如在调用搜索命令时检查其退出状态码如果失败则记录日志并可能重试而不是让整个工作流崩溃。结果去重与历史对比对于自动化监控你可能不仅想知道本周的折扣还想知道相比上周是涨价了还是降价了。你可以将每周的JSON结果保存下来写一个简单的脚本对比同一商品通过描述或URL模糊匹配的历史价格生成价格走势报告。5.3 安全与合规性考量这是一个绕不开的话题。项目作者采用动态提取API密钥的方式本质上是在使用Marktguru公开的、面向网页用户的数据接口。个人使用用于个人比价、自动化提醒通常被认为是合理使用风险极低。这和你手动打开浏览器访问Marktguru网站没有本质区别只是效率更高。商业或大规模使用如果你计划基于此工具开发商业应用或进行极高频率的数据抓取例如每分钟监控数千种商品则必须仔细阅读Marktguru网站的robots.txt文件和服务条款并考虑联系他们获取正式的API许可。未经授权的大规模抓取可能违反其服务条款。数据准确性工具提供的数据完全依赖于Marktguru而Marktguru的数据又来自各超市。可能存在更新延迟或错误。最终购买前务必以超市实体店或官方App内的标价为准。这个工具是一个强大的参考但不是具有法律效力的价格承诺。5.4 扩展可能性这个开源项目提供了一个很好的基础你可以基于它进行扩展支持更多国家/地区项目的核心逻辑是通用的。理论上只要找到目标国家类似的促销信息聚合网站并完成对其API的逆向工程就可以适配出“法国超市折扣”、“日本超市折扣”等技能。增加数据持久化与可视化将每次抓取的结果存入数据库如SQLite或PostgreSQL然后利用Grafana或简单的网页图表库绘制出某个商品如“牛奶”长期的价格曲线和超市间差价这对于研究消费趋势非常有用。集成到更复杂的智能家居场景例如当工具发现你常买的啤酒单价低于历史平均价的20%时不仅发送通知还可以自动在你的购物清单App如Todoist、AnyList中添加一条记录。这个项目就像给你提供了一把精准的“洛阳铲”让你能轻松地挖掘德国超市折扣的金矿。而如何筛选、加工这些矿石最终打造出什么样的“金器”就完全取决于你的想象力和自动化技能了。从我个人的使用体验来看它最大的价值是节省了决策时间。以前需要花十几分钟浏览各个App现在一条命令30秒内就能对全市场的价格了然于胸。这种信息获取效率的提升本身就是一种巨大的财富。