likes
comments
collection
share

10秒自动生成Markdown文件:轻松高效完成任务

作者站长头像
站长
· 阅读数 4

背景

最近有一个小需求,就是从网上爬取内容下来,然后在生成对应的markdown文件,本篇主要记录一下这个过程

生成markdown

生成markdown文件过程如下所示

10秒自动生成Markdown文件:轻松高效完成任务

爬取html内容

在nodejs中爬取html文件的方式有多,这里以axios为例

const url = `https://baike.baidu.com/item/${name}?fromModule=lemma_search-box`
const { data } = axios.get(url) // data就是html

爬取html内容的时候,可能会碰到2个常见的问题

  1. 接口返回403,这是因为服务端判断了一下请求是否来自浏览器客户端,如果不是则直接禁止访问
  2. 接口超时,这是因为我们在国内访问国外的资源,网络较慢,或者被墙了导致无法正常访问

解决403的问题,一般在headers里面带上User-Agent即可,如下所示

// 可以创建一个新实例,所有请求公用
this.request = axios.create({
    headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
    }
})

// 也可以单个请求的时候传User-Agent
axios.get(movieUrl, {
  headers: {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
  }
})

解决超时的问题,一般可以通过设置代理,走我们梯子的本地代理服务来访问国外站点

const { HttpsProxyAgent } = require("https-proxy-agent");

const httpsAgent = new HttpsProxyAgent(`http://127.0.0.1:1087`);

axios.get(movieUrl, {
    // proxy: { // 这种方式不行,response会返回空字符串
    //     protocol: 'http',
    //     host: '127.0.0.1',
    //     port: 1087,
    // },
    httpsAgent,
    proxy:false,
})

注意:在给axios配置代理的时候,先使用的是axios proxy配置,但是发现请求是可以正常响应,但是response.data为空字符串,所以后面换成了传入httpsAgent,并且将proxy:设置成false这种代理设置方式,这样response.data就能正常获取到内容

解析html内容

拿到html之后,怎么从html里面获取到自己想要的信息,这里可以选择的方式有很多种,这里选择最常用的cheerio,通过cheerio来获取html中我们想要的信息

cheerio使用起来相当简单

  1. 安装依赖
  2. load html内容
  3. 通过选择器命中对应的元素
  4. 通过cheerio提供的方法,操作元素内容、属性、样式等

安装依赖

pnpm add cheerio

load html内容

const $ = cheerio.load(html)  //$是 cherrio 规定的

通过选择器命中对应元素

// 标签选择器
$('c-header-content');

// 属性选择器
$('c-list-doc[data-title="测试"]')

// 类选择器
$('.J-lemma-content')

通过cheerio提供的方法,操作元素内容、属性、样式等

// 获取文本内容
$('.J-lemma-content').text()

// 获取属性值
$('c-doc-content').attr('data-original-src');

下载图片

npm里面提供了很多下载图片的包,而这次我们使用的比较简单,直接使用axios下载图片即可,代码如下所示

async downloadImage(url, filename) {
    // 将 responseType设置成arraybuffer即可
    const response = await axios.get(url, { responseType: 'arraybuffer' });

    // 然后通过fs.writeFile写入到本地
    return fs.writeFile(filename, response.data);
}

try {
  await downloadImage(图片url, 本地路径)
} catch(err) {
  console.log('图片下载失败', err)
}

上传图片

有时候我们并不想自己写一个服务来返回上一步下载的图片,这时候我们会想到图床

常用的免费图床有

收费图床有

  • 又拍云
  • 腾讯云cos
  • 阿里云的oss

等更多图床内容可以参考最全的图床集合(国内外,站长必备)

这里我们选择免费的hello图床,hello图床有对应的api文档,比如上传图片调用方式如下所示

loadImage(filePath) {
    const data = new FormData();
    const file = fs.createReadStream(filePath)
    data.append('file', file)
    data.append('permission', 0)
    // 返回值就会包括图片的url 
    return this.request.post(`https://www.helloimg.com/api/v1/upload`, data, {
        headers: {
            'Content-Type': 'multipart/form-data',
            "Authorization": "Bearer xxxxxxxxx",
            Accept: 'application/json'
        }
    })
}

需要注意的是file只能传单个图片,不支持传数组,更多的使用方式可以参考接口文档,还有就是免费的是有调用次数、频率、存储限制的,如果要求高的话,可以考虑其它图床

生成模版

当我们数据都准备好之后,最后就只剩生成内容了,生成内容的方式有很多中,比如直接使用模版字符串生成,如下所示

fs.writeFileSync(`${articsDir}/${name}.md`, `
标题:xxx
图片:${imgUrl}
`)

如果我们的模版内容比较简单,那么通过模版字符串是完全没问题的,但是当我们的模版内容比较复杂,比如有if、else if、遍历等比较复杂的场景时,这时候通过模版字符串就不太好处理,或者代码会不太美观了,这时候我们可以借助模版引擎,只需要两步就可以帮助我们生成内容

  • 第一步:创建对应的模版
  • 第二步:传入对应的数据

这里使用nunjucks模版引擎

创建模版

## 概况
{% for item in actorInfo %}
**{{ item.actor }}**<br/>
{% for it in item.info %}{{ it }}<br/>{{'\n'}}{% endfor %}
简介:{{ item.abstract.trim() }}<br/>
**{{ item.comment }}**<br/>
图片:
{% for img in item.tcImageUrls %}
![]({{img}})
{% endfor %}
{% else %}
  主角信息不存在
{% endfor %}

## 介绍


{% if bkSummary %}
{{bkSummary}}
{% else %}
{{summary}}
{% endif %}

传入数据

const nunjucks = require('nunjucks')

nunjucks.configure({ autoescape: true });

const content = nunjucks.renderString(fs.readFileSync(this.articTemplatePath, 'utf-8'), {
    ...info,
});

fs.writeFileSync(`${articsDir}/${name}.md`, content)

总结

整个过程并不复杂,让我觉得最有用的可能还是使用模版引擎nunjucks生成markdown的这个步骤,因为可以根据事先定义的模版来生成最终的产物,那么在一些需要动态创建文件的场景就会非常有用,因为这种模版引擎的方式相比于模版字符串的方式,代码的可读性会高很多