您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Scrape novel content from bachngocsach.net.vn. Ctrl+Alt+S to start, Ctrl+Alt+C to stop.
// ==UserScript== // @name Bachngocsach Vip scraper // @name:en Bachngocsach Vip scraper // @name:vi Tải truyện vip bachngocsach // @namespace Violentmonkey Scripts // @match *://bachngocsach.info/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @version 1.1.0 // @author Tác giả = tác = làm, giả là giả (dối) = làm giả // @description Scrape novel content from bachngocsach.net.vn. Ctrl+Alt+S to start, Ctrl+Alt+C to stop. // @description:vi Tải truyện từ bachngocsach.net.vn. Ctrl+Alt+S để bắt đầu, Ctrl+Alt+C để ngừng // @description:en Scrape novel content from bachngocsach.net.vn. Ctrl+Alt+S to start, Ctrl+Alt+C to stop // @license MIT2 // ==/UserScript== const startKey={key:'KeyS', ctrlKey:true, altKey: true, shiftKey:false }; const stopKey= {key:'KeyC', ctrlKey:true, altKey: true, shiftKey:false }; const useEvent=false; const sleep = (ms) => new Promise(rs => setTimeout(rs, ms)); const pressKey = (key) => window.dispatchEvent(new KeyboardEvent('keydown', { key: key, code: key, bubbles: true })); function addUrlChangeEvent() { const urlchangeEvent= new Event('urlchange'); history._pushState=history.pushState; history.pushState=(...args)=>{ history._pushState(...args); if(!!window.onurlchange && typeof window.onurlchange =='function') window.onurlchange(); else window.dispatchEvent(urlchangeEvent); } window.addEventListener('popstate',()=>{ if(!!window.onurlchange && typeof window.onurlchange =='function') window.onurlchange(); else window.dispatchEvent(urlchangeEvent); }) } function reEnableConsoleLog(c) { switch (c) { case 1: console.log = console.dir; break; case 2: console.log = console.info; break; case 3: console.log = console.debug; break; case 4: console.log = console.warn; break; default: { const iF = document.createElement('iframe'); document.body.appendChild(iF); iF.style.display = 'none'; window.console.log = iF.contentWindow.console.log; } } } function getStyles(useRegex=true) { const style={}; const styleStr=document.querySelector('style.dynamic-styles').textContent; if (!styleStr) return style; if(useRegex) { const reg=/\.?([0-9a-zA-Z]+?){order:([0-9]+?)}/g styleStr.matchAll(reg).forEach(m=>style[m[1]]=parseInt(m[2])); } else { styleStr.split('}').forEach(m=>{ if(m=='') return; let s=m.split('{order:'); if(s[0].startsWith('.')) style[s[0].slice(1)]=parseInt(s[1]); else style[s[0]]=parseInt(s[1]); }) } return style; } function chapterContentByTreeWalker(el = document.body) { //dang chay sai o day const textList = []; textList.toString = () => { return textList.reduce((s, n) => s += n.nodeValue, '') } const treeWalker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, (node) => { if (['META', 'SCRIPT', 'NOSCRIPT', 'STYLE', 'AREA', 'BASE', 'CANVAS', 'CODE', 'EMBED', 'LINK', 'MAP', 'PARAM', 'SOURCE', 'VIDEO', 'IMG', 'PICTURE', 'INPUT', 'TEXTAREA'].includes(node.parentNode?.tagName)) return NodeFilter.FILTER_REJECT; return NodeFilter.FILTER_ACCEPT; }); let node; while (node = treeWalker.nextNode()) textList.push(node); return textList.toString(); } function chapterContentByStyle() { console.log('Get chapter content'); const result={}; let t= document.querySelectorAll('main>div>.container>div>.line-clamp-1'); result.storyName=t[0].textContent; result.chapterName=t[1].textContent; result.chapterNumber=parseInt(location.href.match(/.\/chuong-(\d+)/)[1]); const badText = document.querySelector('div.published-content'); const goodText = badText?.previousElementSibling?.innerText||''; if (!badText) {result.chapterContent=goodText; return result;} let badLines = Array(badText.children.length); let style = getStyles(); for (const eights of badText.children) { let className = eights.className.toLowerCase(); if (!style[className]) style[className] = parseInt(getComputedStyle(eights).order); let badLine = Array(eights.children.length); for (const sixes of eights.children) { let tagName = sixes.tagName.toLowerCase(); if (!style[tagName]) style[tagName] = parseInt(getComputedStyle(sixes).order); badLine[style[tagName]] = sixes.textContent; } badLines[style[className]] = badLine.join(''); } result.chapterContent = (goodText.trim() + badLines.join('\n\n')).replaceAll(/\n{3,}/g, '\n\n').replaceAll('·', ''); return result; } const getChapterContent=chapterContentByStyle; // const getChapterContent=chapterContentByTreeWalker; function startDownload(url) { console.log('Start downloading'); GM_setValue('downloading', true); if (/^https:\/\/bachngocsach\.net\.vn\/truyen\/[a-z\-\d]+\/?$/.test(window.location.href)) window.location.assign(window.location.href + '/chuong-1'); else window.location.reload(); } async function stopDownload() { console.log('Stop downloading'); let storyName = GM_getValue('storyName'); GM_deleteValue('storyName'); GM_deleteValue('downloading'); let chapters = GM_listValues(); chapters.sort((a, b) => { parseInt(a) - parseInt(b) }); let content = await Promise.all(chapters.map(chapter => GM_getValue(`${chapter}`))); chapters.forEach(ch => GM_deleteValue(ch)); content = content.join('\n\n').replaceAll(/\n{1,1}/g, '\n\n').replaceAll(/\n{3,}/g, '\n\n'); let download = document.createElement('a'); download.href = 'data:attachment/text,' + encodeURI(content); download.target = '_blank'; download.download = storyName + `(c${chapters[0]}-c${chapters.at(-1)})` + '.txt'; download.click(); return; } function nextChapter() { console.log('Go to next chapter'); if(Math.random() > .4) pressKey('ArrowRight'); //ArrowRight else { const t=document.querySelector('.container>div:nth-last-child(2)>div:first-child>a:last-of-type'); t.scrollIntoView({behavior:'smooth'}) t.click(); } } function isLastChapter() { console.log('Is last chapter?'); return document.querySelector('.container>div:nth-last-child(2)>div:first-child>a:last-of-type').href.endsWith('#'); } async function download() { console.log('Downloading...'); scrollTo({left:0,top:10000, behavior:'smooth'}); await sleep(500+ Math.random()*600); //350 is necessary time the chapter content being loaded const chapter=getChapterContent(); GM_setValue('storyName',chapter.storyName); GM_setValue(chapter.chapterNumber,chapter.chapterName+'\n\n'+chapter.chapterContent); scrollTo({left:0,top:0, behavior:'smooth'}); await sleep(100+ Math.random()*300); if (isLastChapter()) await stopDownload(); else nextChapter(); } (async function(){ if(window!==window.top) return; if (useEvent) { addUrlChangeEvent(); //await sleep(1000); let txt=getChapterContent(); console.log(txt.chapterContent); window.addEventListener('urlchange',async (e)=>{ if (GM_getValue('downloading', undefined)) await download(); else { console.log(getChapterContent().chapterContent); } }) } else { let oldURL=''; const observer= new MutationObserver(async (mList)=>{ mList.forEach(async (m)=>{ if (m.target.className?.includes('published-content')&& window.location.href!=oldURL) { oldURL=window.location.href; if (GM_getValue('downloading', false)) await download(); else console.log(getChapterContent().chapterContent); } }); }); observer.observe(document.querySelector('body'), { childList: true, subtree:true }); } window.addEventListener('keydown',async(e)=>{ if (e.ctrlKey==startKey.ctrlKey && e.altKey==startKey.altKey && e.shiftKey==startKey.shiftKey && (e.key==startKey.key||e.code==startKey.key)) startDownload(location.href); if (e.ctrlKey==stopKey.ctrlKey && e.altKey==stopKey.altKey && e.shiftKey==stopKey.shiftKey && (e.key==stopKey.key||e.code==stopKey.key)) await stopDownload(); }) })();