使用方式:
参考输出(依次为 收藏,reclass排序,tag排序,分组tag排序):
开启翻译:
关于tagList
和groupedTagList
在控制台查看会自动按key排序,推荐通过复制最后一行JSON.stringify
输出的JSON字符串,通过vscode的JSON格式化或其他JSON格式化网站查看,如 https://jsonformatter.org/
使用方式:
参考输出(依次为 收藏,reclass排序,tag排序,分组tag排序):
开启翻译:
关于tagList
和groupedTagList
在控制台查看会自动按key排序,推荐通过复制最后一行JSON.stringify
输出的JSON字符串,通过vscode的JSON格式化或其他JSON格式化网站查看,如 https://jsonformatter.org/
// ==UserScript== | |
// @name e站收藏统计 | |
// @namespace Schwi | |
// @version 0.5 | |
// @description 获取e站所有收藏,以及对所有标签进行排序以找到你最爱的标签,可按namespace分组,支持翻译 | |
// @author Schwi | |
// @match *://e-hentai.org/* | |
// @match *://exhentai.org/* | |
// @icon https://e-hentai.org/favicon.ico | |
// @grant none | |
// @license GPLv3 | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// 在 https://e-hentai.org/ 或 https://exhentai.org/ 任意页面运行即可 | |
if (!["e-hentai.org", "exhentai.org"].includes(location.hostname)) { | |
alert("error host!") | |
throw new Error("error host!") | |
} | |
// 是否翻译标签(需下载翻译文本) | |
const config = {} | |
config.translationUrl = "https://raw.githubusercontent.com/EhTagTranslation/DatabaseReleases/master/db.text.json" | |
config.favoritesUrl = location.origin + "/favorites.php?inline_set=dm_e" | |
function collect(config){ | |
// 查询所有收藏 | |
const queryUrl = new URL(config.favoritesUrl || location.href + "favorites.php?inline_set=dm_e") | |
let favList = [] | |
function get(url) { | |
let xhr = new XMLHttpRequest() | |
xhr.open("GET", url, false) | |
xhr.send() | |
if (xhr.status === 200) { | |
return xhr.responseText | |
} else { | |
console.error(xhr.statusText) | |
} | |
} | |
let nextUrl = queryUrl.href | |
while (nextUrl) { | |
let resp = get(nextUrl) | |
if (resp) { | |
let doc = new DOMParser().parseFromString(resp, "text/html") | |
let next = doc.scripts[3] | |
let scriptContent = next.textContent || next.innerText | |
let match = scriptContent.match(/var nexturl="(.*?)"/) | |
nextUrl = match && match[1] | |
if(nextUrl && nextUrl.startsWith('http') && new URL(nextUrl).pathname != queryUrl.pathname) { | |
nextUrl = null | |
} | |
favList = favList.concat(Array.from(doc.querySelectorAll(".itg.glte>tbody>tr"))) | |
} | |
} | |
// 整理所有收藏内容 | |
let myFavList = [] | |
favList.forEach(fav => { | |
let title = fav.querySelector(".glink").innerText | |
let url = fav.href | |
let reclass = fav.querySelector(".cn").innerText | |
let tags = [] | |
fav.querySelectorAll("td>[title]").forEach(tag => { | |
let title = tag.title | |
if (title.startsWith(":")) { | |
title = "temp" + title | |
} | |
tags.push(title) | |
}) | |
myFavList.push({ title, url, reclass, tags }) | |
}) | |
// 翻译tag | |
const select = document.querySelector('#schwi_translate') | |
if (select && select.checked) { | |
const translationUrl = config.translationUrl || "https://raw.githubusercontent.com/EhTagTranslation/DatabaseReleases/master/db.text.json" | |
try { | |
const response = get(translationUrl) | |
if (!response) { | |
throw new Error("Network response was not ok") | |
} | |
const db = JSON.parse(response) | |
myFavList.forEach(fav => { | |
if (fav.reclass in db.data[0].data) { | |
fav.reclass = db.data[0].data[fav.reclass].name | |
} | |
fav.tags = fav.tags.map(fullTag => { | |
let namespace = fullTag.split(":")[0] | |
let tag = fullTag.split(":")[1] | |
let data = db.data.filter(title => title.frontMatters.key === namespace) | |
if (data.length > 0) { | |
namespace = data[0].frontMatters.name | |
if (tag in data[0].data) { | |
tag = data[0].data[tag].name | |
} | |
} | |
return namespace + ":" + tag | |
}) | |
}) | |
} catch (error) { | |
console.error("Error fetching data:", error) | |
} | |
} | |
function sortByCount(list) { | |
const array = Object.entries(list) | |
array.sort((a, b) => (b[1] - a[1]) * 2 + (a[0].toUpperCase() > b[0].toUpperCase() ? 1 : -1)) | |
return Object.fromEntries(array) | |
} | |
// reclass排序 | |
let reclassList = {} | |
myFavList.forEach(fav => { | |
if (fav.reclass in reclassList) { | |
reclassList[fav.reclass]++ | |
} else { | |
reclassList[fav.reclass] = 1 | |
} | |
}) | |
reclassList = sortByCount(reclassList) | |
// 所有tag排序 | |
let tagList = {} | |
myFavList.forEach(fav => { | |
fav.tags.forEach(tag => { | |
if (tag in tagList) { | |
tagList[tag]++ | |
} else { | |
tagList[tag] = 1 | |
} | |
}) | |
}) | |
tagList = sortByCount(tagList) | |
// 按namespace分组的tag排序 | |
let groupedTagList = {} | |
myFavList.forEach(fav => { | |
fav.tags.forEach(fullTag => { | |
let namespace = fullTag.split(":")[0] | |
let tag = fullTag.split(":")[1] | |
if (!(namespace in groupedTagList)) { | |
groupedTagList[namespace] = {} | |
} | |
if (fullTag in groupedTagList[namespace]) { | |
groupedTagList[namespace][fullTag]++ | |
} else { | |
groupedTagList[namespace][fullTag] = 1 | |
} | |
}) | |
}) | |
for (let k in groupedTagList) { | |
groupedTagList[k] = sortByCount(groupedTagList[k]) | |
} | |
return { myFavList, reclassList, tagList, groupedTagList } | |
} | |
const div = document.createElement('div') | |
div.innerHTML = ` | |
<a id="schwi_btn">统计收藏</a> | |
<input type="checkbox" id="schwi_translate" name="schwi_translate" /> | |
<label for="schwi_translate">翻译结果</label> | |
` | |
document.querySelector("#nb").appendChild(div) | |
//不够宽了... | |
document.querySelector("#nb").style.maxWidth='1200px' | |
document.querySelector("#schwi_btn").onclick = (event) => { | |
const { myFavList, reclassList, tagList, groupedTagList } = collect(config) | |
console.log({ '收藏数量':Object.keys(myFavList).length, '标签数量':Object.keys(tagList).length }) | |
console.log(myFavList) | |
console.log(reclassList) | |
console.log(tagList) | |
console.log(groupedTagList) | |
console.log(JSON.stringify(myFavList)) | |
// JSON输出,myFavList太长了,单独打印吧 | |
console.log(JSON.stringify({ reclassList, tagList, groupedTagList })) | |
} | |
})(); |