小红书笔记导出

导出小红书列表数据,方便做数据分析

当前为 2025-02-27 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         小红书笔记导出
// @namespace    http://www.junxiaopang.com/
// @version      1.1.1
// @description  导出小红书列表数据,方便做数据分析
// @author       俊小胖
// @match        https://www.xiaohongshu.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=xiaohongshu.com
// @license      GPL
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/shim.min.js
// ==/UserScript==

(function () {
    'use strict';
    let data = [];
    let itemNum = 0 //当前加载笔记的数量
    let excel_title = '小红书数据'
    let likeNumLimit = 100 //导出的数据点赞数要求
    let keywords = ''//过滤的关键词

    // 定义一个函数来抓取数据
    function fetchData() {
        var itemNumElement = document.getElementById('itemNum');

        // 假设列表数据在某个具有特定类名的元素中
        const listElements = document.querySelectorAll('.note-item'); // 需要根据实际页面结构调整选择器
        const domain = 'https://www.xiaohongshu.com'

        listElements.forEach(item => {
            // 直接获取 <a> 标签的链接
            const linkElement = item.querySelector('a.cover.ld.mask');
            if (linkElement) {
                const link = domain + linkElement.getAttribute('href'); // 拼接完整链接

                // 获取其他信息
                const titleElement = item.querySelector('.title span');
                const authorElement = item.querySelector('.author .name');
                const likeElement = item.querySelector('.count');

                const title = titleElement?.textContent || '无标题';
                const author = authorElement?.textContent || '未知作者';
                const likes = likeElement?.textContent || '0';

                let itemData = [title, author, likeCount, link]
                data[itemIndex] = itemData
            }
        });


        itemNum = data.length

        if (itemNumElement) {
            itemNumElement.textContent = itemNum; // 更新文本内容
        }

    }
    //过滤点赞数限制的数据
    function filterArrayByLikeNum(arr, limit) {
        var newArray = [];
        arr.forEach(function (subArray) {
            if (subArray[2] > limit) {
                newArray.push(subArray);
            }
        });
        return newArray;
    }
    function containsAnyKeyword(str, keywords) {
        const regexPattern = `(${keywords.join('|')})`;
        const regex = new RegExp(regexPattern, 'i'); // 'i' 表示不区分大小写
        return regex.test(str);
    }
    //过滤指定关键词
    function filterArrayByTitle(arr, keywords) {
        var newArray = [];
        arr.forEach(function (subArray) {
            if (!containsAnyKeyword(subArray[0], keywords)) {
                newArray.push(subArray);
            }
        });
        return newArray;
    }
    // 定义点击按钮时执行的函数
    function onButtonClick() {
        // 在这里添加你想要执行的代码
        exportArrayToCSV(data, excel_title);
        // fetchData()

    }

    //自定义页面样式
    function addStyle() {
        var style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = '.export-note{margin: 6px 0 12px 0;}.group-header-title{font-size: 16px;border-bottom: 1px solid #eee;padding: 10px 0;margin-bottom: 10px;color: rgba(51, 51, 51, 0.6);}.group-header-title a{float: right;color: #f6333b;}#itemNum{font-weight: bold;color: red;}.export-data {margin: 6px 0;display: flex;flex-wrap: wrap;flex-direction: row-reverse;border:solid 1px #eee;border-radius: 16px;padding:6px;line-height: 25px;}.export-button{text-align: center;padding:6px;border:border: 1px solid transparent;background-color:#ff2442;color:#ffffff;border-radius:5px;cursor:pointer}.input{margin: 0 4px;padding:2px 4px;border: 1px solid #ccc;border-radius: 4px;background-color: #eee;width: 60px;}';
        document.head.appendChild(style);
    }

    // 动态插入<script>标签
    function loadScript(url, callback) {
        // 创建script元素
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = url;

        // 绑定事件处理程序
        script.onreadystatechange = script.onload = function () {
            var state = this.readyState;
            if (!callback.done && (!state || /loaded|complete/.test(state))) {
                callback.done = true;
                callback();
            }
        };

        // 插入到DOM中
        document.getElementsByTagName('head')[0].appendChild(script);
    }

    // export-to-excel.js
    function exportArrayToExcel(dataArray, fileName) {
        var newDataArray = dataArray
        var keywordArray = []
        var limit = parseFloat(document.getElementById("likeNumLimit").value);
        var keywords_value = document.getElementById("keywords").value;
        if (limit > 0) {
            newDataArray = filterArrayByLikeNum(dataArray, limit)
            if (keywords_value && keywords_value != '') {
                keywordArray = keywords_value.split(","); // 使用逗号作为分隔符
                newDataArray = filterArrayByTitle(newDataArray, keywordArray)
            }
        }
        newDataArray.unshift(['标题', '作者', '点赞数', '链接']);
        // 创建一个工作簿
        const workbook = XLSX.utils.book_new();
        // 将数组转换为工作表
        const worksheet = XLSX.utils.aoa_to_sheet(newDataArray);
        // 将工作表添加到工作簿
        XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
        // 导出工作簿
        XLSX.writeFile(workbook, fileName + ".xlsx");
    }

    function exportArrayToCSV(dataArray, fileName) {
        let csvContent = "data:text/csv;charset=utf-8,";
        dataArray.forEach(row => {
            csvContent += row.map(cell => `"${cell}"`).join(",") + "\r\n";
        });

        const encodedUri = encodeURI(csvContent);
        const link = document.createElement("a");
        link.setAttribute("href", encodedUri);
        link.setAttribute("download", fileName + ".csv");
        document.body.appendChild(link); // 需要添加到 DOM 中才能触发下载
        link.click();
        document.body.removeChild(link); // 下载完成后移除链接
    }

    //转换点赞数为数字
    function convertToNumber(text) {
        // 移除空格
        text = text.replace(/\s+/g, '');
        // 检测单位
        if (text.endsWith('w')) {
            // 移除单位并转换为数字
            return parseFloat(text.slice(0, -1)) * 10000;
        } else {
            // 如果没有单位,直接返回转换后的数字
            return parseFloat(text);
        }
    }

    //监听鼠标滑动更新数据
    document.addEventListener('mousemove', function (event) {
        fetchData()
    });

    // 创建按钮元素
    const button = document.createElement('button');
    button.textContent = '导出excel';
    button.className = 'export-button'

    // 为按钮添加点击事件监听器
    button.addEventListener('click', onButtonClick);

    //载入导出按钮
    function loadExportButton() {
        addStyle()
        // 导出函数到全局
        window.exportArrayToCSV = exportArrayToCSV;
        // 创建一个 URL 对象
        var url = new URL(window.location.href);
        // 使用 URLSearchParams 获取参数
        var keyword = url.searchParams.get('keyword');
        if (keyword) {
            keyword = decodeURIComponent(keyword)

            excel_title = keyword + ' - 小红书数据'
        } else if (document.title) {
            excel_title = document.title;
        }

        // 找到具有特定类名的元素
        var targetElement = document.querySelector('.channel-list');

        // 创建一个新的 div 元素
        var exportDiv = document.createElement('div');
        // 设置 div 的内容
        exportDiv.innerHTML = '<div class="export-note"><div class="group-header-title">红薯笔记导出<a href="http://shang.junxiaopang.com" target="_blank">💰打赏开发者</a></div>支持导出页面上加载过的笔记列表<br>方便运营人员做数据分析提高工作效率<br>当前已加载<span id="itemNum">0</span>条数据<br>上下滑动鼠标可以加载更多<div class="group-header-title">导出设置</div>只导出点赞大于<input name="like-num" id="likeNumLimit" value="' + likeNumLimit + '" class="input" type="number"/>的数据<br>过滤标题中带有<input name="keywords" id="keywords" value="' + keywords + '" class="input" type="text"/>的内容</div>';
        // 添加类名或者样式
        exportDiv.className = 'export-data';
        // 如果要插入到特定位置,比如body的开头,可以直接使用以下代码:
        targetElement.appendChild(exportDiv);
        exportDiv.appendChild(button);

        //页面加载完以后,初始化读取
        fetchData()
    }

    // 等待页面加载完毕再执行
    window.addEventListener('load', loadExportButton);



})();