// ==UserScript==
// @name LOFTER合集一键导出txt文档
// @namespace http://tampermonkey.net/
// @version 1.0
// @description 基于真实文章链接选择器,自动滚动+关键词筛选+导出txt
// @match *://*.lofter.com/view*
// @grant none
// @license MIT
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @downloadURL https://update.greasyfork.icu/scripts/541160/LOFTER%E5%90%88%E9%9B%86%E4%B8%80%E9%94%AE%E5%AF%BC%E5%87%BAtxt%E6%96%87%E6%A1%A3.user.js
// @updateURL https://update.greasyfork.icu/scripts/541160/LOFTER%E5%90%88%E9%9B%86%E4%B8%80%E9%94%AE%E5%AF%BC%E5%87%BAtxt%E6%96%87%E6%A1%A3.meta.js
// ==/UserScript==
(async function() {
'use strict';
const $ = window.jQuery;
const delay = ms => new Promise(r => setTimeout(r, ms));
// 自动滚动到底部,等待文章加载,检测文章数稳定
async function autoScroll(maxAttempts = 40, interval = 1500) {
let lastCount = 0, stableCount = 0;
for(let i = 0; i < maxAttempts; i++) {
window.scrollTo(0, document.body.scrollHeight);
await delay(interval);
const count = $("a[href*='/post/']").length;
if(count === lastCount) {
stableCount++;
if(stableCount >= 3) break;
} else {
stableCount = 0;
lastCount = count;
}
}
return lastCount;
}
// 提取文章列表,去重
function extractArticles() {
const articles = [];
$("a[href*='/post/']").each(function(){
const url = $(this).attr("href");
let title = $(this).text().replace(/\s+/g, " ").trim();
if(url && title) {
articles.push({url, title});
}
});
// 去重
return [...new Map(articles.map(a => [a.url, a])).values()];
}
// 根据文章页面尝试抓正文,尝试多个常用选择器
async function fetchContent(url) {
try {
const html = await $.get(url);
const doc = new DOMParser().parseFromString(html, "text/html");
const selectors = [".post-text", ".text", ".post-content", ".article-desc", ".article-content"];
let content = "";
for(const sel of selectors) {
const elems = doc.querySelectorAll(sel);
if(elems.length) {
content = Array.from(elems).map(e => e.textContent.trim()).join("\n\n");
if(content.length > 30) break; // 找到长度合理的正文就用
}
}
return content || "";
} catch(e) {
console.warn("抓取正文失败:", url, e);
return "";
}
}
// 导出文本
function exportTxt(data,keyword) {
data.sort((a,b) => 0); // 若需时间排序,可加时间字段再排序
data.reverse();
let text = "";
data.forEach(item => {
text += item.content.trim() + "\n\n---------------------------------\n\n";
});
const blob = new Blob([text], {type:"text/plain;charset=utf-8"});
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = keyword ? `lofter_${keyword}.txt` : "lofter_文章导出.txt";
link.click();
URL.revokeObjectURL(link.href);
}
// 插入按钮UI
function injectButton() {
const btn = $('');
$("body").append(btn);
btn.on("click", async () => {
const keyword = prompt("请输入关键词(留空导出全部):") || "";
btn.text("自动滚动加载中...");
const totalCount = await autoScroll();
btn.text(`加载到${totalCount}篇文章,开始抓正文...`);
const articles = extractArticles();
const results = [];
for(let i=0; i setTimeout(injectButton, 2000));
})();