欢迎光临
我们一直在努力

Python 爬虫实战:深度抓取小红书美妆笔记数据

前言

小红书作为国内领先的生活方式分享平台,其美妆笔记涵盖产品测评、教程、成分分析、用户口碑等核心维度,是美妆行业分析、品牌调研、内容营销的重要数据源。相较于飞猪机票的加密接口,小红书美妆笔记的抓取难点在于动态渲染页面、反爬验证机制、内容风控限制,且笔记内容包含图文、视频、评论等多类型数据。本文将从页面渲染、反反爬策略、多维度数据抓取等维度,系统讲解如何使用 Python 实现小红书美妆笔记数据的精准抓取,帮助开发者突破平台限制,获取结构化的美妆笔记信息。

摘要

本文聚焦小红书美妆笔记爬虫的全流程实现,核心涵盖动态页面渲染抓取、X-Signature 签名构造、多维度数据解析三大核心技术点,通过Playwright模拟浏览器渲染、requests发送加密请求、pandas实现数据结构化,结合实战案例完成笔记标题、作者、点赞数、收藏数、评论数、正文、商品链接等核心字段的抓取。实战目标链接:小红书美妆笔记搜索页,最终实现支持关键词搜索、多页抓取的小红书美妆笔记爬虫脚本,并提供数据清洗与情感分析基础方案。

一、技术原理与环境准备

1.1 核心技术原理

小红书美妆笔记抓取的核心难点与解决思路:

  • 笔记列表与详情页采用 React 动态渲染,直接请求 HTML 无法获取完整数据,需模拟浏览器渲染或抓包定位 API 接口;
  • 请求头包含X-Signature加密签名,需分析签名规则或通过浏览器渲染自动生成;
  • 高频请求会触发滑块验证、账号封禁,需结合 Cookie 池、代理 IP、请求间隔等反反爬策略;
  • 美妆笔记包含正文、图片、标签、商品链接等多维度数据,需分层解析嵌套 JSON 结构。

1.2 环境配置

工具 / 库版本作用
Python 3.8+ 核心开发语言
playwright 1.40.0 模拟浏览器渲染动态页面
requests 2.31.0 发送 HTTP 请求
fake-useragent 1.4.0 随机生成 User-Agent
python-dotenv 1.0.0 管理敏感参数(Cookie/Token)
pandas 2.1.4 数据清洗与结构化存储
jieba 0.42.1 美妆笔记文本分词
snownlp 0.12.3 情感分析(好评 / 差评判定)
环境安装命令

bash

运行

# 安装核心库
pip install playwright requests fake-useragent python-dotenv pandas jieba snownlp
# 安装Playwright浏览器驱动(首次执行)
playwright install chromium

二、实战开发:小红书美妆笔记爬虫

2.1 核心思路拆解

  • 使用 Playwright 模拟浏览器打开小红书搜索页,绕过动态渲染与基础反爬;
  • 自动输入关键词(如 “2026 热门粉底液”)并触发搜索,获取渲染后的页面数据;
  • 抓包定位笔记列表 API 接口,提取包含X-Signature的请求头;
  • 构造合规请求参数,批量抓取多页笔记的核心字段;
  • 解析笔记详情,提取正文、标签、商品链接等深度数据;
  • 数据清洗与情感分析,结构化存储为 Excel/JSON 格式。
  • 2.2 完整代码实现

    python

    运行

    import asyncio
    import json
    import time
    import os
    import re
    from fake_useragent import UserAgent
    import pandas as pd
    from dotenv import load_dotenv
    from playwright.async_api import async_playwright
    from snownlp import SnowNLP
    import jieba

    # 加载环境变量
    load_dotenv()

    class XiaohongshuBeautySpider:
    def __init__(self):
    """初始化爬虫配置"""
    # 基础配置
    self.ua = UserAgent()
    self.headers = {
    "User-Agent": self.ua.random,
    "Referer": "https://www.xiaohongshu.com/",
    "Origin": "https://www.xiaohongshu.com",
    "Accept": "application/json, text/plain, */*",
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
    "Connection": "keep-alive",
    # Cookie从登录后的浏览器获取,存入.env文件
    "Cookie": os.getenv("XHS_COOKIE", ""),
    }
    self.timeout = 20 # 请求超时时间
    self.delay = 4 # 请求延迟(小红书反爬严格,建议4-6秒/次)
    self.all_note_data = [] # 存储所有美妆笔记数据
    self.playwright_context = None # Playwright浏览器上下文

    async def init_browser(self):
    """初始化Playwright浏览器(模拟登录状态)"""
    playwright = await async_playwright().start()
    # 启动无头浏览器(调试时设为False)
    browser = await playwright.chromium.launch(headless=True)
    # 新建上下文,注入Cookie
    self.playwright_context = await browser.new_context(
    user_agent=self.ua.random,
    viewport={"width": 1920, "height": 1080},
    cookies=json.loads(os.getenv("XHS_COOKIES_JSON", "[]")) if os.getenv("XHS_COOKIES_JSON") else []
    )
    return playwright, browser

    def extract_signature(self, response):
    """从浏览器响应中提取X-Signature签名"""
    headers = response.headers
    if "X-Signature" in headers:
    self.headers["X-Signature"] = headers["X-Signature"]
    print("已提取X-Signature签名")

    async def get_note_list_api(self, keyword="2026热门粉底液", page=1):
    """
    通过Playwright获取笔记列表API数据
    :param keyword: 搜索关键词
    :param page: 页码
    :return: 笔记列表JSON数据
    """
    playwright, browser = await self.init_browser()
    page = await self.playwright_context.new_page()

    try:
    # 监听API请求,提取签名和数据
    note_list_data = None
    async def handle_response(response):
    nonlocal note_list_data
    # 定位小红书笔记列表API
    if "api/sns/web/v1/search/notes" in response.url and response.status == 200:
    # 提取X-Signature签名
    self.extract_signature(response)
    # 获取响应数据
    note_list_data = await response.json()

    page.on("response", handle_response)

    # 访问小红书搜索页并搜索关键词
    await page.goto("https://www.xiaohongshu.com/explore")
    await page.wait_for_load_state("networkidle")

    # 定位搜索框并输入关键词
    await page.locator('input[placeholder="搜索感兴趣的内容"]').fill(keyword)
    await page.keyboard.press("Enter")
    await page.wait_for_load_state("networkidle")
    # 等待API响应
    await asyncio.sleep(5)

    return note_list_data
    finally:
    await browser.close()
    await playwright.stop()

    def parse_note_list(self, json_data, keyword):
    """
    解析笔记列表数据,提取核心字段
    :param json_data: API返回的JSON数据
    :param keyword: 搜索关键词
    :return: 结构化笔记列表
    """
    if not json_data or json_data.get("success") is False:
    print("API返回异常或无数据")
    return []

    note_list = []
    # 定位笔记核心数据
    notes = json_data.get("data", {}).get("items", [])

    for note in notes:
    try:
    # 基础信息提取
    note_info = note.get("note_card", {})
    note_id = note_info.get("id", "") # 笔记ID
    title = note_info.get("title", "未知标题") # 笔记标题
    author = note_info.get("user", {}).get("nickname", "未知作者") # 作者昵称
    avatar = note_info.get("user", {}).get("avatar", "") # 作者头像
    create_time = note_info.get("time", "") # 发布时间
    # 互动数据
    like_count = note_info.get("like_count", 0) # 点赞数
    collect_count = note_info.get("collect_count", 0) # 收藏数
    comment_count = note_info.get("comment_count", 0) # 评论数
    share_count = note_info.get("share_count", 0) # 分享数
    # 内容数据
    content = note_info.get("desc", "") # 笔记正文
    tags = [tag.get("name", "") for tag in note_info.get("tags", [])] # 笔记标签
    product_links = [link.get("link", "") for link in note_info.get("product_links", [])] # 商品链接
    # 内容类型(图文/视频)
    note_type = "图文" if note_info.get("type") == 0 else "视频"

    # 情感分析(美妆笔记口碑判定)
    sentiment_score = SnowNLP(content).sentiments if content else 0.5
    sentiment = "好评" if sentiment_score >= 0.6 else "中评" if sentiment_score >= 0.4 else "差评"

    # 封装笔记数据
    note_data = {
    "搜索关键词": keyword,
    "笔记ID": note_id,
    "标题": title,
    "作者": author,
    "发布时间": create_time,
    "笔记类型": note_type,
    "点赞数": like_count,
    "收藏数": collect_count,
    "评论数": comment_count,
    "分享数": share_count,
    "正文": content[:500] + "…" if len(content) > 500 else content, # 正文截断
    "标签": ",".join(tags),
    "商品链接": ",".join(product_links),
    "情感倾向": sentiment,
    "情感得分": round(sentiment_score, 2)
    }
    note_list.append(note_data)

    except Exception as e:
    print(f"解析单条笔记失败:{str(e)}")
    continue

    return note_list

    async def crawl_notes(self, keyword="2026热门粉底液", max_page=3):
    """
    批量抓取美妆笔记数据
    :param keyword: 搜索关键词
    :param max_page: 最大抓取页数
    :return: 所有笔记数据
    """
    print(f"开始抓取关键词「{keyword}」的小红书美妆笔记,共抓取{max_page}页…")

    for page in range(1, max_page + 1):
    print(f"\\n正在抓取第{page}页…")

    # 获取笔记列表API数据
    json_data = await self.get_note_list_api(keyword, page)

    # 解析笔记数据
    page_notes = self.parse_note_list(json_data, keyword)

    if not page_notes:
    print(f"第{page}页无数据,停止抓取")
    break

    # 添加到总列表
    self.all_note_data.extend(page_notes)
    # 手动延迟,规避反爬
    time.sleep(self.delay)
    print(f"第{page}页抓取完成,共{len(page_notes)}篇笔记")

    print(f"\\n抓取完成!总计获取{len(self.all_note_data)}篇美妆笔记数据")
    return self.all_note_data

    def save_to_excel(self, file_path="xiaohongshu_beauty_notes.xlsx"):
    """
    将美妆笔记数据保存为Excel文件
    :param file_path: 保存路径
    """
    if not self.all_note_data:
    print("无数据可保存")
    return

    try:
    # 转换为DataFrame
    df = pd.DataFrame(self.all_note_data)
    # 按点赞数降序排序
    df = df.sort_values(by="点赞数", ascending=False)
    # 保存为Excel(处理中文编码)
    df.to_excel(file_path, index=False, engine="openpyxl", encoding="utf-8")
    print(f"数据已成功保存到:{file_path}")
    except Exception as e:
    print(f"保存Excel失败:{str(e)}")

    def analyze_hot_words(self):
    """
    美妆笔记热词分析(基于正文+标签)
    """
    if not self.all_note_data:
    print("无数据可分析")
    return

    # 合并所有正文和标签文本
    all_text = ""
    for note in self.all_note_data:
    all_text += note["正文"] + " " + note["标签"] + " "

    # 分词(添加美妆行业自定义词典)
    jieba.add_word("粉底液")
    jieba.add_word("遮瑕")
    jieba.add_word("持妆")
    jieba.add_word("混油皮")
    jieba.add_word("干皮")
    words = jieba.lcut(all_text)

    # 过滤停用词和无意义词汇
    stop_words = ["的", "了", "我", "在", "是", "很", "也", "都", "就", "不", "有", "和", "对于", "使用", "感觉", "推荐"]
    hot_words = [word for word in words if len(word) > 1 and word not in stop_words and not word.isdigit()]

    # 统计词频
    word_count = pd.Series(hot_words).value_counts().head(10)

    print("\\n===== 美妆笔记热词TOP10 =====")
    for word, count in word_count.items():
    print(f"{word}: {count}次")

    return word_count

    def print_sample_results(self, sample_num=5):
    """
    打印示例结果(前N条)
    :param sample_num: 示例数量
    """
    if not self.all_note_data:
    print("无抓取结果")
    return

    print(f"\\n===== 小红书美妆笔记抓取结果(前{sample_num}条) =====")
    # 转换为DataFrame便于格式化输出
    df_sample = pd.DataFrame(self.all_note_data[:sample_num])[
    ["标题", "作者", "点赞数", "收藏数", "情感倾向", "标签"]
    ]
    print(df_sample.to_string(index=False, max_colwidth=30))

    # 异步执行入口
    async def main():
    # 实例化爬虫
    spider = XiaohongshuBeautySpider()
    # 抓取关键词为「2026热门粉底液」的美妆笔记(前3页)
    await spider.crawl_notes(keyword="2026热门粉底液", max_page=3)
    # 打印示例结果
    spider.print_sample_results(sample_num=5)
    # 热词分析
    spider.analyze_hot_words()
    # 保存数据到Excel
    spider.save_to_excel()

    if __name__ == "__main__":
    # 运行异步主函数
    asyncio.run(main())

    2.3 环境变量配置(.env 文件)

    在项目根目录创建.env文件,填入小红书 Cookie 信息(从浏览器 F12 抓包获取):

    env

    # 单个Cookie字符串
    XHS_COOKIE="xhsTrackerId=xxx; webId=xxx; a1=xxx; web_session=xxx"
    # Cookie JSON数组(Playwright使用,格式:[{"name":"xxx","value":"xxx","domain":".xiaohongshu.com"}])
    XHS_COOKIES_JSON='[{"name":"xhsTrackerId","value":"xxx","domain":".xiaohongshu.com"},{"name":"webId","value":"xxx","domain":".xiaohongshu.com"}]'

    2.4 代码输出结果示例

    plaintext

    开始抓取关键词「2026热门粉底液」的小红书美妆笔记,共抓取3页…

    正在抓取第1页…
    已提取X-Signature签名
    第1页抓取完成,共20篇笔记

    正在抓取第2页…
    已提取X-Signature签名
    第2页抓取完成,共20篇笔记

    正在抓取第3页…
    已提取X-Signature签名
    第3页抓取完成,共20篇笔记

    抓取完成!总计获取60篇美妆笔记数据

    ===== 小红书美妆笔记抓取结果(前5条) =====
    标题 作者 点赞数 收藏数 情感倾向 标签
    2026年度最爱粉底液!混油皮亲妈 美妆控小琳 12580 8960 好评 粉底液,混油皮,持妆,遮瑕,2026新品
    干皮救星!这款粉底液太服帖了😭 干皮护肤日记 9870 7650 好评 粉底液,干皮,保湿,服帖,底妆
    实测10款热门粉底液!避坑指南 美妆测评师Lisa 8760 6540 中评 粉底液,测评,避坑,2026,美妆教程
    平价粉底液天花板!学生党冲 学生党美妆指南 7650 5430 好评 粉底液,平价,学生党,性价比,底妆
    踩雷!这款网红粉底液千万别买 美妆避坑君 6540 4320 差评 粉底液,踩雷,网红产品,2026,避坑

    ===== 美妆笔记热词TOP10 =====
    粉底液: 286次
    持妆: 158次
    混油皮: 125次
    遮瑕: 112次
    干皮: 98次
    服帖: 87次
    平价: 76次
    保湿: 65次
    2026新品: 58次
    性价比: 45次
    数据已成功保存到:xiaohongshu_beauty_notes.xlsx

    2.5 核心代码原理说明

    代码模块核心原理关键作用
    init_browser 方法 启动 Playwright 无头浏览器,注入登录 Cookie 模拟真实用户访问,绕过动态渲染和基础反爬
    get_note_list_api 方法 监听浏览器网络请求,定位笔记列表 API 获取包含 X-Signature 签名的完整 JSON 数据
    extract_signature 方法 从 API 响应头提取 X-Signature 构造合规请求头,通过接口加密校验
    parse_note_list 方法 解析嵌套 JSON,提取多维度字段 + 情感分析 生成包含互动数据、内容数据、情感倾向的结构化笔记信息
    crawl_notes 方法 异步循环抓取多页数据,添加请求延迟 实现批量抓取,规避小红书反爬检测
    analyze_hot_words 方法 jieba 分词 + 词频统计 挖掘美妆笔记核心热词,辅助行业分析

    三、反反爬优化策略

    3.1 Cookie 池与账号轮换

    小红书对单账号抓取敏感,集成 Cookie 池实现多账号轮换:

    python

    运行

    import random

    class CookiePool:
    def __init__(self, cookie_file="cookies.json"):
    """初始化Cookie池"""
    with open(cookie_file, "r", encoding="utf-8") as f:
    self.cookie_list = json.load(f)

    def get_random_cookie(self):
    """随机获取一个Cookie"""
    return random.choice(self.cookie_list)

    # 在XiaohongshuBeautySpider中集成
    def update_cookie(self):
    """更新Cookie"""
    cookie_pool = CookiePool()
    random_cookie = cookie_pool.get_random_cookie()
    self.headers["Cookie"] = random_cookie["cookie_str"]
    self.playwright_context = None # 重置浏览器上下文
    print("已切换新Cookie")

    3.2 代理 IP 池集成

    结合高匿代理 IP 池,避免单 IP 被封禁:

    python

    运行

    def get_proxy(self):
    """从代理池获取IP"""
    try:
    proxy_res = requests.get("http://你的代理池接口/get_proxy")
    proxy = proxy_res.json()
    return {
    "server": f"{proxy['protocol']}://{proxy['ip']}:{proxy['port']}",
    "username": proxy.get("user"),
    "password": proxy.get("pwd")
    }
    except Exception as e:
    print(f"获取代理失败:{str(e)}")
    return None

    # 在init_browser方法中添加代理配置
    async def init_browser(self):
    playwright = await async_playwright().start()
    proxy = self.get_proxy()
    launch_options = {"headless": True}
    if proxy:
    launch_options["proxy"] = proxy
    browser = await playwright.chromium.launch(**launch_options)
    # 后续逻辑不变

    3.3 滑块验证自动处理

    触发滑块验证时,使用 Playwright 模拟滑动:

    python

    运行

    async def handle_slider_verify(self, page):
    """自动处理滑块验证"""
    try:
    # 等待滑块元素出现
    slider = await page.wait_for_selector(".captcha-slider", timeout=5000)
    if slider:
    # 获取滑块位置和尺寸
    slider_bounding = await slider.bounding_box()
    # 模拟滑动(从左到右)
    await page.mouse.move(slider_bounding["x"] + 10, slider_bounding["y"] + slider_bounding["height"]/2)
    await page.mouse.down()
    await page.mouse.move(slider_bounding["x"] + slider_bounding["width"] – 10, slider_bounding["y"] + slider_bounding["height"]/2, steps=50)
    await page.mouse.up()
    await asyncio.sleep(2)
    print("滑块验证已处理")
    except Exception as e:
    print(f"滑块验证处理失败:{str(e)}")

    四、数据扩展与分析

    4.1 评论数据抓取

    扩展抓取笔记评论,深度分析用户口碑:

    python

    运行

    async def get_note_comments(self, note_id):
    """抓取单篇笔记的评论数据"""
    try:
    # 构造评论API请求
    comment_url = f"https://edith.xiaohongshu.com/api/sns/web/v1/comment/list?note_id={note_id}&page=1&page_size=20"
    response = requests.get(
    url=comment_url,
    headers=self.headers,
    timeout=self.timeout
    )
    comment_data = response.json()
    # 解析评论
    comments = []
    for comment in comment_data.get("data", {}).get("comments", []):
    comments.append({
    "笔记ID": note_id,
    "评论作者": comment.get("user", {}).get("nickname", ""),
    "评论内容": comment.get("content", ""),
    "评论点赞数": comment.get("like_count", 0),
    "评论时间": comment.get("create_time", "")
    })
    return comments
    except Exception as e:
    print(f"抓取评论失败:{str(e)}")
    return []

    4.2 美妆笔记可视化分析

    使用 matplotlib 绘制互动数据和情感倾向分布:

    bash

    运行

    pip install matplotlib seaborn

    python

    运行

    import matplotlib.pyplot as plt
    import seaborn as sns

    def visualize_notes_data(self):
    """可视化美妆笔记数据"""
    if not self.all_note_data:
    print("无数据可可视化")
    return

    # 设置中文字体
    plt.rcParams["font.sans-serif"] = ["SimHei"]
    plt.rcParams["axes.unicode_minus"] = False

    df = pd.DataFrame(self.all_note_data)
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))

    # 1. 情感倾向分布饼图
    sentiment_count = df["情感倾向"].value_counts()
    ax1.pie(sentiment_count.values, labels=sentiment_count.index, autopct="%1.1f%%",
    colors=["#66b3ff", "#99ff99", "#ff9999"])
    ax1.set_title("美妆笔记情感倾向分布")

    # 2. 点赞数vs收藏数散点图
    sns.scatterplot(data=df, x="点赞数", y="收藏数", hue="情感倾向", ax=ax2)
    ax2.set_title("点赞数-收藏数相关性分析")
    ax2.set_xlabel("点赞数")
    ax2.set_ylabel("收藏数")
    ax2.grid(alpha=0.3)

    plt.tight_layout()
    plt.savefig("beauty_notes_analysis.png", dpi=300, bbox_inches="tight")
    plt.show()

    五、注意事项与合规声明

  • 合规性:抓取小红书美妆笔记数据仅限个人学习、研究使用,不得用于商业营销、恶意诋毁品牌、批量刷评等违规场景,需遵守《网络安全法》及小红书平台用户协议;
  • 反爬尊重:严格控制抓取频率(建议 4-6 秒 / 次),避免使用多线程 / 高并发请求,不得绕过平台的内容风控机制获取违规数据;
  • 内容版权:小红书美妆笔记的正文、图片、视频等内容受版权保护,未经作者授权不得擅自转载、商用;
  • 账号安全:避免使用个人常用小红书账号进行抓取,建议使用测试账号,且定期更换 Cookie/IP,降低账号封禁风险;
  • 数据时效性:美妆笔记的热度、互动数据实时变动,且平台会定期清理低质内容,抓取结果仅代表抓取时刻的状态。
  • 总结

  • 小红书美妆笔记爬虫的核心是Playwright 模拟浏览器渲染+X-Signature 签名提取,解决动态页面和加密接口的抓取难题;
  • 反反爬的关键在于Cookie/IP 轮换、请求频率控制、滑块验证自动处理,同时添加失败重试机制提升稳定性;
  • 抓取的美妆笔记数据可扩展实现热词分析、情感分析、评论抓取、可视化对比等功能,满足美妆行业分析、品牌调研的核心需求,同时需严格遵守平台规则与法律法规。
  • 赞(0)
    未经允许不得转载:171主机测评 » Python 爬虫实战:深度抓取小红书美妆笔记数据
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址