// ==UserScript==
// @name 小红书工具
// @version 3.1.16
// @description 仅用于学习:小红书搜索、首页、推荐页面笔记数据导出、小红书笔记图片视频导出、小红书搜索快速跳转
// @match https://www.xiaohongshu.com/*
// @license MIT
// @namespace https://greasyfork.org/users/1172355
// ==/UserScript==
(function () {
'use strict';
// use regex instead of hard code
const profileRegex = /\/profile\//;
const recommendRegex1 = /https:\/\/www.xiaohongshu.com\/explore$/;
const recommendRegex2 = /https:\/\/www.xiaohongshu.com\/[\/]?\?channel_id=.*$/;
const searchResultRegex = /\/search_result[\/]?\?keyword=.*&source=/;
const noteRegex_old = /https:\/\/www.xiaohongshu.com\/explore\/[0-9a-z]+$/;
const noteRegex = /https:\/\/www.xiaohongshu.com\/explore\/[0-9a-z]+/;
// const isNote = document.getElementsByClassName('note-detail-mask').length > 0 ? true:false;
if (searchResultRegex.test(window.location.href)
|| profileRegex.test(window.location.href)
|| recommendRegex1.test(window.location.href)
|| recommendRegex2.test(window.location.href)) {
// 第一个功能脚本 - 从小红书搜索页面提取笔记内容数据并导出为表格
// 创建一个空数组,用于存储提取的数据
let data = [];
// 创建一个 Set,用于存储已提取的笔记链接
let extractedLinks = new Set();
// 创建用于显示当前采集数量的元素
const countElement = document.createElement("div");
countElement.style.position = "fixed";
countElement.style.bottom = "20px";
countElement.style.right = "20px";
countElement.style.zIndex = "9999";
countElement.style.backgroundColor = "#fff";
countElement.style.padding = "10px";
countElement.style.borderRadius = "5px";
countElement.style.boxShadow = "0 2px 5px rgba(0, 0, 0, 0.2)";
countElement.style.fontWeight = "bold";
countElement.style.fontSize = "16px";
countElement.style.color = "#333";
document.body.appendChild(countElement);
// 创建一个文件名称
const title_name = document.querySelector("head > title").innerText;
const indexOfDash = title_name.indexOf('-');
const title_result = title_name.substring(0, indexOfDash).trim() + '.csv';
// 创建下载按钮
const downloadBtn = document.createElement("button");
downloadBtn.innerText = "一键导出数据";
downloadBtn.style.position = "fixed";
downloadBtn.style.bottom = "60px";
downloadBtn.style.right = "20px";
downloadBtn.style.zIndex = "9999";
downloadBtn.style.backgroundColor = "#4CAF50";
downloadBtn.style.color = "#fff";
downloadBtn.style.border = "none";
downloadBtn.style.borderRadius = "5px";
downloadBtn.style.padding = "10px";
downloadBtn.style.fontSize = "16px";
downloadBtn.style.cursor = "pointer";
// downloadBtn.addEventListener("click", () => exportToCSV(data, 'note_data.csv'));
downloadBtn.addEventListener("click", () => exportToCSV(data, title_result));
document.body.appendChild(downloadBtn);
// 添加表头
data.push(['笔记标题', '笔记链接', '作者', '作者链接', '点赞数', '视频']);
// 提取笔记内容数据
let count = 0; // 计数器
extractNoteData();
// 监听页面滚动事件,当加载更多内容时,重新提取数据
window.addEventListener("scroll", extractNoteData);
// 导出函数,将数据导出为 CSV 文件
function exportToCSV(data, filename) {
// 删除第三行数据
data.splice(2, 1);
const csvContent = "data:text/csv;charset=utf-8,\uFEFF" + // 添加 BOM 头以处理 UTF-8 编码
data.map(row => row.join(",")).join("\n");
const encodedUri = encodeURI(csvContent);
const link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", filename);
document.body.appendChild(link); // 需要将链接元素添加到文档中才能生效
link.click();
}
// 提取笔记内容数据
function extractNoteData() {
const noteElements = document.querySelectorAll('div[data-v-3e97982a]');
noteElements.forEach(noteElement => {
// 检查是否已提取过该笔记内容数据
if (!noteElement.classList.contains('extracted')) {
// 提取标题和笔记链接
const titleElement = noteElement.querySelector('a.title span');
const title = titleElement ? titleElement.innerText : '';
const noteLinkElement = noteElement.querySelector('a.cover');
const noteLink = noteLinkElement ? noteLinkElement.href.replace('/search_result/', '/explore/') : '';
// 检查笔记链接是否已提取过
if (!extractedLinks.has(noteLink) && !title.startsWith('#')) {
// 提取作者和作者链接
const authorElement = noteElement.querySelector('div.author-wrapper a.author');
const author = authorElement ? authorElement.querySelector('span.name').innerText : '';
const authorLink = authorElement ? 'https://www.xiaohongshu.com' + authorElement.getAttribute('href') : '';
// 提取点赞数
const likeElement = noteElement.querySelector('span.like-wrapper span.count');
const likeCount = likeElement ? likeElement.innerText : '';
// 提取是否是视频
const is_video = noteElement.querySelector("span.play-icon");
const video = is_video ? 1 : 0;
// 将提取的数据添加到数组中
data.push([title, noteLink, author, authorLink, likeCount, video]);
// 将笔记链接添加到已提取的链接集合中
extractedLinks.add(noteLink);
// 标记为已提取
noteElement.classList.add('extracted');
// 增加计数器
count++;
}
}
});
// 更新当前采集数量的显示
countElement.innerText = "工具已获取:" + count + "条";
}
// 在每次重新打开页面时清除之前保存的数据和链接记录
window.addEventListener("beforeunload", () => {
data = [];
extractedLinks = new Set();
});
} if (noteRegex.test(window.location.href)) {
// 第二个功能脚本 - 导出小红书笔记页面的图片和视频
function exportImages() {
var imageElements = document.querySelectorAll('.swiper-slide');
var imageUrls = new Set();
imageElements.forEach(function (element) {
var backgroundImage1 = element.style.backgroundImage;
var backgroundImage = element.innerHTML;
var imageUrl = backgroundImage.match(/src="(https:\/\/[^"]+)"/)[1];
imageUrls.add(imageUrl);
});
var titleElement = document.querySelector('.title');
var pageTitle = titleElement.innerText.trim();
var index = 1;
imageUrls.forEach(function (imageUrl) {
fetch(imageUrl)
.then(response => response.blob())
.then(blob => {
var imageURL = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = imageURL;
a.download = pageTitle + '-' + index + '.png';
a.click();
URL.revokeObjectURL(imageURL);
});
index++;
});
}
// 导出视频(去重处理,并使用采集页面标题命名)
function exportVideos() {
var videoElements = document.querySelectorAll('video');
var videoUrls = new Set();
videoElements.forEach(function (element) {
var videoUrl = element.getAttribute('src') || element.getAttribute('data-src');
if (videoUrl) {
videoUrls.add(videoUrl);
}
});
var titleElement = document.querySelector('.title');
var pageTitle = titleElement.innerText.trim();
var index = 1;
videoUrls.forEach(function (videoUrl) {
var videoName = pageTitle + '-' + index + '.mp4';
var a = document.createElement('a');
a.href = videoUrl;
a.download = videoName;
a.click();
index++;
});
}
// 创建下载按钮
function createDownloadButton() {
var button = document.createElement('button');
button.textContent = '一键下载本条图片和视频';
button.style.position = 'fixed';
button.style.bottom = '20px';
button.style.left = '20px';
button.style.zIndex = '9999';
button.style.padding = '10px 20px';
button.style.border = 'none';
button.style.backgroundColor = '#ff5a5f';
button.style.color = '#fff';
button.style.fontFamily = 'Arial, sans-serif';
button.style.fontSize = '16px';
button.style.fontWeight = 'bold';
button.style.cursor = 'pointer';
button.addEventListener('click', function () {
exportImages();
exportVideos();
});
document.body.appendChild(button);
}
// 检查当前页面是否匹配指定的链接格式
function checkPage() {
var currentURL = window.location.href;
var pattern1 = /^https:\/\/www\.xiaohongshu\.com\/explore\/.*$/;
var pattern1 = /^https:\/\/www\.xiaohongshu\.com\/.*$/;
return pattern.test(currentURL);
}
// 在页面加载完成后创建下载按钮
window.addEventListener('load', function () {
createDownloadButton();
if (checkPage()) {
createDownloadButton();
}
});
}
// if (window.location.href.includes('/search_result/')) {
if (searchResultRegex.test(window.location.href)) {
// 第三个功能脚本 - 在小红书搜索页面上添加其他搜索引擎的跳转链接按钮
// 定义搜索引擎数据
var searchEnginesData = [
{ name: "用百度搜", url: "https://www.baidu.com/s?wd=" },
{ name: "用知乎搜", url: "https://www.zhihu.com/search?type=content&q=" },
{ name: "用抖音搜", url: "https://www.douyin.com/search/" },
{ name: "用公号搜", url: "https://weixin.sogou.com/weixin?type=2&query=" }
// 添加其他搜索引擎的数据,格式为 { name: "搜索引擎名称", url: "搜索引擎链接" }
];
// 创建搜索引擎导航按钮
function createSearchEngineButtons() {
var channelList = document.querySelector(".channel-list");
if (channelList) {
var buttonsWrapper = document.createElement("div");
buttonsWrapper.className = "search-engine-buttons";
// 查找目标位置的父节点
var parentElement = document.querySelector(".bottom-channel");
if (parentElement) {
parentElement.parentNode.insertBefore(buttonsWrapper, parentElement.nextSibling);
} searchEnginesData.forEach(function (engine, index) {
var button = document.createElement("a");
button.textContent = engine.name;
button.href = "javascript:void(0);"; // 设置初始链接为 javascript:void(0);
button.addEventListener("click", function () {
var keyword = getSearchKeyword();
if (keyword) {
var url = engine.url + encodeURIComponent(keyword);
window.open(url, "_blank"); // 在新窗口或标签页中打开搜索引擎链接
}
});
// 设置按钮样式
button.className = "search-engine-button";
button.classList.add("large-button");
if (index === 2 || index === 3) {
button.classList.add("bottom-row");
} buttonsWrapper.appendChild(button);
});
}
}// 获取搜索关键词
function getSearchKeyword() {
var keywordInput = document.querySelector('.input-box input.search-input');
if (keywordInput) {
return keywordInput.value.trim();
} return "";
}// 添加搜索按钮样式
var styleElement = document.createElement("style");
styleElement.textContent = `
.search-engine-buttons {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 10px;
}.search-engine-button {
display: inline-block;
width: calc(50% - 10px);
padding: 10px;
background-color: #4caf50;
border-radius: 8px;
color: #fff !important;
font-size: 18px;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: background-color 0.2s;
margin-bottom: 0px;
}.search-engine-button.large-button {
padding: 10px;
}.search-engine-button.bottom-row {
margin-top: 5px;
}.search-engine-button:hover {
background-color: #365843;
}`;
document.head.appendChild(styleElement);
// 主程序
function main() {
createSearchEngineButtons();
}// 在页面加载完成后执行主程序
window.addEventListener("load", main);
}
})();