【爬虫】自动获取showdoc指定项目中的所有文档

news2024/9/22 19:21:34

▒ 目录 ▒

    • 🛫 导读
      • 需求
    • 1️⃣ 格式分析
      • 官方下载文件内容
      • prefix_info.json文件格式
    • 2️⃣ 封包分析
      • `/api/page/info`
      • `/api/item/info`
    • 3️⃣ 编码
      • 代码特点
      • 问题
    • 📖 参考资料

🛫 导读

需求

showdoc是一个API文档、技术文档工具网站,经常能搜到一些很好的文档,但是由于没有权限,往往无法将这些文档下载和修改。(前几年该网站还经常出现崩溃问题,使得文档无法正常查看,让人头痛不已)。
今天我们就通过技术手段,将这些文件下载到本地,方便执行其他操作。

1️⃣ 格式分析

官方下载文件内容

showdoc本身支持下载功能,首先,我们自己生成一个项目,然后查看下官网提供的格式。
在这里插入图片描述

从图中,我们可以看出,文件分为三类

  • prefix_info.json
    • 包含了项目的所有内容(文档及目录结构、文档基本信息、文档内容)。
  • prefix_readme.md
    • 从该文件描述看,它只是保存了文件标题和下载文件名的对应关系。
  • prefix_***.md
    • 这些文件是每个文档中的内容。

工具apifox可以导入showdoc,就是用到了prefix_info.json文件。所以我们继续分析该文件结构

prefix_info.json文件格式

通过工具我们将prefix_info.json格式化,可以看到,除了一些基本信息外,核心数据都保存在pages字段(与内部的pages含义不一样)。
在这里插入图片描述

内部的pages就是一个数组,包含了文章列表,如下图所示
在这里插入图片描述

内部的catalogs字段,结构如下,其中catalogs就是个无线套娃的树状结构,我们需要做的就是递归处理该逻辑。
在这里插入图片描述

2️⃣ 封包分析

打开浏览器调试界面,我们可以看到,showdoc只有两类请求:
在这里插入图片描述

/api/page/info

其中/api/page/info内容相对简单,变化的只有page_id,而所有的page_id都在/api/item/info中保存着。
在这里插入图片描述
至于该接口的返回结果,我们只需要取出page_content字段即可。
在这里插入图片描述

/api/item/info

/api/item/info请求也相对简单,需要传递item_id即可。
在这里插入图片描述
其请求结果跟上文分析的prefix_info.json极为相似。
在这里插入图片描述

3️⃣ 编码

逻辑相对简单,函数相关含义都加了注释。打开目标页面的控制台,执行函数即可。


// 获取全部菜单
function getMenu() {
  return fetch("https://source.showdoc.com.cn/server/index.php?s=/api/item/info", {
    "headers": {
      "accept": "application/json, text/plain, */*",
      "accept-language": "zh,zh-CN;q=0.9,ja;q=0.8,ko;q=0.7,en;q=0.6,zh-TW;q=0.5",
      "content-type": "application/x-www-form-urlencoded",
      "sec-ch-ua": "\"Not_A Brand\";v=\"99\", \"Google Chrome\";v=\"109\", \"Chromium\";v=\"109\"",
      "sec-ch-ua-mobile": "?0",
      "sec-ch-ua-platform": "\"macOS\"",
      "sec-fetch-dest": "empty",
      "sec-fetch-mode": "cors",
      "sec-fetch-site": "same-site"
    },
    "referrer": "https://www.showdoc.com.cn/",
    "referrerPolicy": "strict-origin-when-cross-origin",
    "body": `item_id=${my_item_id}&keyword=&default_page_id=${my_default_page_id}&user_token=${my_user_token}&_item_pwd=null`,
    "method": "POST",
    "mode": "cors",
    "credentials": "omit"
  }).then(
    res => res.json()
  ).then(j => {
    console.log(j)
    // window.my_menu = j.menu
    return j.data.menu
  })
}

// 获取页面的markdown内容
function getPage(page_id) {
  return fetch("https://source.showdoc.com.cn/server/index.php?s=/api/page/info", {
    "headers": {
      "accept": "application/json, text/plain, */*",
      "accept-language": "zh,zh-CN;q=0.9,ja;q=0.8,ko;q=0.7,en;q=0.6,zh-TW;q=0.5",
      "content-type": "application/x-www-form-urlencoded",
      "sec-ch-ua": "\"Not_A Brand\";v=\"99\", \"Google Chrome\";v=\"109\", \"Chromium\";v=\"109\"",
      "sec-ch-ua-mobile": "?0",
      "sec-ch-ua-platform": "\"macOS\"",
      "sec-fetch-dest": "empty",
      "sec-fetch-mode": "cors",
      "sec-fetch-site": "same-site"
    },
    "referrer": "https://www.showdoc.com.cn/",
    "referrerPolicy": "strict-origin-when-cross-origin",
    "body": `page_id=${page_id}&user_token=${my_user_token}&_item_pwd=null`,
    "method": "POST",
    "mode": "cors",
    "credentials": "omit"
  }).then(
    res => res.json()
  ).then(j => {
    console.log(j)
    // window.my_menu = j.menu
    return j.data.page_content
  })
}

let sleep = function (time) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

async function deal_catalogs(catalogs) {
  for (let index = 0; index < catalogs.length; index++) {
    const element = catalogs[index];

    // 处理目录
    await deal_catalogs(element.catalogs)
    // 处理页
    await deal_pages(element.pages)
  }
}

async function deal_pages(pages) {
  for (var i = pages.length - 1; i >= 0; i--) {
    var page = pages[i]
    console.log(page.page_id)
    var page_content = await getPage(page.page_id)
    page.page_content = page_content

    // sleep 1秒
    await sleep(1*1000)
  }
}


async function main() {
  // 设置token等
  // my_user_token = '请填写token,也是可以自动获取的,执行一个fetch请求即可;或者localstory中的userinfo获取'
  my_user_token = JSON.parse(localStorage.getItem('userinfo')).user_token

  // 根据url获取item_id、default_page_id
  var paths = location.pathname.split('/')
  my_item_id = paths[1]
  my_default_page_id = paths[2] || ''
  my_menu = {}
  // 获取所有menu
  getMenu()
  .then(async menu => {
    my_menu = menu
    // 处理目录
    await deal_catalogs(menu.catalogs)
    // 处理页
    await deal_pages(menu.pages)

    // 打印信息
    var d = {
      my_item_id: my_item_id,
      my_default_page_id: my_default_page_id,
      "item_type": "1",
      "item_name": "==",
      "item_description": "====",
      "password": "***",
      "pages": my_menu
    }
    console.log( JSON.stringify(d) )
  })
}

await main()

代码特点

  • 所有函数都是同步的,使得逻辑相对简单
  • 递归调用,每个函数执行明确的逻辑,保证正常退出循环
  • 使用浏览器生成的fetch函数,不用纠结请求的参数传递问题

问题

  • 部分文档生成的json无法直接给apifox使用,需要将其中的中文等特殊字符转换为unicode编码,使用了几个网上的工具,都无法完全解决导入问题。
    • https://khz.gitee.io/Ctool/tool.html#/tool/unicode 部分page导入失败
    • https://c.runoob.com/front-end/3602/ 导入后的文章会包含%编码

📖 参考资料

  • 本文链接 https://blog.csdn.net/kinghzking/article/details/129111792

**ps:**文章中内容仅用于技术交流,请勿用于违规违法行为。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/357328.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

String intern方法理解

1、原理 参考学习视频&#xff1a; https://www.bilibili.com/video/BV1WK4y1M77t/?spm_id_from333.337.search-card.all.click&vd_source4dc3f886f5ce1d43363b603935f02bd1 String s1 “hello”; String s1 "hello"; 代码原理解释如下图String s1 new Str…

进程章节总结性实验

进程实验课笔记 本节需要有linux基础&#xff0c;懂基本的linux命令操作即可。 Ubuntu镜像下载 https://note.youdao.com/s/VxvU3eVC ubuntu安装 https://www.bilibili.com/video/BV1j44y1S7c2/?spm_id_from333.999.0.0 实验环境ubuntu22版本&#xff0c;那个linux环境都可以…

Linux-VMware常用设置(时间+网络)及网络连接激活失败解决方法-基础篇②

目录一、设置时间二、网络设置1. 激活网卡方法一&#xff1a;直接启动网卡&#xff08;仅限当此&#xff09;方法二&#xff1a;修改配置文件&#xff08;永久&#xff09;2. 将NAT模式改为桥接模式什么是是NAT模式&#xff1f;如何改为桥接模式&#xff1f;三、虚拟机网络连接…

20230219 质心和重心的区别和性质

质心&#xff1a;&#xff08;无需重力场的前提&#xff09;所有质点的位置关于它们的质量的加权平均数。 重心&#xff1a;&#xff08;需要重力场的前提&#xff09;重力对系统中每个质点关于重心的力矩之和为零。 质心&#xff1a; xˉ∑i1nmixi∑i1nmi,yˉ∑i1nmiyi∑i1nmi…

Fiddler的报文分析

目录 1.Statistics请求性能数据 2.检测器&#xff08;Inspectors&#xff09; 3.自定义响应&#xff08;AutoResponder&#xff09; 1.Statistics请求性能数据 报文分析&#xff1a; Request Count: 1 请求数&#xff0c;该session总共发的请求数 Bytes …

vue3.0 生命周期

目录前言&#xff1a;vue3.0生命周期图例1.beforeCreate2.created3.beforeMount/onBeforeMount4.mounted/onMounted5.beforeUpdate/onBeforeUpdate6.updated/onUpdated7.beforeUnmount/onBeforeUnmount8.unmounted/onUnmounted案例&#xff1a;总结前言&#xff1a; 每个Vue组…

智慧城市应急指挥中心数字化及城市驾驶舱建设方案

目 录 第一章 项目概述 1.1 项目背景 1.2 项目范围 第二章 建设内容 2.1 三维可视化平台 2.1.1 多源数据接入 2.1.2 可视化编排 2.1.3 三维可视化编辑 2.1.4 空间数据可视化 2.1.5 集成框架支持 2.2 可视化场景定制开发 2.2.1 城市驾驶总舱 2.2.2 城市安全分舱 2.…

PLT/PDF转CAD:scViewerX 8.1 Crack

scViewerX是一个功能强大的 ActiveX 控件&#xff0c;允许您查看、打印和转换 PLT、Adobe PDF、Autodesk DWF、CGM、Calcomp、HPGL/2、Gerber、TIF、CALS 和其他几种格式。 ScViewerX 可以将您的文件转换为多种不同的输出文件格式&#xff0c;包括 PDF、PDF/A、TIFF、DXF、DWF、…

【人工智能AI】三、NoSQL 实战《NoSQL 企业级基础入门与进阶实战》

帮我写一篇介绍NoSQL的技术文章&#xff0c;文章标题是《NoSQL 实战》&#xff0c;不少于3000字。这篇文章的目录是 3.NoSQL 实战 3.1 MongoDB 入门 3.1.1 MongoDB 基本概念 3.1.2 MongoDB 安装与配置 3.1.3 MongoDB 数据库操作 3.2 Redis 入门 3.2.1 Redis 基本概念 3.2.2 Red…

windows微软商店下载应用失败/下载故障的解决办法;如何在网页上下载微软商店的应用

一、问题背景 设置惠普打印机时&#xff0c;需要安装hp smart&#xff0c;但是官方只提供微软商店这一下载渠道。 点击安装HP Smart&#xff0c;确定进入微软商店下载。 完全加载不出来&#xff0c;可能是因为开了代理。 把代理关了&#xff0c;就能正常打开了。 但是点击“…

IsADirectoryError: [Errno 21] Is a directory【已解决】

问题描述 生成数据&#xff0c;存储时候报错。 IsADirectoryError: [Errno 21] Is a directory: /home/LIST_2080Ti/njh/CHB-MIT-DATA/epilepsy_eeg_classification/data_processing/chb28/520.csv 问题分析 按我的认知&#xff0c;python执行的时候&#xff0c;比如这句 d…

FLAT:Flat-LAttice Transformer

中文NLP的一个问题&#xff0c;就是中文的字除了句句之间有标点符号之外都是连在一起的&#xff0c;不像英文词语是单独分割的。中文NLP处理一般会有2种方式&#xff1a;基于字的&#xff0c;char-level。现在比较常用的方法&#xff0c;但会缺少词组的语义信息。基于词的&…

TCP流套接字编程

ServerSocket API ServerSocket 是创建TCP服务端Socket的API。 ServerSocket 构造方法&#xff1a; ServerSocket 方法&#xff1a; Socket API Socket 是客户端Socket&#xff0c;或服务端中接收到客户端建立连接&#xff08;accept方法&#xff09;的请求后&#xff0…

【Java集合类】ArrayList

内部结构 ArrayList内部核心是一个Object数组elementDataObject数组的长度&#xff08;length&#xff09;视为ArrayList当前的容量&#xff08;capacity&#xff09;size对象表示ArrayList当前的元素个数 类上的重要注释 内部是Object数组 允许put null值,会自动扩容 size、…

基于springboot+vue的个人健康信息服务平台

基于springbootvue的个人健康信息服务平台 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背…

Spring Cloud Nacos实战(八) - Nacos集群配置

Nacos集群配置 更改Nacos启动命令配置原理 我们现在知道&#xff0c;想要启动Naocs只需要启动startup.sh命令即可&#xff0c;但是如果启动3个Nacos那&#xff1f;所以如果我们需要启动多个Nacos&#xff0c;其实Nacos本身默认启动就是集群模式。 注意点&#xff1a;如果是l…

【Redis】Redis 有序集合 Zset 操作 ( 简介 | 查询操作 | 增加操作 | 删除操作 | 修改操作 )

文章目录一、有序集合 Zset二、查询操作1、查询 Zset 所有数据2、查询 Zset 所有数据和评分3、查询指定评分范围的 Zset 数据4、查询指定评分范围的 Zset 数据并从大到小排序5、统计指定评分范围的 Zset 数据个数6、查询指定元素在 Zset 有序集合中的排名三、增加操作1、向 Red…

Kotlin 32. Kotlin 多语言支持

Kotlin 多语言支持 对于 Kotlin 来说&#xff0c;当我们新建一个项目时&#xff0c;会默认在 values/ 文件夹下&#xff0c;生成一个 strings.xml 文件。比如说&#xff0c; <resources><string name"app_name">exampleNewProject</string> <…

Python数据容器、list列表、tuple元组、str字符串、数据容器(序列)切片、set集合、dict字典、字符串大小比较

数据来源 01 数据容器 为什么学习数据容器 数据容器 总结 02 列表 1&#xff09;列表定义 为什么需要列表 列表的定义语法 列表的定义方式 演示 """ 演示数据容器之:list列表 语法:[元素,元素,......] """ # 定义一个列表list my_list …

使用k8s创建nginx服务—通过ingress类型暴露

文章目录1、使用ingress暴露应用—以nginx为例2、使用master节点IP也可以通过域名访问nginx1、使用ingress暴露应用—以nginx为例 把ingress通过 DaemonSet 的方式部署集群中&#xff0c;而且该节点打上污点不允许业务pod进行调度&#xff0c;以避免业务应用与Ingress服务发生…