系列文章覆盖了淘宝、拼多多、小红书、抖音等多个平台,但一直有一个平台没有专门讲过:TEMU(拼多多的跨境电商平台)。
TEMU在北美市场增长迅猛,很多做跨境电商的读者都在做TEMU的选品分析和价格监控。但TEMU的页面结构和国内拼多多不同——它是英文界面、反爬策略更强、商品状态(在售/下架/售罄)的变化也更频繁。
今天专门讲TEMU的数据采集方案,以及一套商品状态自动化监控体系。
先说TEMU采集的三个特殊点
| 特殊点 | 说明 | 应对策略 |
|--------|------|---------|
| 英文界面 | 元素文本是英文,定位方式需调整 | 用英文关键词定位,而非中文 |
| 价格单位 | USD(美元),格式为
$XX.XX | 清洗时保留美元符号或转为数字 |
| 状态变化快 | 商品频繁在“在售/售罄/下架”间切换 | 专门做状态监控和变化检测 |
第一部分:环境准备与登录态管理
TEMU的登录态管理跟拼多多国内版类似,但验证码触发机制更强。
选型建议: 同样推荐用移动端浏览器环境(m.temu.com),PC端限制较多。
影刀实操路径: 新建流程 → 浏览器类型选择“移动端(安卓)”
# ============================================
# 打开TEMU并管理登录态
# ============================================

# 1. 打开TEMU移动端
打开网页("https://m.temu.com/", 打开方式="新建标签页")
等待(3秒)
# 2. 检测登录态

# TEMU登录态标志:页面顶部是否有"Account"或"Sign Out"
登录标志 = "//*[contains(text(),'Account') or contains(text(),'Sign Out')]"
如果 判断元素存在(登录标志) == False:
输出日志("未登录,开始登录流程")
# 点击"Sign In"按钮
点击元素("//*[contains(text(),'Sign In')]")
等待(2秒)
# 输入账号(用placeholder定位)
输入文本("//input[@placeholder='Email or phone number']", "your_email@example.com")
输入文本("//input[@type='password']", "your_password")
# 点击登录
点击元素("//*[contains(text(),'Sign In')]")
等待(3秒)
# 处理可能出现的验证码
If 判断元素存在("//*[contains(text(),'verification')]") == True:
输出日志("检测到验证码,请手动处理")
等待(30) # 预留手动处理时间
EndIf
输出日志("登录完成")
否则:
输出日志("已登录")

EndIf
第二部分:商品数据采集(搜索+列表+详情)
2.1 搜索商品列表
TEMU的搜索框定位和结果列表采集:
# ============================================
# 搜索关键词并获取商品列表
# ============================================
# 读取关键词列表
读取Excel文件("C:/data/temu_keywords.xlsx", 输出变量=keywords_data)
设置变量(全部商品数据 = [])
按列表循环(keywords_data[1:], 循环项=keyword_row):
keyword = keyword_row[0]
输出日志("===== 搜索关键词:" + keyword + " =====")
# ----- 1. 执行搜索 -----
# 点击搜索框(用placeholder定位)
点击元素("//input[@placeholder='Search']")

等待(1秒)
# 输入关键词
输入文本("//input[@placeholder='Search']", keyword)
等待(0.5秒)
# 点击搜索按钮(用文本定位)
点击元素("//*[contains(text(),'Search')]")
等待(3秒)
# ----- 2. 获取商品列表 -----
# TEMU的商品卡片特征:包含价格($)和图片
# 注意:class是动态的,用属性和结构定位
获取相似元素列表("//div[descendant::*[contains(text(),'$')] and descendant::img]", 存入列表=商品卡片)
输出日志("找到 " + 获取列表长度(商品卡片) + " 个商品")
# ----- 3. 采集每个商品 -----
按列表循环(商品卡片, 循环项=卡片):
Try:
商品数据 = 采集单个TEMU商品(卡片)
将 商品数据 加入列表(全部商品数据)
Catch:
输出日志("单个商品采集失败,跳过")
继续循环
EndTry
结束循环
# ----- 4. 滚动加载更多(TEMU是无限滚动)-----
调用子流程(滚动加载更多TEMU商品)
输出日志("关键词 " + keyword + " 采集完成")
等待(随机数(3, 6)) # 关键词切换间隔
结束循环
输出日志("全部采集完成,共 " + 获取列表长度(全部商品数据) + " 个商品")
2.2 采集单个商品(避开动态class)
# ============================================
# 子流程:采集单个TEMU商品
# 输入:商品卡片元素
# 输出:商品数据字典
# ============================================
Try:
# ----- 1. 商品名 -----
# TEMU商品名通常较长,在特定位置
Try:
商品名 = 获取元素文本(卡片 + "//*[string-length(text()) > 10]")
# 过滤掉短文本(如"$"、"Add to cart"等)
如果 获取文本长度(商品名) < 5:
商品名 = "未知商品"
结束If
Catch:
商品名 = "未知商品"
EndTry
# ----- 2. 价格(美元)-----
# 价格通常包含"$"符号
Try:
价格文本 = 获取元素文本(卡片 + "//*[contains(text(),'$')]")
# 从"$XX.XX"中提取数字
价格 = 提取数字(价格文本)
Catch:
价格 = 0
EndTry
# ----- 3. 销量/评价数 -----
# TEMU通常显示"XXX sold"或"XXX reviews"
Try:
销量文本 = 获取元素文本(卡片 + "//*[contains(text(),'sold') or contains(text(),'reviews')]")
销量 = 提取数字(销量文本)
Catch:
销量 = 0
EndTry
# ----- 4. 商品状态(在售/售罄)-----
# 检查是否有"Out of Stock"或"Sold Out"标签
如果 判断元素存在(卡片 + "//*[contains(text(),'Out of Stock') or contains(text(),'Sold Out')]") == True:
状态 = "售罄"
否则:
状态 = "在售"
结束If
# ----- 5. 商品链接 -----
Try:
商品链接 = 获取元素属性(卡片 + "//a[1]", "href")
如果 商品链接 不以 "http" 开头:
商品链接 = "https://m.temu.com" + 商品链接
结束If
Catch:
商品链接 = ""
EndTry
# ----- 6. 商品ID(从URL中提取)-----
商品ID = 提取URL参数(商品链接, "goods_id")
# ----- 7. 组装数据 -----
商品数据 = {
"商品名": 商品名,
"价格USD": 价格,
"销量": 销量,
"状态": 状态,
"链接": 商品链接,
"商品ID": 商品ID,
"采集时间": 获取当前时间()
}
返回结果(商品数据)
Catch:
输出日志("单商品采集失败")
返回结果(商品数据={})
EndTry
2.3 无限滚动处理
# ============================================
# 子流程:滚动加载更多TEMU商品
# ============================================
设置变量(连续无新增 = 0)
设置变量(上次数量 = 0)
设置变量(最大滚动次数 = 30)
条件循环(连续无新增 < 3 且 当前滚动次数 < 最大滚动次数):
当前滚动次数 = 当前滚动次数 + 1
# 获取当前所有商品卡片
获取相似元素列表("//div[descendant::*[contains(text(),'$')]]", 存入列表=当前卡片)
当前数量 = 获取列表长度(当前卡片)
如果 当前数量 > 上次数量:
输出日志("新增 " + (当前数量 - 上次数量) + " 个商品")
设置变量(连续无新增 = 0)
设置变量(上次数量 = 当前数量)
# 只采集新增的商品
设置变量(起始索引 = 上次数量)
按次数循环(当前数量 - 起始索引):
当前索引 = 起始索引 + 循环索引
Try:
商品数据 = 采集单个TEMU商品(当前卡片[当前索引])
将 商品数据 加入列表(全部商品数据)
Catch:
继续循环
EndTry
结束循环
否则:
设置变量(连续无新增 = 连续无新增 + 1)
输出日志("无新增,连续第 " + 连续无新增 + " 次")
结束If
# 滚动到底部
执行JavaScript("window.scrollTo(0, document.body.scrollHeight);")
等待(随机数(1.5, 3))
结束条件循环
输出日志("滚动加载完成,共 " + 获取列表长度(全部商品数据) + " 个商品")
第三部分:商品状态监控(核心功能)
TEMU商品状态变化频繁,价格波动也大。监控的核心是每天定时采集,对比前一天的状态和价格。
3.1 状态监控配置
# ============================================
# 商品状态监控配置
# ============================================
# 监控商品列表(从Excel读取)
读取Excel文件("配置/监控商品列表.xlsx", 输出变量=监控列表)
# 结构:商品ID | 商品名 | 关注的URL | 上次状态 | 上次价格
设置变量(状态变化列表 = [])
设置变量(价格变化列表 = [])
按列表循环(监控列表[1:], 循环项=商品配置):
商品ID = 商品配置[0]
商品名 = 商品配置[1]
商品URL = 商品配置[2]
上次状态 = 商品配置[3]
上次价格 = 商品配置[4]
# ----- 1. 采集当前状态 -----
Try:
打开网页(商品URL, 打开方式="新建标签页")
等待(3秒)
# 检测状态
如果 判断元素存在("//*[contains(text(),'Out of Stock') or contains(text(),'Sold Out')]") == True:
当前状态 = "售罄"
否则:
当前状态 = "在售"
结束If
# 采集当前价格
价格文本 = 获取元素文本("//*[contains(text(),'$')]")
当前价格 = 提取数字(价格文本)
# ----- 2. 状态变化检测 -----
如果 当前状态 != 上次状态:
状态变化列表.append({
"商品名": 商品名,
"商品ID": 商品ID,
"原状态": 上次状态,
"新状态": 当前状态,
"时间": 获取当前时间()
})
输出日志("【状态变化】" + 商品名 + ":" + 上次状态 + " → " + 当前状态)
结束If
# ----- 3. 价格变化检测(阈值5%)-----
如果 上次价格 > 0:
变化率 = abs(当前价格 - 上次价格) / 上次价格 * 100
如果 变化率 > 5:
价格变化列表.append({
"商品名": 商品名,
"商品ID": 商品ID,
"原价": 上次价格,
"现价": 当前价格,
"变化率": round(变化率, 1),
"时间": 获取当前时间()
})
输出日志("【价格变化】" + 商品名 + ":" + 上次价格 + " → " + 当前价格 + "(" + 变化率 + "%)")
结束If
结束If
# ----- 4. 更新配置表(写入最新状态和价格)-----
调用子流程(更新监控商品状态,
输入: 商品ID=商品ID, 新状态=当前状态, 新价格=当前价格
)
Catch:
输出日志("监控 " + 商品名 + " 失败")
截图保存("监控失败_" + 商品名 + ".png")
继续循环
Finally:
关闭标签页()
等待(随机数(1, 2))
EndTry
结束循环
3.2 状态变化告警
# ============================================
# 状态变化告警推送
# ============================================
# 构建告警消息
告警消息 = "📊 **TEMU商品状态监控报告**\n\n"
# 状态变化部分
如果 获取列表长度(状态变化列表) > 0:
告警消息 = 告警消息 + "**状态变化(" + 获取列表长度(状态变化列表) + "个)**\n"
按列表循环(状态变化列表, 循环项=变化):
告警消息 = 告警消息 + "• " + 变化["商品名"] + ":" + 变化["原状态"] + " → **" + 变化["新状态"] + "**\n"
结束循环
否则:
告警消息 = 告警消息 + "✅ 无状态变化\n"
结束If
# 价格变化部分
如果 获取列表长度(价格变化列表) > 0:
告警消息 = 告警消息 + "\n**价格变化(" + 获取列表长度(价格变化列表) + "个)**\n"
按列表循环(价格变化列表, 循环项=变化):
方向 = "↑" if 变化["现价"] > 变化["原价"] else "↓"
告警消息 = 告警消息 + "• " + 变化["商品名"] + ":" + 变化["原价"] + " → " + 变化["现价"] + "(" + 方向 + 变化["变化率"] + "%)\n"
结束循环
否则:
告警消息 = 告警消息 + "\n✅ 无价格变化\n"
结束If
告警消息 = 告警消息 + "\n🕐 更新时间:" + 获取当前时间()
# 发送到企业微信/飞书
如果 获取列表长度(状态变化列表) > 0 或 获取列表长度(价格变化列表) > 0:
调用子流程(Send_Notification,
输入:
msg_type="warning" if 获取列表长度(状态变化列表) > 0 else "info",
content=告警消息,
mention_all=获取列表长度(状态变化列表) > 0 # 有状态变化才@所有人
)
否则:
输出日志("无变化,无需告警")
结束If
第四部分:价格清洗(TEMU美元价格)
TEMU的价格显示为美元格式,需要清洗成纯数字以便分析。
# ============================================
# TEMU价格清洗(Python)
# ============================================
import pandas as pd
import re
import json
def clean_temu_price(text):
"""
清洗TEMU美元价格
输入:"$29.99" → 29.99
输入:"$29.99-$49.99" → [29.99, 49.99]
输入:"From $29.99" → 29.99
"""
if pd.isna(text):
return None
text = str(text)
# 提取所有美元数字
nums = re.findall(r'\$(\d+\.?\d*)', text)
if not nums:
return None
# 如果是区间价,取最低价
prices = [float(n) for n in nums]
return min(prices) # 取最低价作为参考价
# 应用清洗
df['价格USD_清洗'] = df['价格USD'].apply(clean_temu_price)
# 去除异常值(TEMU商品通常在$1-$200之间)
df = df[(df['价格USD_清洗'] >= 1) & (df['价格USD_清洗'] <= 200)]
第五部分:完整主流程
# ============================================
# 主流程:TEMU_Monitor
# 执行频率:每天3次(8:00 / 14:00 / 20:00)
# ============================================
输出日志("===== TEMU监控系统启动 =====")
开始时间 = 获取当前时间()
# ----- 1. 检查登录态 -----
调用子流程(Check_Login_Status)
# ----- 2. 采集监控商品数据 -----
输出日志("【1/3】采集监控商品数据")
调用子流程(采集监控商品, 输出=监控商品数据)
# ----- 3. 检测状态和价格变化 -----
输出日志("【2/3】检测变化")
调用子流程(Detect_Changes,
输入: 监控商品数据,
输出: 状态变化列表, 价格变化列表
)
# ----- 4. 发送告警 -----
输出日志("【3/3】发送通知")
调用子流程(Send_Alert,
输入: 状态变化列表, 价格变化列表
)
# ----- 5. 保存历史数据 -----
调用子流程(Save_History,
输入: 监控商品数据, 当前日期
)
结束时间 = 获取当前时间()
总耗时 = 计算时间差(开始时间, 结束时间)
输出日志("===== TEMU监控完成,耗时 " + 总耗时 + " 秒 =====")
防限流策略(TEMU特供版)
| 策略 | 说明 |
|---|---|
| 每次操作间隔 | 2-4秒(比国内电商更慢) |
| 关键词切换间隔 | 5-10秒 |
| 单次采集量 | <300商品/次 |
| IP策略 | 强烈建议使用住宅代理 |
| 登录态 | Profile保存,减少登录频率 |
| 采集时段 | 避开美国东部时间晚间高峰 |
常见问题速查
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 价格采集为0 | XPath没定位到价格元素 | 检查价格是否用"$"定位 |
| 状态检测不准 | "Sold Out"文本位置变化 | 用contains模糊匹配 |
| 登录频繁弹验证码 | IP被标记 | 换住宅代理 |
| 滚动加载触发不了 | 滚动速度太快 | 增加每次滚动后的等待时间 |
| 商品链接获取不到 | 链接是相对路径 | 拼接完整域名 |
| 商品ID提取失败 | URL结构变化 | 用正则提取goods_id |
推荐资源
- TEMU移动端页面(m.temu.com)——建议用美国IP访问
- 本系列第17篇《拼多多商品数据自动化采集》
- 本系列第60篇《浏览器指纹、反爬策略与账号防封》
- 本系列第65篇《抖音/TikTok商品数据采集》
#影刀RPA #RPA自动化 #TEMU #跨境电商 #数据采集 #价格监控 #状态监控
作者:林焱
本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。
