告别手动复制粘贴:用TagUI实现美食数据自动化采集与营养分析全流程
你是否还在为整理菜谱数据而重复复制粘贴?是否因手工录入营养成分表格而焦头烂额?本文将带你用RPA(Robotic Process Automation,机器人流程自动化)工具TagUI,构建一个从美食网站自动采集菜谱信息、提取营养数据并生成分析报告的完整解决方案。读完本文你将掌握:
- 使用TagUI实现网页元素精确定位与数据抓取
- 批量处理多页菜谱的自动化流程设计
- 数据清洗与营养成分提取的实用技巧
- 生成可视化分析报告的完整代码实现
为什么选择TagUI做美食数据自动化?
TagUI作为一款开源RPA工具,特别适合处理菜谱数据采集这类规则性强但重复度高的任务。其核心优势在于:
相比Python爬虫需要处理复杂的反爬机制,TagUI通过模拟真实用户操作(如点击、输入、滚动)来获取数据,大幅降低被网站屏蔽的风险。同时支持Chrome/Edge浏览器的无头模式运行,可在后台静默完成整个采集过程,不影响正常电脑使用。
环境准备与基础配置
快速安装TagUI
在Linux系统中,通过以下命令即可完成安装:
# 克隆官方仓库
git clone https://gitcode.com/gh_mirrors/ta/TagUI /data/web/disk1/git_repo/gh_mirrors/ta/TagUI
# 进入目录并赋予执行权限
cd /data/web/disk1/git_repo/gh_mirrors/ta/TagUI/src
chmod +x tagui
# 验证安装成功
./tagui --version
Windows用户可直接下载安装包,Mac用户则可通过Homebrew安装。安装完成后,建议启动一次tagui live进入实时模式,熟悉基础操作语法。
项目结构设计
为美食数据采集项目创建以下目录结构,保持代码组织清晰:
food_analysis/
├── flows/ # 主流程文件
│ ├── recipe_crawler.tag # 菜谱采集主程序
│ └── nutrient_analyzer.tag # 营养分析模块
├── data/ # 数据存储
│ ├── raw_recipes.csv # 原始采集数据
│ └── cleaned_data.csv # 清洗后数据
├── images/ # 定位用图片资源
│ ├── recipe_card.png # 菜谱卡片识别图
│ └── nutrient_table.png # 营养表格识别图
└── reports/ # 生成的报告
└── analysis_report.html # 营养分析报告
核心技术:TagUI选择器与数据抓取技巧
精确定位网页元素
TagUI提供多种元素定位方式,针对美食网站常见的菜谱卡片,推荐使用XPath定位:
以某美食网站为例,要提取菜谱名称和制作时间,可使用如下代码:
// 访问目标页面
https://www.example-food-site.com/recipes/chinese
// 等待页面加载完成
wait 3 seconds
// 读取菜谱标题(XPath定位)
read //div[@class="recipe-card"]/h2 to recipe_title
// 读取烹饪时间(CSS选择器)
read .recipe-meta time to cook_time
// 输出获取结果
echo 菜谱名称: `recipe_title`
echo 烹饪时间: `cook_time`
处理分页与动态加载内容
许多美食网站采用瀑布流或分页加载方式展示菜谱列表,可通过循环结合滚动操作实现全量采集:
// 初始化页码计数器
page = 1
// 定义最大页数
max_pages = 5
// 循环采集多页数据
for page from 1 to max_pages
echo 正在采集第`page`页数据...
// 抓取当前页所有菜谱链接
read //div[@class="recipe-list"]//a@href to recipe_links
// 处理当前页每个菜谱
for link in recipe_links
// 在新标签页打开菜谱
popup `link`
// 采集菜谱详情...
read //h1[@itemprop="name"] to dish_name
read //span[@itemprop="totalTime"] to total_time
// 其他数据采集步骤...
// 保存到CSV文件
write `csv_row([dish_name, total_time, ingredients])` to data/raw_recipes.csv
// 关闭当前标签页
close popup
end
// 点击下一页
click //a[contains(text(),"下一页")]
wait 2 seconds
end
实战案例:构建完整的菜谱数据采集流程
设计采集流程
我们以"下厨房"网站为例,构建一个包含以下步骤的自动化流程:
完整代码实现
创建flows/recipe_crawler.tag文件,实现上述完整流程:
// 菜谱数据采集主流程
// 作者:美食自动化爱好者
// 版本:1.0
// 日期:2025-09-20
// 配置参数
base_url = "https://www.xiachufang.com/category/40076/"
output_file = "data/raw_recipes.csv"
max_pages = 3
// 初始化CSV文件(写入表头)
dump 菜名,烹饪时间,难度,食材,热量(千卡),蛋白质(克),脂肪(克),碳水化合物(克) to `output_file`
// 主循环 - 多页采集
for page from 1 to max_pages
echo === 开始采集第`page`页 ===
// 访问目标页面
`base_url`?page=`page`
// 等待页面加载
wait 3 seconds
// 获取当前页所有菜谱卡片
recipe_count = count('//div[@class="recipe"]')
echo 发现`recipe_count`个菜谱
// 循环处理每个菜谱
for i from 1 to recipe_count
echo 正在处理第`i`个菜谱...
// 点击菜谱卡片(XPath索引从1开始)
click (//div[@class="recipe"])[`i`]
// 等待详情页加载
wait 2 seconds
// 提取基本信息
read //h1[@class="page-title"] to dish_name
read //div[@class="info"]/span[1] to cook_time
read //div[@class="info"]/span[2] to difficulty
// 提取食材列表
ingredients = ""
ingredient_count = count('//div[@class="ings"]//li')
for j from 1 to ingredient_count
read (//div[@class="ings"]//li)[`j`] to ingredient
ingredients = ingredients + "|" + ingredient
end
// 提取营养成分(处理可能不存在的情况)
if exist('//table[@class="nutritions"]')
read (//table[@class="nutritions"]//td)[2] to calories
read (//table[@class="nutritions"]//td)[4] to protein
read (//table[@class="nutritions"]//td)[6] to fat
read (//table[@class="nutritions"]//td)[8] to carbs
else
calories = "N/A"
protein = "N/A"
fat = "N/A"
carbs = "N/A"
end
// 保存数据到CSV
write `csv_row([dish_name, cook_time, difficulty, ingredients, calories, protein, fat, carbs])` to `output_file`
// 返回列表页
click //a[@class="back"]
wait 2 seconds
end
end
echo 数据采集完成!共采集`page*recipe_count`条菜谱数据
echo 数据已保存至:`output_file`
数据清洗与营养分析
数据预处理
原始采集的数据往往包含多余空格、特殊字符等噪音,需要进行清洗。创建flows/data_cleaner.tag:
// 数据清洗流程
// 输入:raw_recipes.csv
// 输出:cleaned_data.csv
// 加载原始数据
load data/raw_recipes.csv to raw_data
// 初始化清洗后的数据文件
dump 菜名,烹饪时间(分钟),难度,主要食材,热量(千卡),蛋白质(克),脂肪(克),碳水化合物(克) to data/cleaned_data.csv
// 跳过表头行,从第二行开始处理
for row from 1 to raw_data.length-1
// 解析CSV行数据(split函数将字符串转为数组)
js data_row = raw_data[`row`].split(',')
// 提取字段(处理CSV中的逗号问题)
dish_name = data_row[0]
cook_time = data_row[1]
difficulty = data_row[2]
ingredients = data_row[3]
calories = data_row[4]
protein = data_row[5]
fat = data_row[6]
carbs = data_row[7]
// 清洗烹饪时间(提取数字部分)
js cook_time_minutes = cook_time.replace(/[^0-9]/g, '')
// 提取主要食材(取前三个)
js main_ingredients = ingredients.split('|').slice(1,4).join(';')
// 清洗营养数据(去除非数字字符)
js calories_clean = calories.replace(/[^0-9.]/g, '')
js protein_clean = protein.replace(/[^0-9.]/g, '')
js fat_clean = fat.replace(/[^0-9.]/g, '')
js carbs_clean = carbs.replace(/[^0-9.]/g, '')
// 写入清洗后的数据
write `csv_row([dish_name, cook_time_minutes, difficulty, main_ingredients, calories_clean, protein_clean, fat_clean, carbs_clean])` to data/cleaned_data.csv
end
echo 数据清洗完成!共处理`row`条记录
生成营养分析报告
使用TagUI的模板功能结合HTML/CSS,生成可视化的营养分析报告:
// 生成营养分析报告
// 读取清洗后的数据
load data/cleaned_data.csv to nutrition_data
// 创建HTML报告文件
dump <!DOCTYPE html>
<html>
<head>
<title>美食营养分析报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #d35400; border-bottom: 2px solid #d35400; }
.summary { background-color: #f8f9fa; padding: 15px; border-radius: 5px; }
.table-container { overflow-x: auto; margin-top: 20px; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
th { background-color: #f1c40f; color: #333; }
tr:hover { background-color: #f5f5f5; }
.high-protein { background-color: #d5f5e3; }
.low-calorie { background-color: #eaf2f8; }
</style>
</head>
<body>
<h1>美食营养分析报告</h1>
<p>生成日期: `date()`</p>
<p>分析样本: `nutrition_data.length-1`道菜谱</p>
<div class="summary">
<h2>营养概览</h2>
<p>平均热量: `avg_calories` 千卡</p>
<p>平均蛋白质: `avg_protein` 克</p>
<p>最高蛋白质菜谱: `high_protein_dish`</p>
<p>低卡健康选择: `low_calorie_dish`</p>
</div>
<div class="table-container">
<h2>详细营养数据</h2>
<table>
<tr>
<th>菜名</th>
<th>烹饪时间(分钟)</th>
<th>难度</th>
<th>主要食材</th>
<th>热量(千卡)</th>
<th>蛋白质(克)</th>
<th>脂肪(克)</th>
<th>碳水化合物(克)</th>
</tr>
to reports/analysis_report.html
// 计算统计数据
total_calories = 0
total_protein = 0
high_protein = 0
high_protein_dish = ""
low_calorie = 9999
low_calorie_dish = ""
valid_count = 0
// 遍历数据计算统计值
for row from 1 to nutrition_data.length-1
js data_row = nutrition_data[`row`].split(',')
calories = data_row[4]
protein = data_row[5]
// 累加有效数据
if calories != "N/A" and protein != "N/A"
total_calories = total_calories + parseFloat(calories)
total_protein = total_protein + parseFloat(protein)
valid_count = valid_count + 1
// 跟踪高蛋白菜谱
if parseFloat(protein) > high_protein
high_protein = parseFloat(protein)
high_protein_dish = data_row[0]
end
// 跟踪低卡菜谱
if parseFloat(calories) < low_calorie
low_calorie = parseFloat(calories)
low_calorie_dish = data_row[0]
end
end
// 添加表格行数据
// 判断是否为高蛋白或低卡菜品,添加样式标记
if data_row[5] == high_protein
write <tr class="high-protein"> to reports/analysis_report.html
else if data_row[4] == low_calorie
write <tr class="low-calorie"> to reports/analysis_report.html
else
write <tr> to reports/analysis_report.html
end
// 写入表格内容
for col from 0 to data_row.length-1
write <td>`data_row[col]`</td> to reports/analysis_report.html
end
write </tr> to reports/analysis_report.html
end
// 计算平均值
avg_calories = total_calories / valid_count
avg_protein = total_protein / valid_count
// 更新报告中的统计数据
// 使用JavaScript替换占位符
js const fs = require('fs');
js let report = fs.readFileSync('reports/analysis_report.html', 'utf8');
js report = report.replace('`avg_calories`', `avg_calories`.toFixed(1));
js report = report.replace('`avg_protein`', `avg_protein`.toFixed(1));
js report = report.replace('`high_protein_dish`', '`high_protein_dish` ('`high_protein`'克)');
js report = report.replace('`low_calorie_dish`', '`low_calorie_dish` ('`low_calorie`'千卡)');
js fs.writeFileSync('reports/analysis_report.html', report);
// 完成报告HTML
write </table>
</div>
</body>
</html> to reports/analysis_report.html
echo 营养分析报告生成完成!
echo 报告路径:reports/analysis_report.html
自动化任务调度与扩展
设置定时运行
在Linux系统中,可通过crontab设置每周自动更新菜谱数据:
# 编辑crontab配置
crontab -e
# 添加以下行(每周日凌晨2点运行)
0 2 * * 0 /data/web/disk1/git_repo/gh_mirrors/ta/TagUI/src/tagui /data/web/disk1/git_repo/gh_mirrors/ta/TagUI/flows/recipe_crawler.tag -headless
功能扩展建议
- 多网站支持:通过参数化URL和选择器,扩展支持多个美食网站
- 图片采集:添加
snap命令保存菜谱图片,丰富数据集 - AI分类:集成营养分析API,自动识别食材类别和烹饪方式
- 异常处理:增加try-catch机制和错误日志,提高稳定性
常见问题与解决方案
页面加载缓慢导致数据抓取失败
解决方案:使用动态等待代替固定等待时间
// 不推荐:固定等待
wait 3 seconds
// 推荐:等待元素出现后再操作
timeout 10 // 设置最长等待10秒
wait for exist('//h1[@class="page-title"]')
网站结构变化导致选择器失效
解决方案:采用多属性组合定位,提高鲁棒性
// 脆弱的定位方式(容易因样式变化失效)
click //div[@class="recipe-card-2023"]
// 更健壮的定位方式(组合文本和属性)
click //div[contains(@class,"recipe-card") and contains(text(),"家常菜")]
反爬机制限制采集速度
解决方案:模拟人类操作节奏,添加随机等待
// 引入随机等待时间
js random_wait = Math.floor(Math.random() * 3) + 1; // 1-3秒随机等待
wait `random_wait` seconds
总结与下一步
通过本文介绍的方法,我们构建了一个完整的美食数据自动化采集与分析系统。这个方案不仅适用于菜谱数据,还可推广到其他需要重复操作的场景,如电商产品信息采集、新闻文章汇总、金融数据整理等。
下一步建议:
- 尝试使用TagUI的
live模式进行交互式开发,实时调试选择器 - 探索Excel集成功能,直接将数据导入电子表格进行分析
- 研究TagUI的OCR功能,处理图片中的菜谱信息
最后,分享一个完整的运行命令,一键启动从采集到报告的全流程:
# 全流程运行命令
tagui flows/recipe_crawler.tag && tagui flows/data_cleaner.tag && tagui flows/report_generator.tag -h
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



