您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在油猴菜单中添加下载选项,支持下载帖子文本(TXT)和完整内容(HTML含图片)
// ==UserScript== // @name 小红书帖子内容下载增强版 // @namespace http://tampermonkey.net/ // @version 2.0 // @description 在油猴菜单中添加下载选项,支持下载帖子文本(TXT)和完整内容(HTML含图片) // @author schweigen // @match *://*.xiaohongshu.com/* // @grant GM_registerMenuCommand // @grant GM_download // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // 注册菜单命令 GM_registerMenuCommand("📄 下载帖子TXT", downloadTXT); GM_registerMenuCommand("🌐 下载帖子HTML(含图片)", downloadHTML); // 从帖子中提取文本内容 function extractPostContent() { const descContainer = document.getElementById("detail-desc"); if (!descContainer) { alert("未找到帖子内容,请确认页面已加载完毕!"); return null; } return descContainer.innerText || descContainer.textContent; } // 提取帖子中的所有图片URL(只提取帖子主体图片,排除评论区) function extractImageUrls() { const images = []; const processedUrls = new Set(); console.log('开始提取图片...'); // 方法1:直接查找轮播图片 (swiper组件) const swiperWrapper = document.querySelector('.swiper-wrapper'); if (swiperWrapper) { console.log('找到轮播容器'); // 只取非duplicate的slide,按DOM顺序 const swiperSlides = swiperWrapper.querySelectorAll('.swiper-slide:not(.swiper-slide-duplicate)'); console.log('非duplicate的slide数量:', swiperSlides.length); swiperSlides.forEach((slide, index) => { const img = slide.querySelector('img[data-xhs-img]'); if (img && img.src && !processedUrls.has(img.src)) { console.log(`提取图片 ${index + 1}:`, img.src); images.push(img.src); processedUrls.add(img.src); } }); } // 方法2:如果轮播没找到图片,尝试查找帖子区域的图片 if (images.length === 0) { console.log('轮播中未找到图片,尝试其他方式...'); // 查找所有小红书图片,但排除明显的评论区图片 const allImages = document.querySelectorAll('img[src*="sns-webpic-qc.xhscdn.com"]'); console.log('找到所有小红书图片数量:', allImages.length); allImages.forEach((img, index) => { // 排除评论区图片的判断 const isInComment = img.closest('[class*="comment"]') || img.closest('[class*="interaction"]') || img.closest('[class*="user-info"]') || img.closest('[data-v-5e5b6d96]'); // 评论区可能的class if (!isInComment && img.src && !processedUrls.has(img.src)) { console.log(`备用方法提取图片 ${index + 1}:`, img.src); images.push(img.src); processedUrls.add(img.src); } }); } console.log('最终提取到的图片数量:', images.length); console.log('图片URLs:', images); return images; } // 将图片URL转换为Base64 async function imageToBase64(url) { try { const response = await fetch(url); const blob = await response.blob(); return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }); } catch (error) { console.error('转换图片失败:', url, error); return null; } } // 下载TXT格式 function downloadTXT() { const content = extractPostContent(); if (!content) return; const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); const filename = (document.title || "xiaohongshu_post") + ".txt"; const downloadLink = document.createElement("a"); downloadLink.href = url; downloadLink.download = filename; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); setTimeout(() => URL.revokeObjectURL(url), 1000); } // 下载HTML格式(含图片) async function downloadHTML() { const content = extractPostContent(); if (!content) return; const imageUrls = extractImageUrls(); console.log('找到图片:', imageUrls.length, '张'); // 显示加载提示 const loadingDiv = document.createElement('div'); loadingDiv.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.8); color: white; padding: 20px; border-radius: 8px; z-index: 10000; font-size: 16px; text-align: center; `; loadingDiv.textContent = '正在处理图片,请稍候...'; document.body.appendChild(loadingDiv); try { // 转换所有图片为Base64 const imagePromises = imageUrls.map(async (url, index) => { loadingDiv.textContent = `正在处理图片 ${index + 1}/${imageUrls.length}...`; const base64 = await imageToBase64(url); return { url, base64 }; }); const imageResults = await Promise.all(imagePromises); // 构建HTML内容 let htmlContent = ` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${document.title || '小红书帖子'}</title> <style> body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; max-width: 800px; margin: 20px auto; padding: 20px; line-height: 1.6; background: #f5f5f5; } .container { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); } .title { font-size: 24px; font-weight: bold; margin-bottom: 20px; color: #333; } .content { margin-bottom: 30px; white-space: pre-wrap; color: #555; } .images { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px; } .image-item { text-align: center; } .image-item img { max-width: 100%; height: auto; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); } .footer { margin-top: 30px; text-align: center; color: #999; font-size: 14px; } </style> </head> <body> <div class="container"> <div class="title">${document.title || '小红书帖子'}</div> <div class="content">${content}</div> <div class="images"> `; // 添加图片 imageResults.forEach((result, index) => { if (result.base64) { htmlContent += ` <div class="image-item"> <img src="${result.base64}" alt="图片 ${index + 1}" /> </div>`; } }); htmlContent += ` </div> <div class="footer"> <p>下载时间: ${new Date().toLocaleString()}</p> <p>来源: ${window.location.href}</p> </div> </div> </body> </html>`; // 下载HTML文件 const blob = new Blob([htmlContent], { type: "text/html;charset=utf-8" }); const url = URL.createObjectURL(blob); const filename = (document.title || "xiaohongshu_post") + ".html"; const downloadLink = document.createElement("a"); downloadLink.href = url; downloadLink.download = filename; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); setTimeout(() => URL.revokeObjectURL(url), 1000); } catch (error) { console.error('下载HTML失败:', error); alert('下载失败: ' + error.message); } finally { document.body.removeChild(loadingDiv); } } })();