Greasy Fork is available in English.
彼岸图网图片批量下载,现代美化GUI,支持暂停/继续
// ==UserScript==
// @name TONG-彼岸图网4K抓取
// @namespace http://tampermonkey.net/
// @version 0.4
// @description 彼岸图网图片批量下载,现代美化GUI,支持暂停/继续
// @author tong
// @match https://pic.netbian.com/*
// @grant none
// @license MTT
// ==/UserScript==
(function() {
'use strict';
// 全局状态
let isStopped = false;
let currentIndex = 0;
let allDetailUrls = [];
let category = '';
let pageCount = 0;
// ====== 创建美化GUI界面 ======
function createGUI() {
if (document.getElementById('bian-crawler-gui')) return;
const gui = document.createElement('div');
gui.id = 'bian-crawler-gui';
gui.style.position = 'fixed';
gui.style.top = '40px';
gui.style.right = '40px';
gui.style.zIndex = 999999999;
gui.style.background = 'rgba(255,255,255,0.98)';
gui.style.border = '2px solid #4a90e2';
gui.style.borderRadius = '18px';
gui.style.boxShadow = '0 8px 32px 0 rgba(74,144,226,0.25), 0 1.5px 8px #aaa';
gui.style.padding = '36px 44px 28px 44px';
gui.style.fontSize = '20px';
gui.style.color = '#222';
gui.style.width = '420px';
gui.style.userSelect = 'none';
gui.style.pointerEvents = 'auto';
gui.innerHTML = `
<div style="font-weight:bold;font-size:28px;margin-bottom:18px;color:#357ae8;letter-spacing:1px;text-align:center;">
彼岸图网4K图片批量下载
</div>
<div style="margin-bottom:18px;">
<label>分类字段:</label>
<input id="bian-category" type="text" value="4kdongman" style="width:180px;font-size:18px;padding:4px 8px;border-radius:6px;border:1.5px solid #b0c4de;">
</div>
<div style="margin-bottom:18px;">
<label>爬取页数:</label>
<input id="bian-pagecount" type="number" value="2" min="1" style="width:80px;font-size:18px;padding:4px 8px;border-radius:6px;border:1.5px solid #b0c4de;">
</div>
<button id="bian-start" style="
background: linear-gradient(90deg,#4a90e2 0%,#357ae8 100%);
color: #fff;
font-size: 20px;
border: none;
border-radius: 8px;
padding: 10px 36px;
margin-right: 18px;
cursor: pointer;
box-shadow: 0 2px 8px #b0c4de;
transition: background 0.2s;
">开始爬取</button>
<button id="bian-stop" style="
background: #fff;
color: #e23c3c;
font-size: 20px;
border: 2px solid #e23c3c;
border-radius: 8px;
padding: 10px 24px;
margin-right: 18px;
cursor: pointer;
transition: background 0.2s;
">停止</button>
<button id="bian-close" style="
background: #fff;
color: #357ae8;
font-size: 20px;
border: 2px solid #4a90e2;
border-radius: 8px;
padding: 10px 24px;
cursor: pointer;
transition: background 0.2s;
">关闭</button>
<div id="bian-status" style="margin-top:18px;color:#007b00;font-size:17px;min-height:28px;"></div>
<div style="margin-top:10px;color:#e23c3c;font-size:14px;">首次批量下载时请允许浏览器自动下载多个文件</div>
`;
document.body.appendChild(gui);
gui.addEventListener('mousedown', function(e) {
gui.style.zIndex = 999999999;
});
document.getElementById('bian-close').onclick = () => gui.remove();
document.getElementById('bian-start').onclick = startCrawl;
document.getElementById('bian-stop').onclick = stopCrawl;
}
// ====== 停止按钮逻辑 ======
function stopCrawl() {
isStopped = true;
const statusDiv = document.getElementById('bian-status');
statusDiv.textContent = '已停止,点击“开始爬取”可继续。';
}
// ====== 主爬取逻辑 ======
async function startCrawl() {
const statusDiv = document.getElementById('bian-status');
// 如果是继续任务
if (allDetailUrls.length > 0 && isStopped && currentIndex < allDetailUrls.length) {
isStopped = false;
statusDiv.textContent = '继续爬取...';
await downloadImages();
return;
}
// 否则为新任务
category = document.getElementById('bian-category').value.trim();
pageCount = parseInt(document.getElementById('bian-pagecount').value.trim());
if (!category) {
statusDiv.textContent = '请填写分类字段!';
return;
}
if (!pageCount || pageCount < 1) {
statusDiv.textContent = '请填写有效的页数!';
return;
}
isStopped = false;
currentIndex = 0;
allDetailUrls = [];
statusDiv.textContent = '开始爬取...';
for (let page = 1; page <= pageCount; page++) {
if (isStopped) {
statusDiv.textContent = '已停止,点击“开始爬取”可继续。';
return;
}
statusDiv.textContent = `正在获取第${page}页的图片详情链接...`;
let url = page === 1
? `https://pic.netbian.com/${category}/index.html`
: `https://pic.netbian.com/${category}/index_${page}.html`;
let resp = await fetch(url, {credentials: 'include'});
let text = await resp.text();
let parser = new DOMParser();
let doc = parser.parseFromString(text, 'text/html');
let links = Array.from(doc.querySelectorAll("ul.clearfix li a"))
.map(a => a.getAttribute('href'));
allDetailUrls.push(...links);
}
statusDiv.textContent = `共获取到${allDetailUrls.length}个详情页链接,开始下载图片...`;
await downloadImages();
}
// ====== 下载图片逻辑(支持断点续传)======
async function downloadImages() {
const statusDiv = document.getElementById('bian-status');
for (; currentIndex < allDetailUrls.length; currentIndex++) {
if (isStopped) {
statusDiv.textContent = '已停止,点击“开始爬取”可继续。';
return;
}
let detailUrl = allDetailUrls[currentIndex];
let imgUrl = await getImageUrl(detailUrl);
if (imgUrl) {
let filename = `pic_${currentIndex + 1}.jpg`;
statusDiv.textContent = `正在下载第${currentIndex + 1}张图片...`;
downloadImage(imgUrl, filename);
await new Promise(r => setTimeout(r, 1000 + Math.random() * 2000));
}
}
statusDiv.textContent = '下载完成!';
alert('下载完成!');
// 重置状态
allDetailUrls = [];
currentIndex = 0;
}
// 获取大图URL
async function getImageUrl(detailUrl) {
let url = 'https://pic.netbian.com' + detailUrl;
let resp = await fetch(url, {credentials: 'include'});
let text = await resp.text();
let parser = new DOMParser();
let doc = parser.parseFromString(text, 'text/html');
let img = doc.querySelector("div.photo-pic a img");
return img ? 'https://pic.netbian.com' + img.getAttribute('src') : null;
}
// 下载图片
function downloadImage(imgUrl, filename) {
let a = document.createElement('a');
a.href = imgUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// ====== 页面加载后自动插入GUI ======
setTimeout(createGUI, 1000);
// 保证GUI始终在最顶层
const observer = new MutationObserver(() => {
const gui = document.getElementById('bian-crawler-gui');
if (gui) gui.style.zIndex = 999999999;
});
observer.observe(document.body, {childList: true, subtree: true});
})();