用js脚本下载某书的所有文章

简介: 用js脚本下载某书的所有文章

前言

在某书上的写了好几年的文章,发现某书越来越烂了,全是广告,各种擦边标题党文章和小说等,已经不适合技术人员了。

想把某书上的文章全部下载下来整理一下,某书上是有一个下载所有文章功能的,用了以后发现下载功能现在有问题,无法下载个人账号里所有文章,不知道是不是下载功能根据日期什么判断了,还是bug了,试了好几次都这样,官方渠道只能放弃了。

手动一篇一篇粘贴的成本太高了,不仅有发布的文章,还有各种没有发布的笔记在里面,各种文章笔记加起来好几百篇呢,既然是工程师,就用工程师思维解决实际问题,能用脚本下载个人账号的下的所有文章吗?

思路梳理

由于是下载个人账号下的所有文章,包含发布的和未发布的,来看下个人账号的文章管理后台

image.png

根据操作以及分析浏览器控制台 网络 请求得知,文章管理后台逻辑是这样的,默认查询所有文集(文章分类列表), 默认选中第一个文集,查询第一个文集下的所有文章,默认展示第一篇文章的内容,点击文集,获取当前文集下的所有文章,默认展示文集中的第一篇文章,点击文章获取当前文章数据,来分析一下相关的接口请求

获取所有文集

https://www.jianshu.com/author/notebooks 这个 Get 请求是获取所有 文集,用户信息是放在 cookie

image.png

来看下返回结果

[
    {
        "id": 51802858,
        "name": "思考,工具,痛点",
        "seq": -4
    },
    {
        "id": 51783763,
        "name": "安全",
        "seq": -3
    },
    {
        "id": 51634011,
        "name": "数据结构",
        "seq": -2
    },
    ...
]

接口返回内容很简单,一个 json 数据,分别是:id、文集名称、排序字段。

获取文集中的所有文章

https://www.jianshu.com/author/notebooks/51802858/notes 这个 Get 请求是根据 文集id 获取所有文章,51802858"思考,工具,痛点" 文集的id, 返回数据如下

[
    {
        "id": 103888430, // 文章id
        "slug": "984db49de2c0",
        "shared": false,
        "notebook_id": 51802858, // 文集id
        "seq_in_nb": -4,
        "note_type": 2,
        "autosave_control": 0,
        "title": "2022-07-18", // 文章名称
        "content_updated_at": 1658111410,
        "last_compiled_at": 0,
        "paid": false,
        "in_book": false,
        "is_top": false,
        "reprintable": true,
        "schedule_publish_at": null
    },
    {
        "id": 98082442,
        "slug": "6595bc249952",
        "shared": false,
        "notebook_id": 51802858,
        "seq_in_nb": -3,
        "note_type": 2,
        "autosave_control": 3,
        "title": "架构图",
        "content_updated_at": 1644215292,
        "last_compiled_at": 0,
        "paid": false,
        "in_book": false,
        "is_top": false,
        "reprintable": true,
        "schedule_publish_at": null
    },
    ...
]

接口返回的 json 数据里包含 文章id文集名称,这是接下来需要的字段,其他字段暂时忽略。

获取文章内容

https://www.jianshu.com/author/notes/98082442/content 这个 Get 请求是根据 文章id 获取文章 Markdown 格式内容, 98082442《架构图》 文章的id, 接口返回为 Markdown 格式的字符串

{"content":"![微服务架构图 (3).jpg](https://upload-images.jianshu.io/upload_images/6264414-fa0a7893516725ff.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)\n"}

现在,我们了解清楚了文集,文章,以及文档内容的获取方式,接下来开始脚本实现。

代码实现

由于我是前端攻城狮,优先考虑使用 js 来实现下载,还有一个考虑因素是,接口请求里面的用户信息是通过读取 cookie 来实现的,js 脚本在浏览器的控制台执行发起请求时,会自动读取 cookie,很方便。

如果要下载个人账号下所有文章的话,根据梳理出来的思路编写代码就行

获取所有文集id

fetch("https://www.jianshu.com/author/notebooks")
  .then((res) => res.json())
  .then((data) => {
    // 输出所有文集
    console.log(data);
  })

使用fetch函数进行请求,得到返回结果,上面的代码直接在浏览器控制台执行即可,控制台输出效果如下

image.png

根据文集数据获取所有文章

上一步得到了所有文集,使用 forEach 循环所有文集,再根据 文集id 获取对应文集下的所有文章,依然使用 fetch 进行请求

...
let wenjiArr = [];
wenjiArr = data; // 文集json数据
let articleLength = 0;
wenjiArr.forEach((item, index) => {
  // 根据文集获取文章
  fetch(`https://www.jianshu.com/author/notebooks/${item.id}/notes`)
    .then((res2) => res2.json())
    .then((data2) => {
      console.log("输出文集下的所有文章:", data2);
    });
});

根据文章id获取文章内容,并下载 Markdown 文件

有了文章 id, 根据 id 获取内容,得到的内容是一个对象,对象中的 content 属性是文章的 Markdown 字符串,使用 Blob 对象和 a 标签,通过 click() 事件实现下载。

在这里的代码中使用 articleLength 变量记录了一下文章数量,使用循环中的文集名称和文章名称拼成 Markdown 文件名 item.name - 《item2.title》.md

...
console.log(item.name + " 文集中的文章数量: " + data2.length);
articleLength = articleLength + data2.length;
console.log("articleLength: ", articleLength);
data2.forEach(async (item2, i) => {
  // 根据文章id获取Markdown内容
  fetch(`https://www.jianshu.com/author/notes/${item2.id}/content`)
    .then((res3) => res3.json())
    .then((data3) => {
      console.log(data3);
      const blob = new Blob([data.content], {
        type: "text/markdown",
      });
      const link = document.createElement("a");
      link.href = window.URL.createObjectURL(blob);
      link.download = item.name + " - 《" + item2.title + `》.md`;
      link.click();
    });
});

代码基本完成,运行

在浏览器控制台中运行写好的代码,浏览器下方的下载提示嗖嗖的显示,由于没有做任何处理,当前脚本执行过程中报错了,文章下载了几十个以后就停止了,提示 429

HTTP 请求码 429 表示客户端发送了太多的请求,服务器无法处理。这种错误通常表示服务器被攻击或过载。

文章内容太多了,意料之中的情况,需要改进代码

思路改进分析

根据问题分析,脚本里的代码是循环套循环发请求的,这部分改造一下试试效果。

把每个循环里面发送 fetch 请求的外面是加个 setTimeout, 第一个循环里面的 setTimeout 延迟参数设置为 1000 * indexindex 为当前循环的索引,第一个请求0秒后执行,后面每一次都加1秒后执行,由于文集的数量不多,大约20个,这一步这样实现是没问题的。

重点是第二个循环,根据文集获取所有文章,每个文集里多的文章超过50篇,少的可能2,3篇,这里面的 setTimeout 延迟参数这样设置 2000 * (i + index)i 为第二个循环的索引,这样保证在后面的请求中避免了某个时间段发送大量请求,导致丢包的问题。

再次执行代码,对比控制台输出的文章数量和下载目录中的文章(项目)数量,如果一致,说明文章都下载好了

image.png

image.png

改造后的完整代码地址

github.com/gywgithub/F…

思考

整体来看,文章下载脚本的逻辑并不复杂,接口参数也简单明确,两个 forEach 循环,三个 fetch 请求,把获取到的文章内容使用 a 标签下载下来就行了。关于大量请求发送导致 429 或者请求丢失的问题,脚本中使用了一种方案,当时还想到了另外两种方案:

请求同步执行

通过同步的方式先得到所有文集下的所有文章,再根据文章列表数组同步发请求下载,请求一个个发,文章一篇篇下载

Promise.all

使用 Promise.all() 分批发送请求,避免一次请求发送太多

也可能还有其他的解决方案,欢迎大家评论区讨论交流,一起学习共同进步 ^-^

既然你都看到这里啦,动动你的小手,点个赞再走吧

目录
相关文章
|
9月前
|
Web App开发 存储 数据处理
Chrome 下载大文件报错!用 Streamsaver.js 完美填坑
本文探讨了Chrome下载大文件报“网络错误”的原因及解决方案。由于Chrome对Blob数据有大小限制,导致大文件下载失败。通过将responseType改为ArrayBuffer可临时解决1-2G文件问题,但超3G仍会崩溃。最佳方案是使用Streamsaver.js实现流式下载,边接收边保存,避免内存溢出,完美支持超大文件下载。
4936 3
|
数据采集 Web App开发 JavaScript
Puppeteer自动化:使用JavaScript定制PDF下载
在现代Web开发中,自动化工具如Puppeteer可显著提升效率并减少重复工作。Puppeteer是一款强大的Node.js库,能够控制无头Chrome或Chromium浏览器,适用于网页快照生成、数据抓取及自动化测试等任务。本文通过示例展示了如何使用Puppeteer自动化生成定制化的PDF文件,并介绍了如何通过配置代理IP、设置user-agent和cookie等技术增强自动化过程的灵活性与稳定性。具体步骤包括安装Puppeteer、配置代理IP、设置user-agent和cookie等,最终生成符合需求的PDF文件。此技术可应用于报表生成、发票打印等多种场景。
813 6
Puppeteer自动化:使用JavaScript定制PDF下载
|
存储 前端开发 JavaScript
仿真银行app下载安装, 银行卡虚拟余额制作app,用html+css+js实现逼真娱乐工具
这是一个简单的银行账户模拟器项目,用于学习前端开发基础。用户可进行存款、取款操作,所有数据存储于浏览器内存中
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
487 2
|
数据采集 JavaScript Android开发
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
723 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
前端开发
【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
1276 1
【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
|
JavaScript 前端开发
Node.js 中实现多任务下载的并发控制策略
Node.js 中实现多任务下载的并发控制策略
611 15
|
JavaScript Shell C#
多种脚本批量下载 Docker 镜像:Shell、PowerShell、Node.js 和 C#
本项目提供多种脚本(Shell、PowerShell、Node.js 和 C#)用于批量下载 Docker 镜像。配置文件 `docker-images.txt` 列出需要下载的镜像及其标签。各脚本首先检查 Docker 是否安装,接着读取配置文件并逐行处理,跳过空行和注释行,提取镜像名称和标签,调用 `docker pull` 命令下载镜像,并输出下载结果。使用时需创建配置文件并运行相应脚本。C# 版本需安装 .NET 8 runtime。
1202 3
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
367 1
JavaScript中的原型 保姆级文章一文搞懂
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
367 5
在浏览器执行js脚本的两种方式