1+ // ==UserScript==
2+ // @name DownloadAllContent
3+ // @name :zh-CN 懒人小说下载器
4+ // @name :zh-TW 懶人小説下載器
5+ // @name :ja 怠惰者小説ダウンロードツール
6+ // @namespace hoothin
7+ // @version 0.3
8+ // @description Fetch and download main content on current page
9+ // @description :zh-CN 通用网站内容抓取工具,可抓取小说,论坛内容等并保存为TXT文档
10+ // @description :zh-TW 通用網站內容抓取工具,可抓取小說,論壇內容等並保存為TXT文檔
11+ // @description :ja ユニバーサルサイトコンテンツクロールツール、クロール、フォーラム内容など
12+ // @author hoothin
13+ // @include *
14+ // @grant GM_xmlhttpRequest
15+ // @grant GM_registerMenuCommand
16+ // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js
17+ // @license MIT License
18+ // @compatible chrome
19+ // @compatible firefox
20+ // @compatible opera 未测试
21+ // @compatible safari 未测试
22+ // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=rixixi@sina.com&item_name=Greasy+Fork+donation
23+ // @contributionAmount 1
24+ // ==/UserScript==
25+
26+ ( function ( ) {
27+ 'use strict' ;
28+ var lang = navigator . appName == "Netscape" ?navigator . language :navigator . userLanguage ;
29+ var i18n = { } ;
30+ switch ( lang ) {
31+ case "zh-CN" :
32+ i18n = {
33+ fetch :"开始下载小说或其他" ,
34+ info :"本文使用DownloadAllContent脚本下载"
35+ } ;
36+ break ;
37+ default :
38+ i18n = {
39+ fetch :"Download All Content" ,
40+ info :"The TXT is downloaded with 'DownloadAllContent'"
41+ } ;
42+ break ;
43+ }
44+
45+ function indexDownload ( aEles ) {
46+ var rocketContent = document . createElement ( "div" ) ;
47+ document . body . appendChild ( rocketContent ) ;
48+ rocketContent . outerHTML = `
49+ <div id="txtDownContent" style="display: none;">
50+ <div style="width:300px;height:50px;position:fixed;left:50%;top:50%;margin-top:-25px;margin-left:-150px;z-index:100000;background-color:#ffffff;border:1px solid #afb3b6;border-radius:10px;opacity:0.95;filter:alpha(opacity=95);box-shadow:5px 5px 20px 0px #000;">
51+ <div id="txtDownWords" style="position:absolute;left:20px;top:10px;width:260px;">
52+ </div>
53+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAAA5FBMVEUAAAD+/v7////9/f7////////+/v7+/v7////+/v7+/v7////+/v7+/v7////+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7////////////+/v7+/v7+/v7+/v7+/v4uje3///82ke7s9P3N5PtQoPDI4fqCu/Tu9v5Im+/6/P+VxfZgqPFNnvDp8/3f7fq42Pmnz/d1tPNvsfNkq/JCmO/4+/7X6fz19/rn8PqYx/aNwfV8uPRqrvJZpfFUovAzkO3Q5vu92/mr0fieyva92fWx0vQ6lO5pygFTAAAAJHRSTlMAmfD+RMGwgj2mknlIKR/36+XGnIyHfnJfVDk2My8S4E1CJBvTatKDAAABY0lEQVQoz4WSZ1fCMBRA05ahLPfemkspBUFwgGz3+P//x/hK6ZBzvB/ak96+kZeoGCtFK5eziitqCSfWOnM2CuWUrOyQYPc0bjM2htHXh+/ffQ4lQ6zEPoZ6TwdUp10M56EtAe5MR7y0HGAeX7Ghe6MTzIZwFtTfBrepUzw4UFSGY8CUTTMGLo3eglv58By21pRnB7aNBqTwPfWq/OVey9sH22wZXiWo6Yr3GtRl/f0IRyoPYy14v97YWjVY12BPrcK9XvhaZPUdWCoLbR35yOoW5P7R8eQ3JrmbSp6HVmSrXuTrmNYyUAs2JkL6D6YjG1PgNGXK87F4nWAsUxmL2gyH6oVD7cuhdqFg9CE4T/oPE6CsgvBOP21715AP7ugaDFK+3YD1KyUcAG47bn0TSylxFd8WDTwMMByoBSUHw+jd9/3JbQODnVExygUS7FRUksPVtdDZW8dqCZm8lc1auxcq4gc02GVGTUchmgAAAABJRU5ErkJggg==" id="txtDownQuit" style="position:absolute;right:0px;top:0px;cursor: pointer;" />
54+ </div>
55+ </div>` ;
56+ var txtDownContent = document . querySelector ( "#txtDownContent" ) ;
57+ var txtDownWords = document . querySelector ( "#txtDownWords" ) ;
58+ var txtDownQuit = document . querySelector ( "#txtDownQuit" ) ;
59+ txtDownQuit . onclick = function ( ) {
60+ txtDownContent . style . display = "none" ;
61+ txtDownContent . parentNode . removeChild ( txtDownContent ) ;
62+ } ;
63+ var j = 0 , rCats = [ ] ;
64+ for ( let i = 0 ; i < aEles . length ; i ++ ) {
65+ let aTag = aEles [ i ] ;
66+ GM_xmlhttpRequest ( {
67+ method : 'GET' ,
68+ url : aTag . href ,
69+ onload : function ( result ) {
70+ var doc = null ;
71+ try {
72+ doc = document . implementation . createHTMLDocument ( '' ) ;
73+ doc . documentElement . innerHTML = result . responseText ;
74+ }
75+ catch ( e ) {
76+ console . log ( 'parse error' ) ;
77+ }
78+ if ( ! doc ) {
79+ return ;
80+ }
81+ j ++ ;
82+ rCats [ i ] = ( aTag . textContent + "\r\n" + getPageContent ( doc ) ) ;
83+ txtDownContent . style . display = "block" ;
84+ txtDownWords . innerHTML = "已下载完成 " + j + " 段,剩余 " + ( aEles . length - j ) + " 段" ;
85+ if ( j == aEles . length ) {
86+ txtDownWords . innerHTML = "已全部下载完成,共 " + j + " 段" ;
87+ var blob = new Blob ( [ i18n . info + "\r\n" + document . title + "\r\n\r\n" + rCats . join ( "\r\n\r\n" ) ] , { type : "text/plain;charset=utf-8" } ) ;
88+ saveAs ( blob , document . title + ".txt" ) ;
89+ }
90+ }
91+ } ) ;
92+ }
93+ }
94+
95+ function getPageContent ( pageData ) {
96+ var i , rStr = "" ;
97+ var largestContent , contents = pageData . querySelectorAll ( "span,div,article,p,td" ) ;
98+ for ( i = 0 ; i < contents . length ; i ++ ) {
99+ var content = contents [ i ] ;
100+ if ( content . firstChild && (
101+ ( content . firstChild . nodeType != 3 && ! / ^ [ I | A ] $ / . test ( content . firstChild . tagName ) ) ||
102+ ( / ^ \s * $ / . test ( content . firstChild . data ) &&
103+ ( ! content . childNodes [ 1 ] || ! / ^ [ I | A ] $ / . test ( content . childNodes [ 1 ] . tagName ) ) )
104+ ) )
105+ continue ;
106+ if ( pageData == document && content . offsetWidth <= 0 && content . offsetHeight <= 0 )
107+ continue ;
108+ if ( navigator . userAgent . toLowerCase ( ) . indexOf ( 'firefox' ) != - 1 ) {
109+ if ( ! largestContent || largestContent . textContent . length < content . textContent . length ) {
110+ largestContent = content ;
111+ }
112+ } else {
113+ if ( ! largestContent || largestContent . innerText . length < content . innerText . length ) {
114+ largestContent = content ;
115+ }
116+ }
117+ }
118+ var childlist = pageData . querySelectorAll ( largestContent . tagName ) ;
119+ for ( i = 0 ; i < childlist . length ; i ++ ) {
120+ var child = childlist [ i ] ;
121+ if ( largestContent . className && largestContent . className == child . className ) {
122+ } else if ( child . firstChild && ( ( child . firstChild . nodeType != 3 && ! / ^ [ I | A ] $ / . test ( child . firstChild . tagName ) ) || ( / ^ \s * $ / . test ( child . firstChild . data ) && ( ! child . childNodes [ 1 ] || ! / ^ [ I | A ] $ / . test ( child . childNodes [ 1 ] . tagName ) ) ) ) ) continue ;
123+ if ( getDepth ( child ) == getDepth ( largestContent ) ) {
124+ let childNodes = child . childNodes , cStr = "\r\n" , hasText = false ;
125+ for ( var j = 0 ; j < childNodes . length ; j ++ ) {
126+ var childNode = childNodes [ j ] ;
127+ if ( childNode . nodeType == 3 && childNode . data && ! / ^ \s * $ / . test ( childNode . data ) ) hasText = true ;
128+ if ( childNode . tagName == "BR" ) cStr += "\r\n" ;
129+ else if ( ! / S C R I P T | S T Y L E / . test ( childNode . tagName ) && childNode . textContent ) cStr += childNode . textContent . replace ( / \s * / , " " ) ;
130+ }
131+ if ( hasText ) rStr += cStr ;
132+ }
133+ }
134+ return rStr ;
135+ }
136+
137+ function getDepth ( dom ) {
138+ var pa = dom , i = 0 ;
139+ while ( pa . parentNode ) {
140+ pa = pa . parentNode ;
141+ i ++ ;
142+ }
143+ return i ;
144+ }
145+
146+ function fetch ( ) {
147+ var aEles = document . querySelectorAll ( "a" ) , list = [ ] ;
148+ for ( var i = 0 ; i < aEles . length ; i ++ ) {
149+ var aEle = aEles [ i ] ;
150+ if ( / 第 [ \d | 〇 | 零 | 一 | 二 | 三 | 四 | 五 | 六 | 七 | 八 | 九 | 十 | 百 | 千 | 万 | 萬 ] + [ 章 | 节 | 回 | 卷 | 折 | 篇 | 幕 | 集 ] | 序 | 序 \s * 言 | 序 \s * 章 | 前 \s * 言 | 引 \s * 言 | 引 \s * 子 | 摘 \s * 要 | 楔 \s * 子 | 后 \s * 记 | 附 \s * 言 | 结 \s * 语 / . test ( aEle . innerHTML ) ) {
151+ list . push ( aEle ) ;
152+ }
153+ }
154+ if ( list . length > 2 ) {
155+ indexDownload ( list ) ;
156+ } else {
157+ var blob = new Blob ( [ i18n . info + "\r\n" + document . title + "\r\n\r\n" + getPageContent ( document ) ] , { type : "text/plain;charset=utf-8" } ) ;
158+ saveAs ( blob , document . title + ".txt" ) ;
159+ }
160+ }
161+
162+ document . addEventListener ( "keydown" , function ( e ) {
163+ if ( e . keyCode == 120 && e . ctrlKey ) {
164+ fetch ( ) ;
165+ }
166+ } ) ;
167+ GM_registerMenuCommand ( i18n . fetch , fetch ) ;
168+ } ) ( ) ;
0 commit comments