// ==UserScript== // @name 导出百度贴吧楼主帖子 // @namespace http://tampermonkey.net/ // @version 1.1.1 // @description 将百度贴吧某帖子中楼主的所有发言保存为 HTML 文件,方便离线浏览 // @author wiiiind // @match https://tieba.baidu.com/p/* // @grant GM_download // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/518200/%E5%AF%BC%E5%87%BA%E7%99%BE%E5%BA%A6%E8%B4%B4%E5%90%A7%E6%A5%BC%E4%B8%BB%E5%B8%96%E5%AD%90.user.js // @updateURL https://update.greasyfork.icu/scripts/518200/%E5%AF%BC%E5%87%BA%E7%99%BE%E5%BA%A6%E8%B4%B4%E5%90%A7%E6%A5%BC%E4%B8%BB%E5%B8%96%E5%AD%90.meta.js // ==/UserScript== (function() { 'use strict'; // 添加按钮到页面 function addButton() { const button = document.createElement('a'); button.innerText = '保存楼主发言'; button.href = 'javascript:;'; button.className = 'btn-sub btn-small'; button.onclick = saveTiebaPosts; // 找到按钮组区域 const btnGroup = document.querySelector('.core_title_btns'); if (btnGroup) { // 插入到按钮组的第一个位置 btnGroup.insertBefore(button, btnGroup.firstChild); } } let currentPage = 1; let totalPages = 1; let posts = []; function fetchPosts(page) { const url = window.location.href.replace(/&pn=\d+/, '') + '&pn=' + page; return fetch(url) .then(response => response.text()) .then(html => { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const postElements = doc.querySelectorAll('.l_post'); postElements.forEach(post => { try { // 获取IP属地 const ipSpan = post.querySelector('.post-tail-wrap span:not([class])'); const ip = ipSpan ? ipSpan.innerText.trim().replace(/^IP属地:/, '') : '未知IP'; // 获取其他信息 const tailInfoSpans = post.querySelectorAll('.post-tail-wrap .tail-info'); let deviceInfo = '未知设备'; let floor = '未知楼层'; let time = '未知时间'; // 遍历所有tail-info span,找到包含设备信息、楼层和时间的span tailInfoSpans.forEach(span => { const text = span.innerText.trim(); if (span.querySelector('a') && text.includes('来自')) { deviceInfo = span.querySelector('a').innerText.trim(); } else if (text.includes('楼')) { floor = text; } else if (text.match(/\d{4}-\d{2}-\d{2}/)) { time = text; } }); // 获取内容 const contentElement = post.querySelector('.d_post_content'); const content = contentElement ? contentElement.innerHTML.trim() : ''; // 修改图片获取逻辑,只获取BDE_Image类的图片 const images = Array.from(post.querySelectorAll('.d_post_content img.BDE_Image')).map(img => img.src || ''); posts.push({ ip, deviceInfo, floor, time, content, images }); } catch (error) { console.error('处理帖子时出错:', error); } }); return Promise.resolve(); }) .catch(error => { console.error(`获取第${page}页数据时出错:`, error); }); } function savePosts() { const title = document.querySelector('.core_title_txt').innerText.trim(); const date = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); const fileName = `${title}_${date.split(' ')[0]}.html`; // 获取楼主信息 const authorElement = document.querySelector('.d_name .p_author_name'); const authorName = authorElement ? authorElement.innerText : '未知用户'; const authorLink = authorElement ? authorElement.href : '#'; const authorAvatar = document.querySelector('.p_author_face img'); const avatarSrc = authorAvatar ? authorAvatar.src : ''; const originalLink = window.location.href; // 在生成HTML前对posts进行排序 posts.sort((a, b) => { // 从楼层文本中提取数字 const getFloorNumber = (floor) => { const match = floor.match(/(\d+)/); return match ? parseInt(match[1], 10) : 0; }; return getFloorNumber(a.floor) - getFloorNumber(b.floor); }); let htmlContent = `
${post.floor}
IP属地: ${post.ip}
设备: ${post.deviceInfo}
时间: ${post.time}
|
${contentWithoutImages}
${post.images.map(src => `
|