影刀RPA实战案例:TEMU商品数据采集与上架状态自动化监控

系列文章覆盖了淘宝、拼多多、小红书、抖音等多个平台,但一直有一个平台没有专门讲过:TEMU(拼多多的跨境电商平台)。

TEMU在北美市场增长迅猛,很多做跨境电商的读者都在做TEMU的选品分析和价格监控。但TEMU的页面结构和国内拼多多不同——它是英文界面、反爬策略更强、商品状态(在售/下架/售罄)的变化也更频繁。

picture.image

今天专门讲TEMU的数据采集方案,以及一套商品状态自动化监控体系。


picture.image

先说TEMU采集的三个特殊点

| 特殊点 | 说明 | 应对策略 |

picture.image |--------|------|---------| | 英文界面 | 元素文本是英文,定位方式需调整 | 用英文关键词定位,而非中文 | | 价格单位 | USD(美元),格式为 $XX.XX | 清洗时保留美元符号或转为数字 | | 状态变化快 | 商品频繁在“在售/售罄/下架”间切换 | 专门做状态监控和变化检测 |

picture.image

第一部分:环境准备与登录态管理

TEMU的登录态管理跟拼多多国内版类似,但验证码触发机制更强。

picture.image

选型建议: 同样推荐用移动端浏览器环境m.temu.com),PC端限制较多。

影刀实操路径: 新建流程 → 浏览器类型选择“移动端(安卓)”

picture.image

# ============================================
# 打开TEMU并管理登录态
# ============================================


![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/5408bc66a535433daecc2ba541868965~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783559143&x-signature=2nwh2o0aW92psKaSmDbjwENoTo0%3D)
# 1. 打开TEMU移动端
打开网页("https://m.temu.com/", 打开方式="新建标签页")
等待(3秒)

# 2. 检测登录态

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/07ca2124a5ae4b569206e031c98e32e2~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783559143&x-signature=ulN8LT7hPnG2H8%2F4z%2B1uETLeWko%3D)
# 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
    
    输出日志("登录完成")
否则:
    输出日志("已登录")
    
![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/c6b0db3d486a4b16835f6dff52a44b2d~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783559143&x-signature=UflhpvK2K9ffCuSg%2BUk6U51wn2s%3D)
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']")
    
![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/ca2d4d7ef2c24951b45bbf243a260544~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783559143&x-signature=h1HUIiRXfyjjWORu4keo3lYHb68%3D)
    等待(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保存,减少登录频率
采集时段避开美国东部时间晚间高峰

常见问题速查

问题原因解决方法
价格采集为0XPath没定位到价格元素检查价格是否用"$"定位
状态检测不准"Sold Out"文本位置变化用contains模糊匹配
登录频繁弹验证码IP被标记换住宅代理
滚动加载触发不了滚动速度太快增加每次滚动后的等待时间
商品链接获取不到链接是相对路径拼接完整域名
商品ID提取失败URL结构变化用正则提取goods_id

推荐资源

  • TEMU移动端页面(m.temu.com)——建议用美国IP访问
  • 本系列第17篇《拼多多商品数据自动化采集》
  • 本系列第60篇《浏览器指纹、反爬策略与账号防封》
  • 本系列第65篇《抖音/TikTok商品数据采集》

#影刀RPA #RPA自动化 #TEMU #跨境电商 #数据采集 #价格监控 #状态监控

作者:林焱

本文为《影刀RPA学习手册》系列文章之一,内容源于实操经验的整理与分享。

0
0
0
0
评论
未登录
暂无评论