影刀RPA实战案例:小红书笔记批量采集与评论分析

小红书是内容驱动的平台,用户真实评价的价值远高于商品详情页的官方介绍。 很多做电商的人都在爬小红书笔记做竞品分析、口碑调研。

小红书的采集有两个特殊难点:无限滚动加载动态class(类名随机变化)。处理不好,翻页到一半就卡死。今天用一套完整的采集方案把这两个问题一并解决。

picture.image


先说这个流程能做什么

picture.image

输入: 一个关键词列表(如“蓝牙耳机”“充电宝”) 输出:

  • 笔记清单(标题、点赞数、收藏数、作者、发布时间)
  • 每条笔记的评论(评论内容、评论者、点赞数、时间)

picture.image

适用场景: 竞品口碑分析、用户需求挖掘、选品灵感收集。


picture.image

核心难点与应对策略

难点原因应对方案
无限滚动下拉到底部自动加载更多,没有传统翻页按钮条件循环 + 连续无新增检测

picture.image | class动态变化 | 小红书前端用随机生成的class名,每次刷新都变 | 避开class,用属性+结构定位 | | 登录态限制 | 未登录只能看少量内容 | 用手机号登录,Cookie保存在Profile里 | | 反爬限流 | 请求频率过高会弹出验证 | 随机延迟 + 关键词切换间隔 |


picture.image

第一步:环境与登录

选型建议: 小红书PC端限制较多,移动端页面(手机或模拟器)更友好

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

# ============================================
# 打开小红书移动端并登录

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/24eba702743a4400b2b350603e997a86~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783192725&x-signature=K39h%2BOODkoZc0UI5bAsFkse0Qvo%3D)
# ============================================

# 1. 打开小红书移动端首页
打开网页("https://www.xiaohongshu.com/", 打开方式="新建标签页")
等待(3秒)

# 2. 检测登录态(用“关注”按钮是否存在来判断)
# 注意:不能用class,用属性或文本
登录标志 = "//*[contains(text(),'关注')]"

如果 判断元素是否存在(登录标志) == False:
    输出日志("未登录,开始登录流程")
    
    # 点击"我的"(用文本定位,不用class)
    点击元素("//*[contains(text(),'我的')]")
    等待(2秒)
    
    # 点击登录按钮
    点击元素("//*[contains(text(),'登录')]")
    等待(2秒)
    
    # 选择手机号登录
    点击元素("//*[contains(text(),'手机号')]")
    等待(1秒)
    
    # 输入手机号(用placeholder属性定位)
    输入文本("//input[@placeholder='手机号']", "13800138000")
    
    # 点击获取验证码
    
![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/78c1a300d80c47c4b34888e84ed0af87~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783192725&x-signature=Mqx9jP9b92%2BEHGXgEGTHxwE4WcQ%3D)
    点击元素("//*[contains(text(),'获取验证码')]")
    # 等待验证码短信,手动输入
    输出日志("请查看手机验证码,在弹窗中输入")
    等待(30秒)  # 预留输入时间
    
    # 点击登录确认
    点击元素("//*[contains(text(),'登录')]")
    等待(3秒)
    
    输出日志("登录完成")
否则:
    输出日志("已登录,跳过登录步骤")
结束如果

第二步:搜索关键词与处理无限滚动

核心思路: 搜索后通过“连续滚动的次数”来判断是否已经加载完毕。

# ============================================
# 搜索关键词并滚动加载笔记列表
# ============================================

# 读取关键词列表

![picture.image](https://p6-volc-community-sign.byteimg.com/tos-cn-i-tlddhu82om/40ae5f269fad4a4693c0e86e8d27a5e9~tplv-tlddhu82om-image.image?=&rk3s=8031ce6d&x-expires=1783192725&x-signature=6pB8vRzyuHZcAaZuY%2B1QvHsqKF4%3D)
读取Excel文件("C:/data/keywords.xlsx", 输出变量=keywords_data)
关键词列表 = keywords_data[1:]  # 从第二行开始

设置变量(全部笔记列表 = [])

按列表循环(关键词列表, 循环项=keyword_row):
    keyword = keyword_row[0]  # 第一列是关键词
    输出日志("===== 开始采集关键词:" + keyword + " =====")

    # ----- 1. 执行搜索 -----
    # 点击搜索框(用placeholder或文本定位)
    点击元素("//input[@placeholder='搜索']")
    等待(1秒)
    
    # 输入关键词
    输入文本("//input[@placeholder='搜索']", keyword)
    等待(0.5秒)
    
    # 点击搜索按钮(用文本定位)
    点击元素("//*[contains(text(),'搜索')]")
    等待(3秒)  # 等待搜索结果加载

    # ----- 2. 滚动加载更多笔记 -----
    # 小红书是无限滚动,没有“下一页”按钮
    # 策略:持续滚动直到连续3次滚动都没有新增内容
    
    设置变量(连续无新增 = 0)
    设置变量(上一次数量 = 0)
    设置变量(当前页笔记数 = 0)
    
    # 用于存储当前关键词的笔记
    设置变量(本关键词笔记列表 = [])
    
    条件循环(连续无新增 < 3):
        # 获取当前页面的所有笔记卡片
        # 注意:用属性定位,不用动态class
        获取相似元素列表("//section[contains(@class,'note-item') or contains(@class,'feed-item')]", 存入列表=当前笔记卡片)
        当前数量 = 获取列表长度(当前笔记卡片)
        
        输出日志("当前已加载 " + 当前数量 + " 条笔记")
        
        如果 当前数量 > 上一次数量:
            # 有新增内容:采集新增的部分
            输出日志("检测到新增 " + (当前数量 - 上一次数量) + " 条笔记")
            设置变量(连续无新增 = 0)
            设置变量(上一次数量 = 当前数量)
            
            # 采集笔记数据(增量采集,只采新增的)
            # 用“获取相似元素列表”配合索引范围只取新增部分
            获取相似元素列表("//section[contains(@class,'note-item')]", 存入列表=全部卡片)
            
            # 从上次数量开始循环
            设置变量(起始索引 = 上次采集数量)
            按次数循环(当前数量 - 起始索引):
                当前索引 = 起始索引 + 循环索引
                Try:
                    单条笔记 = 采集单条笔记(全部卡片[当前索引])
                    将 单条笔记 加入列表(本关键词笔记列表)
                Catch:
                    输出日志("第 " + 当前索引 + " 条笔记采集失败,跳过")
                    继续循环
                EndTry
            结束循环
            
            设置变量(上次采集数量 = 当前数量)
            
        否则:
            # 无新增
            设置变量(连续无新增 = 连续无新增 + 1)
            输出日志("无新增内容,连续第 " + 连续无新增 + " 次")
        结束如果
        
        # 滚动到页面底部(触发加载)
        Try:
            # 滚动到页面底部最后可见的元素
            滚动到元素("//footer")  # 或者滚动到页面最后
        Catch:
            输出日志("滚动到底部失败,尝试用JS滚动")
            执行JavaScript("window.scrollTo(0, document.body.scrollHeight);")
        EndTry
        
        等待(随机数(2, 4))  # 等待加载
        
   结束条件循环
   
   输出日志("关键词 " + keyword + " 滚动结束,共采集 " + 获取列表长度(本关键词笔记列表) + " 条")
   
   # 将当前关键词的数据合并到总列表
   将 本关键词笔记列表 合并到 全部笔记列表
   
   # 切换关键词前重置搜索
   点击元素("//*[contains(text(),'取消') or contains(text(),'×')]")
   等待(2秒)
   
   # 关键词间休息
   等待(随机数(3, 6))

结束循环

输出日志("全部采集完成,共 " + 获取列表长度(全部笔记列表) + " 条")

第三步:采集单条笔记(笔记列表页获取基本信息)

在列表页直接获取笔记的基本信息(标题、作者、互动数据),不需要点进详情页。

# ============================================
# 子流程:采集单条笔记(列表页)
# ============================================
# 输入参数:note_card(笔记卡片元素)
# 输出参数:note_data(笔记数据字典)

Try:
    # ----- 1. 采集标题 -----
    # 标题通常在一个a标签或span里,用文本定位
    Try:
        # 方法1:通过链接文本
        标题 = 获取元素文本(note_card + "//a[contains(@class,'title')]")
    Catch:
        # 方法2:通过包含文本的通用定位
        标题 = 获取元素文本(note_card + "//*[contains(@class,'title')]")
    EndTry
    
    如果 标题 == "" 或 标题 == None:
        标题 = "未获取到标题"
    EndIf

    # ----- 2. 采集作者名 -----
    Try:
        作者名 = 获取元素文本(note_card + "//span[contains(@class,'author')]")
    Catch:
        作者名 = "未知作者"
    EndTry

    # ----- 3. 采集点赞数 -----
    Try:
        # 点赞数通常在带❤️图标的span里
        点赞数文本 = 获取元素文本(note_card + "//span[contains(@class,'like')]")
        点赞数 = 提取数字(点赞数文本)  # "1.2万" → 12000
    Catch:
        点赞数 = 0
    EndTry

    # ----- 4. 采集收藏数 -----
    Try:
        收藏数文本 = 获取元素文本(note_card + "//span[contains(@class,'collect')]")
        收藏数 = 提取数字(收藏数文本)
    Catch:
        收藏数 = 0
    EndTry

    # ----- 5. 采集笔记链接(用于后续评论采集)-----
    Try:
        笔记链接 = 获取元素属性(note_card + "//a[contains(@class,'title')]", "href")
        # 拼接完整链接
        如果 笔记链接.startswith("/"):
            笔记链接 = "https://www.xiaohongshu.com" + 笔记链接
        结束如果
    Catch:
        笔记链接 = ""
    EndTry

    # ----- 6. 组装数据 -----
    笔记数据 = {
        "标题": 标题,
        "作者": 作者名,
        "点赞数": 点赞数,
        "收藏数": 收藏数,
        "链接": 笔记链接,
        "采集时间": 获取当前时间()
    }
    
    返回结果(note_data=笔记数据)
    
Catch:
    输出日志("单条笔记采集失败")
    返回结果(note_data={})
EndTry

第四步:采集评论(进入笔记详情页)

只采集互动量高的笔记的评论(点赞数>100),避免无效数据。

# ============================================
# 子流程:采集笔记评论
# ============================================
# 输入参数:note_url(笔记链接), max_comments(最大评论数)
# 输出参数:comments_list(评论列表)

# 1. 打开笔记详情页
打开网页(note_url, 打开方式="新建标签页")
等待(3秒)

# 2. 滚动加载评论(小红书评论也是无限滚动)
设置变量(评论列表 = [])
设置变量(连续无新增 = 0)
设置变量(上次评论数 = 0)

条件循环(连续无新增 < 3 且 获取列表长度(评论列表) < max_comments):
    # 获取当前所有评论
    获取相似元素列表("//div[contains(@class,'comment-item')]", 存入列表=当前评论列表)
    当前数量 = 获取列表长度(当前评论列表)
    
    如果 当前数量 > 上次评论数:
        设置变量(连续无新增 = 0)
        设置变量(上次评论数 = 当前数量)
        
        # 只采集新增的评论(从上次采集的位置开始)
        设置变量(起始索引 = 获取列表长度(评论列表))
        按次数循环(当前数量 - 起始索引):
            当前索引 = 起始索引 + 循环索引
            Try:
                item = 当前评论列表[当前索引]
                
                # 采集评论内容
                评论内容 = 获取元素文本(item + "//span[contains(@class,'content')]")
                
                # 采集评论者
                评论者 = 获取元素文本(item + "//span[contains(@class,'name')]")
                
                # 采集评论点赞数
                Try:
                    评论点赞 = 提取数字(获取元素文本(item + "//span[contains(@class,'like')]"))
                Catch:
                    评论点赞 = 0
                EndTry
                
                一行评论 = [评论内容, 评论者, 评论点赞, 获取当前时间()]
                将 一行评论 加入列表(评论列表)
                
            Catch:
                输出日志("单条评论采集失败")
                继续循环
            EndTry
        结束循环
    否则:
        设置变量(连续无新增 = 连续无新增 + 1)
    结束如果
    
    # 滚动加载更多评论
    滚动到元素("//div[contains(@class,'comment-list')]//div[last()]")
    等待(随机数(1.5, 3))

结束条件循环

输出日志("采集到 " + 获取列表长度(评论列表) + " 条评论")

关闭标签页()
返回结果(comments_list=评论列表)

第五步:数据处理与清洗

# Python清洗:去重、字数统计、情感标记
import pandas as pd
import re

# 输入:raw_notes(笔记列表), raw_comments(评论列表)

# 1. 笔记去重(按标题)
notes_df = pd.DataFrame(raw_notes)
notes_df = notes_df.drop_duplicates(subset=['标题'], keep='first')

# 2. 评论去重
comments_df = pd.DataFrame(raw_comments)
comments_df = comments_df.drop_duplicates(subset=['评论内容'], keep='first')

# 3. 评论长度分析
comments_df['评论长度'] = comments_df['评论内容'].str.len()

# 4. 简单情感标记(包含特定关键词)
好评词 = ['好用', '推荐', '满意', '不错', '值得']
差评词 = ['不好', '差', '失望', '垃圾', '后悔']

def 情感分析(text):
    if pd.isna(text):
        return '中性'
    text = str(text)
    if any(word in text for word in 好评词):
        return '正面'
    if any(word in text for word in 差评词):
        return '负面'
    return '中性'

comments_df['情感'] = comments_df['评论内容'].apply(情感分析)

# 5. 输出
cleaned_notes = notes_df.to_dict('records')
cleaned_comments = comments_df.to_dict('records')

限流防护策略

策略具体做法
随机延迟每次操作后等待0.5-3秒随机时间
关键词切换间隔每个关键词采集完等待5-10秒
评论采集限制每篇笔记最多采50条评论
总采集量控制单次流程不超过500篇笔记
登录态保持Cookie保存在Profile中,减少重复登录

常见问题速查

问题原因解决方法
滚动加载不到新内容已加载全部或网络慢增加单次等待时间到3-5秒
class定位不到元素class动态生成改用文本/属性定位
验证码频繁弹出请求频率过高增加延迟,切换关键词间隔拉长
登录态失效Cookie过期重新登录,或提前用手机扫码
笔记链接采集不到链接是相对路径拼完整域名

推荐资源

  • 小红书移动端页面——用Chrome开发者工具的移动端模拟模式调试
  • 影刀官方《小红书采集模板》——流程市场可以下载参考
  • 我的小红书XPath库:维护了一份常用定位表达式(全部用文本和属性,不用class)

#影刀RPA #RPA自动化 #小红书 #内容采集 #数据采集

作者:林焱

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

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