下载博客

下载22/7博客内容

安装此脚本?
作者推荐脚本

您可能也喜欢去广告&关键词屏蔽

安装此脚本
  1. // ==UserScript==
  2. // @name 下载博客
  3. // @namespace 22/7 blog download
  4. // @match *://blog.nanabunnonijyuuni.com/s/n227/diary/detail/*
  5. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
  6. // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.0/FileSaver.min.js
  7. // @require https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.1.0/jszip-utils.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.14.0/beautify-html.min.js
  10. // @grant none
  11. // @version 1.2
  12. // @author FBZ
  13. // @description 下载22/7博客内容
  14. // @license MIT
  15. /* jshint esversion: 6 */
  16. // ==/UserScript==
  17. ;(function () {
  18. const blogBaseUrl = 'http://blog.nanabunnonijyuuni.com'
  19.  
  20. const downloadButton = `<div id="downloadButton" title="下载">
  21. <span class="inner">↓</span>
  22. </div>`
  23.  
  24. const downloadBtnCss = `
  25. #downloadButton {
  26. position: fixed;
  27. bottom: 3rem;
  28. left: 2rem;
  29. border: 1px solid rgba(0, 0, 0, 0.5);
  30. border-radius: 50%;
  31. width: 2.5rem;
  32. height: 2.5rem;
  33. display: flex;
  34. align-items: center;
  35. justify-content: center;
  36. cursor: pointer;
  37. }
  38.  
  39. #downloadButton:hover {
  40. color: #409EFF;
  41. border-color: #409EFF;
  42. }
  43. `
  44.  
  45. const htmlTemplate = `<!DOCTYPE html>
  46. <html>
  47. <head>
  48. <meta charset="utf-8">
  49. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  50. <meta name="viewport" content="width=device-width">
  51. <meta name="format-detection" content="telephone=no">
  52. <title>BLOG | 22/7(ナナブンノニジュウニ)</title>
  53.  
  54. <style type="text/css">
  55. #container {
  56. display: flex;
  57. justify-content: center;
  58. }
  59. </style>
  60. </head>
  61. <body>
  62. <div id="container"></div>
  63. </body>
  64. </html>`
  65.  
  66. const beautifyOpts = {
  67. indent_size: '2',
  68. indent_char: ' ',
  69. max_preserve_newlines: '0',
  70. preserve_newlines: true,
  71. keep_array_indentation: true,
  72. break_chained_methods: true,
  73. indent_scripts: 'keep',
  74. brace_style: 'collapse,preserve-inline',
  75. space_before_conditional: false,
  76. unescape_strings: false,
  77. jslint_happy: true,
  78. end_with_newline: true,
  79. wrap_line_length: '80',
  80. indent_inner_html: true,
  81. comma_first: false,
  82. e4x: true,
  83. indent_empty_lines: false,
  84. }
  85.  
  86. let isDownloading = false
  87. const zip = new JSZip()
  88. addStyle(downloadBtnCss)
  89. generateDownloadBtn()
  90.  
  91. /* 下载博客 */
  92. async function downloadBlog() {
  93. if (isDownloading) return
  94. try {
  95. setLoading()
  96.  
  97. const title = document
  98. .querySelector('.blog_detail__head')
  99. .querySelector('.blog_detail__title').textContent //获取博客标题
  100. const name = document
  101. .querySelector('.blog_detail__head')
  102. .querySelector('.blog_detail__date')
  103. .querySelector('.name').textContent //获取成员名字
  104. const date = document
  105. .querySelector('.blog_detail__head')
  106. .querySelector('.blog_detail__date')
  107. .querySelector('.date').textContent //获取博客日期
  108. const { newHtml, imgList, cssList } = generateHtml()
  109.  
  110. zip.file(
  111. 'blog.html',
  112. html_beautify(`<!DOCTYPE html>\n${newHtml.outerHTML}`, beautifyOpts)
  113. ) //生成html
  114.  
  115. zip.folder('assets/images') // 创建目录存放图片资源
  116. imgList.forEach(({ filename, src }) => {
  117. JSZipUtils.getBinaryContent(src, function (err, data) {
  118. if (err) {
  119. throw err // or handle err
  120. }
  121. zip.file(`assets/images/${filename}`, data, { binary: true }) // 批量塞入图片
  122. })
  123. })
  124.  
  125. zip.folder('assets/css') // 创建目录存放图片资源
  126. cssList.forEach(({ filename, src }) => {
  127. JSZipUtils.getBinaryContent(src, function (err, data) {
  128. if (err) {
  129. throw err // or handle err
  130. }
  131. zip.file(`assets/css/${filename}`, data, { binary: true }) // css存到本地
  132. })
  133. })
  134.  
  135. const indexImg = await generateScreenShot() // 生成博客截图
  136. const indexImg_transparent = base64Decode(await generateScreenShot(true)) // 生成博客截图
  137. zip.file('screenshot.png', base64Decode(indexImg), { base64: true })
  138. zip.file(
  139. 'screenshot_transparent.png',
  140. base64Decode(indexImg_transparent),
  141. {
  142. base64: true,
  143. }
  144. )
  145.  
  146. // 下载生成的文件
  147. const content = await zip.generateAsync({ type: 'blob' })
  148. saveAs(content, `${name}-${date}-${title}.zip`)
  149. resetLoading()
  150. } catch (error) {
  151. resetLoading()
  152. }
  153. }
  154.  
  155. /* 加载中 */
  156. function setLoading() {
  157. isDownloading = true
  158. document.querySelector('#downloadButton').style.cursor = 'progress'
  159. }
  160.  
  161. /* 重置加载 */
  162. function resetLoading() {
  163. isDownloading = false
  164. document.querySelector('#downloadButton').style.cursor = ''
  165. }
  166. /* 生成博客截图 */
  167. function generateScreenShot(transparent = false) {
  168. return new Promise((resolve, reject) => {
  169. const blogDetail = document.querySelector('.blog_detail')
  170. domtoimage
  171. .toPng(blogDetail, {
  172. bgcolor: transparent ? '' : '#ffffff',
  173. /*style: {
  174. padding: '32px',
  175. },*/
  176. filter: (node) => {
  177. return node.className !== 'btnTweet'
  178. },
  179. })
  180. .then((res) => {
  181. resolve(res)
  182. })
  183. .catch((err) => {
  184. reject(err)
  185. })
  186. })
  187. }
  188.  
  189. /* 往新的html里填充内容 */
  190. function generateHtml() {
  191. const parser = new DOMParser()
  192. const { documentElement: newHtml } = parser.parseFromString(
  193. htmlTemplate,
  194. 'text/html'
  195. ) //通过模板生成html
  196.  
  197. const container = newHtml.querySelector('#container')
  198. const blogDetail = document.querySelector('.blog_detail').cloneNode(true)
  199. const blogDetailMain = blogDetail.querySelector('.blog_detail__main')
  200. const twitterNode = blogDetailMain.querySelector('.btnTweet')
  201. blogDetailMain.removeChild(twitterNode)
  202.  
  203. const imgNodes = blogDetail.querySelectorAll('img')
  204. const imgList = []
  205. for (const node of imgNodes) {
  206. const i = node.src.lastIndexOf('/')
  207. const filename = node.src.slice(i + 1)
  208. imgList.push({
  209. filename,
  210. src: node.src,
  211. })
  212. node.src = `./assets/images/${filename}`
  213. }
  214.  
  215. const linkNodes = document.cloneNode(true).querySelectorAll('link')
  216. console.log('linkNodes: ', linkNodes)
  217. const cssList = []
  218. for (const node of linkNodes) {
  219. if (
  220. node.href.includes('blog.nanabunnonijyuuni.com') &&
  221. node.href.includes('.css')
  222. ) {
  223. const i = node.href.lastIndexOf('/')
  224. const filename = node.href.slice(i + 1)
  225. cssList.push({
  226. filename,
  227. src: node.href,
  228. })
  229. const link = document.createElement('link')
  230. link.href = `./assets/css/${filename}`
  231. link.type = 'text/css'
  232. link.rel = 'stylesheet'
  233. newHtml.querySelector('head').appendChild(link)
  234. }
  235. }
  236.  
  237. container.appendChild(blogDetail)
  238. return { newHtml, imgList, cssList }
  239. }
  240.  
  241. /* 生成下载按钮 */
  242. function generateDownloadBtn() {
  243. const div = document.createElement('div')
  244. div.innerHTML = downloadButton
  245. document.body.appendChild(div)
  246.  
  247. document.querySelector('#downloadButton').addEventListener('click', () => {
  248. downloadBlog()
  249. })
  250. }
  251.  
  252. /* 添加样式 */
  253. function addStyle(css) {
  254. if (!css) return
  255. var head = document.querySelector('head')
  256. var style = document.createElement('style')
  257. style.innerHTML = css
  258. head.appendChild(style)
  259. }
  260.  
  261. // base64去头
  262. function base64Decode(code) {
  263. if (code && code.includes('data:image')) {
  264. code = code.slice(code.indexOf(',') + 1)
  265. }
  266. return code
  267. }
  268. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址