使用Python爬取小红书笔记与评论(仅供学习与参考)

本文介绍了一种无需编程背景也可实现的小红书数据抓取方法,通过JS注入获取加密参数,配合Python爬虫技术抓取笔记与评论数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【🏠作者主页】:吴秋霖
【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作!
【🌟作者推荐】:对爬虫领域以及JS逆向分析感兴趣的朋友可以关注《爬虫JS逆向实战》《深耕爬虫领域》
未来作者会持续更新所用到、学到、看到的技术知识!包括但不限于:各类验证码突防、爬虫APP与JS逆向分析、RPA自动化、分布式爬虫、Python领域等相关文章

作者声明:文章仅供学习交流与参考!严禁用于任何商业与非法用途!否则由此产生的一切后果均与作者无关!如有侵权,请联系作者本人进行删除!

1. 写在前面

  目前很多小伙伴可能或多或少都需要一些基础的笔记或者评论数据进行分析!有的可能想要通过一些关键词进行特定领域话题、笔记内容的搜索。有的则希望监测某篇笔记下面新增的评论内容或者某篇笔记下热度数据指标。从技术角度来说,在作者之前的文章中我们除了通过对x-sx-s-common参数进行逆向分析还原外加密算法外,其实还可以通过JS注入免扣加密算法的RPC方案去获取到加密后的参数值,再通过请求获取到数据

但是,不管哪一种方案对于没有爬虫以及编程经验的人来说无疑是有一定难度与门槛的!

所以,在此前的一段时间内。作者也曾针对上面提到的两种方案分别都尝试进行了验证,比如设定好关键词又或是放置笔记链接来运行程序帮助我们获取到相关的数据:

  • 关键词搜索
  • 作品列表获取
  • 笔记详情获取
  • 评论内容获取

如果你是一名开发者且有爬虫及逆向的相关经验。可以参考作者之前所分享的文章,相关参数加密分析、Python与JS的纯算法、密钥信息如何定位都在文章内:x-s、x-s-common加密纯算法还原

当然,还有一种纯自动化的方案。对于一些仅有Py基础的小伙伴也是适用的,上手成本很低

2. 分析加密入口

在这里插入图片描述

不管是RPC还是还原算法,其核心参数就是对x-s参数的的校验。这段密文是一个base64编码的,你可以拿到本地解密查看明文内容,如下所示:

在这里插入图片描述

RPC如何实现?首先需要先找到加密的入口,可以看到上图断点处l包含x-sx-t的返回,那么加密操作就在上面完成。看下面这行代码:

l = (a && void 0 !== window._webmsxyw ? window._webmsxyw : encrypt_sign)(s, i) || {};

window._webmsxyw函数内即加密逻辑,在自执行函数内部并添加在了window属性中

该函数接受两个参数,s是api接口的路径,i是请求提交的参数

3. 使用JS注入

这是一种简单的实现思路,提供大家进行参考!相对于纯自动化的效果肯定要略好,具体方案可以考虑使用Playwright或者pyppeteer等都可以实现。通过浏览器的JavaScript注入来获取到加密参数,实现方案Demo分别如下所示:

  • Playwright实现示例
import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as playwright:
        browser = await playwright.chromium.launch(headless=True)
        page = await browser.new_page()
		# 注入stealth.min.js脚本
        await page.add_init_script(path="stealth.min.js")
        url = "" # 请求api
        data = "" # 请求参数

        # 执行JavaScript
        encrypt_params = await page.evaluate('([url, data]) => window._webmsxyw(url, data)', [url, data])
        local_storage = await page.evaluate('() => window.localStorage')

        print(encrypt_params)
        print(local_storage)

        await browser.close()
        
asyncio.run(main())
  • pyppeteer实现示例
import asyncio
from pyppeteer import launch

async def main():
    browser = await launch(headless=True)
    page = await browser.newPage()

    # 注入stealth.min.js脚本
    stealth_script = open("stealth.min.js", "r").read()
    await page.evaluateOnNewDocument(stealth_script)

    url = ""  # 请求api
    data = ""  # 请求参数

    # 执行JavaScript
    encrypt_params = await page.evaluate('([url, data]) => window._webmsxyw(url, data)', [url, data])
    local_storage = await page.evaluate('() => window.localStorage')

    print(encrypt_params)
    print(local_storage)

    await browser.close()

asyncio.get_event_loop().run_until_complete(main())

上面的stealth.min.js脚本是一位大佬开源的!注入的作用是为了防止被检测。不过你也可以选择使用一些已经做好反检测的框架或者方案进行。目前像笔记详情、评论内容都新增了xsec_token参数的校验。所以在搜索拿到结果后可以预先获取这个参数再传递到笔记详情与评论采集中,代码示例如下所示:

import time
import random
import requests
from typing import Dict

BASE_URL = '' # 网站地址
HEADER = {} # 请求头

def base36encode(number, digits='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
    base36 = ""
    while number:
        number, i = divmod(number, 36)
        base36 = digits[i] + base36
    return base36.lower()

def generate_search_id():
    timestamp = int(time.time() * 1000) << 64
    random_value = int(random.uniform(0, 2147483646))
    return base36encode(timestamp + random_value)

def request_api(method: str, uri: str, params: Dict = None, json: Dict = None) -> Dict:
    url = f"{BASE_URL}{uri}"
    response = requests.request(method, url, headers=HEADERS, params=params, json=json)
    response.raise_for_status()
    return response.json()

def get_keyword_note_result(keyword: str, page: int = 1, page_size: int = 20) -> Dict:
	"""
    根据关键词获取笔记信息。
    :param keyword: 搜索关键词
    :param page: 下拉翻页数
    :page_size: 每一页最大数据量
    :return: 搜索关键词详情
    """
    uri = "/api/sns/web/v1/search/notes"
    data = {
        "keyword": keyword,
        "page": page,
        "page_size": page_size,
        "search_id": generate_search_id(),
        "sort": 'general', # 默认则是综合,time_descending最新、popularity_descending最热
        "note_type": 0, # 默认则全部,其他筛选1是视频、2是图片
        "image_formats": [
	        "jpg",
	        "webp",
	        "avif"
	    ]
    }
    response = request_api("POST", uri, json=data)

def fetch_note_by_id(note_id: str, xsec_token: str) -> Dict:
    """
    根据笔记ID获取详细信息。
    :param note_id: 笔记ID
    :param xsec_token: 验证Token
    :return: 笔记详情
    """
    payload = {
        "source_note_id": note_id,
        "image_formats": ["jpg", "webp", "avif"],
        "extra": {"need_body_topic": "1"},
        "xsec_source": "pc_feed",
        "xsec_token": xsec_token,
    }
    endpoint = "/api/sns/web/v1/feed"
    response = request_api("POST", endpoint, json=payload)
    return response.get("items", [{}])[0].get("note_card", {})


def fetch_note_comments(note_id: str, cursor: str = "", xsec_token: str = "") -> Dict:
    """
    获取笔记评论。
    :param note_id: 笔记ID
    :param cursor: 游标,用于分页
    :param xsec_token: 验证Token
    :return: 评论详情
    """
    params = {
        "note_id": note_id,
        "cursor": cursor,
        "top_comment_id": "",
        "image_formats": "jpg,webp,avif",
        "xsec_token": xsec_token,
    }
    endpoint = "/api/sns/web/v2/comment/page"
    return request_api("GET", endpoint, params=params)


def fetch_sub_comments(note_id: str, root_comment_id: str, num: int = 30, cursor: str = "", xsec_token: str = "") -> Dict:
    """
    获取子评论。
    :param note_id: 笔记ID
    :param root_comment_id: 根评论ID
    :param num: 每页评论数量
    :param cursor: 游标,用于分页
    :param xsec_token: 验证Token
    :return: 子评论详情
    """
    params = {
        "note_id": note_id,
        "root_comment_id": root_comment_id,
        "num": num,
        "cursor": cursor,
        "image_formats": "jpg,webp,avif",
        "top_comment_id": "",
        "xsec_token": xsec_token,
    }
    endpoint = "/api/sns/web/v2/comment/sub/page"
    return request_api("GET", endpoint, params=params)

在此之前,其中很多细节上的处理这个都是最终工程化需要考虑的事情。比如采集的过程中频率的控制、资源的调度切换等等。本篇文章主要讲解的是通过非逆向分析的方式去解决加密参数问题!再携带加密参数对搜索、笔记、评论等接口发送请求快速获取结构化数据的实现过程

window.localStorage在之前加密分析的文章中已经详细介绍了,localStorage是一个在浏览器中存储键值对的API,通常用于持久化地存储数据,所需的b1参数就在其中

JS注入方式运行结果如下所示:

在这里插入图片描述

x-sx-t的加密参数通过注入的方式能够直接拿到,但是x-s-common的参数仍需要通过加密算法生成!但是这个参数目前大部分API并不校验,仍然以x-s参数为主

JS注入的方式对于有前端基础及经验的小伙伴,就很简单了。通过上面的方式获取到所有的加密参数后,接下来就是爬虫的工程化(下图是feed接口

在这里插入图片描述

4. 爬虫工程化

以笔记搜索为例,下面对数据的抓取示例采用了上面作者提到的另一篇以还原加密算法的文章内算法测试的(非浏览器自动化或注入)。需要注意一下search_id是动态生成的!完整的爬虫代码实现如下所示:

import json
import time
import random
import execjs
import requests

def base36encode(number, digits='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
    base36 = ""
    while number:
        number, i = divmod(number, 36)
        base36 = digits[i] + base36
    return base36.lower()

def generate_search_id():
    timestamp = int(time.time() * 1000) << 64
    random_value = int(random.uniform(0, 2147483646))
    return base36encode(timestamp + random_value)

url = 'https://edith.xiaohongshu.com/api/sns/web/v1/search/notes'
api_endpoint = '/api/sns/web/v1/search/notes'
a1_value = '' # 自行获取

search_data = {
    "keyword": "北京美食",
    "page": 1,
    "page_size": 20,
    "search_id": generate_search_id(),
    "sort": "general",
    "note_type": 0
}

headers = { 
  'sec-ch-ua': 'Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
  'Content-Type': 'application/json;charset=UTF-8',
  'sec-ch-ua-mobile': '?0',
  'Referer': 'https://www.xiaohongshu.com/',
  'sec-ch-ua-platform': 'macOS',
  'Origin': 'https://www.xiaohongshu.com',
  'Cookie': '', # 自行获取
  'User-Agent': '' # 自行获取
}

with open('GenXsAndCommon.js', 'r', encoding='utf-8') as f:
    js_script = f.read()
    context = execjs.compile(js_script)
    sign = context.call('get_xs_xsc', api_endpoint, search_data, a1_value)

headers['x-s'] = sign['X-s']
headers['x-t'] = str(sign['X-t'])
headers['X-s-common'] = sign['X-s-common']

response = requests.post(url, headers=headers, data=json.dumps(search_data, separators=(",", ":"), ensure_ascii=False).encode('utf-8'))

print(response.json())

最后,如果没有编程与爬虫经验的小伙伴!有研究、学习的需求也可以找作者领取开箱即用的完整项目源码进行学习!有兴趣的也可以根据文章所提供分享的思路自己进行研究与实现,没有太大难度!可以咨询作者给予必要的技术指导~

关于数据的获取,像笔记关键词搜索出来的所有笔记内容在抓取完成后均会自动存储到本地的Excel文件内,如下所示:

在这里插入图片描述

笔记所对应的所有评论内容在抓取完成后同样也会存储在本地的Excel文件内,如下所示:

在这里插入图片描述

在这里插入图片描述

获取只需要笔记下面所有的全量一级、二级评论内容,可以直接去指定笔记ID然后获取,如下所示:

在这里插入图片描述

最后!互联网任何公开的数据源有获取数据的需求,可以适当的利用工具与技术来助力。但切记不要滥用,以免对任何第三份平台与网站造成压力与负担!请使用合理、合法、合规、合情的方式去满足自己的需求

爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
### 创建或使用小红书评论爬虫的方法 #### 工具和技术栈的选择 为了高效地构建用于抓取小红书平台上的商品推荐和评论的小型网络爬虫,建议采用Python编程语言及其生态系统中的多个库来辅助开发过程。具体来说,可以利用`requests`来进行HTTP请求发送;借助`BeautifulSoup`解析HTML文档结构;如果遇到动态加载的内容,则可能需要用到像Selenium这样的自动化测试框架模拟浏览器操作[^1]。 #### 实现步骤概述 - **环境搭建**:安装必要的依赖包,包括但不限于上述提到的几个核心组件。 - **目标页面分析**:仔细观察并理解要抓取的目标网页源码,识别出哪些部分包含了所需的数据项(例如用户名、发布时间戳以及具体内容),同时注意API接口调用模式或是JavaScript异步更新机制的存在形式。 - **编写代码逻辑** - 发起初始GET请求访问指定URL; - 解析返回的结果提取有效信息片段; - 对于分页情况下的连续翻页处理需特别关注链接参数变化规律; - 存储所获资料至本地文件系统或者数据库中以便后续加工利用。 下面给出一段简单的伪代码示例展示基本流程: ```python import requests from bs4 import BeautifulSoup def fetch_page(url): response = requests.get(url) soup = BeautifulSoup(response.text, 'html.parser') return soup def parse_comments(soup): comments_list = [] comment_elements = soup.find_all('div', class_='comment') # 假设每条评论都在此类别的<div>标签内 for element in comment_elements: author = element.select_one('.author').text.strip() content = element.select_one('.content').text.strip() timestamp = element.select_one('.timestamp')['datetime'] comments_list.append({ 'author': author, 'content': content, 'timestamp': timestamp }) return comments_list base_url = "https://www.xiaohongshu.com/discovery/item/" soup = fetch_page(base_url) comments_data = parse_comments(soup) for item in comments_data[:5]: print(f"{item['author']} said at {item['timestamp']}: {item['content']}") ``` 请注意这只是一个非常简化版本的例子,在实际应用当中还需要考虑更多细节问题比如异常捕获、登录态维持等[^2]。 此外,考虑到现代Web应用程序广泛采用了前后端分离架构设计思路,很多情况下直接向服务器发起RESTful API调用可能是更优解法之一。此时可参照官方开放平台文档说明尝试获取合法授权密钥后按照规定方式读取消息流数据[^3]。
评论 137
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴秋霖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值