本篇文章预计阅读时间 6 分钟,使用技术包含:
– Github Action
– Nodejs
– Puppeteer+Chromium

最近有空闲,做了一个有意思的小玩意,通过Github Action能定时对我的网站首页做截图并保存起来,具体是怎么实现的呢?这就不得不介绍一个非常实用的插件Puppeteer 了。

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.
摘自 puppeteer 官方介绍

Puppeteer 是一个针对Chromium API进行封装的Node包,其可以通过几个简单的函数就实现对网页的操作,而且因为使用的是Chromium内核,所以使用ReactVueAngularJS等前端渲染的页面,也能完全载入不受影响,可以说和我们用浏览器访问的效果是一样的。

安装

安装puppeteer,安装时需要注意,是需要将对应版本的Chromium也安装的。

npm i puppeteer

然后需要进入puppeteer包内安装下依赖,这样对应版本的chromium就安装上了。

cd node_modules/puppeteer
npm install

接下来,新建一个js文件

touch index.js

编码

开始编写代码,首先引入puppeteer,定义截图的网站地址以及存储路径

const website = 'https://developerhome.net';
const snapshotPathRoot = './snapshot/'
const puppeteer = require('puppeteer');

因为网站首页采用了延迟加载,需要下拉页面多次来实现让页面的延迟部分加载,同时为了保证翻页后能保证图片等都能载入完成,就需要每次完成操作后都进行一定时间的休眠,而JavaScript里的休眠是采用setTimeout回调方式来进行的,为了方便简单通过Promise实现一个sleep函数,能休眠需要的时间,这样通过await来调用这个函数,就能实现同步的休眠。

// 此函数调用方式为 await sleep(1); 表示休眠一秒
const sleep = (second) => {
    return new Promise((resolve, reject) => {
        console.debug('start to sleep '+second+' seconds...');
        // 到达休眠时间后调用resolve函数
        setTimeout(resolve, second*1000);
    }).then(() => {
        console.debug(second+' seconds up!')
    })
}

下面来实现具体的业务逻辑

(async () => {
    console.debug('launch browser...')
    // 启动一个浏览器
    const browser = await puppeteer.launch();
    console.debug('create new page...')
    // 创建一个标签页
    const page = await browser.newPage();
    // 配置窗口大小和PC浏览
    await page.setViewport({
        width: 1280,
        height: 960,
        isMobile: false,
    })
    // 开启js有效
    await page.setJavaScriptEnabled(true)
    // 配置跳转无超时时间
    await page.setDefaultNavigationTimeout(0)
    // 配置默认无超时时间
    await page.setDefaultTimeout(0)
    console.debug('start to visit website: '+website)
    // 标签页打开网站
    await page.goto(website);
    // 定义截图名称,使用ISO时间做图片名称
    const filename = (new Date()).toISOString()+".png"
    console.debug('screenshot website to file: '+snapshotPathRoot+filename)
    // 连续多次按PageDown下翻页,让延迟加载的内容触发加载
    for (let i = 0; i < 5; i++) {
        await page.keyboard.press('PageDown')
        // 每次按完了休眠0.5秒,以便让页面资源载入
        await sleep(0.5)
    }
    // 再按同样次数的上翻页,回到页面最头部
    for (let i = 0; i < 5; i++) {
        await page.keyboard.press('PageUp')
        // 每次按完了休眠0.5秒,以便让页面资源载入
        await sleep(0.5)
    }
    console.debug('wait page load...')
    // 休眠2秒,以便让页面资源载入
    await sleep(2)
    console.debug('time up, start screenshot')
    // 对当前页面做截图,配置存储文件绝对路径,并配置截全页面
    await page.screenshot({
        path: snapshotPathRoot+filename,
        fullPage: true
    })
    console.debug('wait close browser...')
    // 关闭浏览器
    await browser.close()
    console.debug('all close exit.')
})();

以上这个脚本就是一个一次性脚本,实现了一次对目标网站的地址打开,并加载延迟加载内容后进行了全页面截图并保存在定义的目标目录下,并用当前ISO格式的时间作为截图文件名。

自动化

脚本已经实现完成了,下一步就需要一个定期执行这个脚本的方式,而免费的Github Action更是首选,主要是因为
- Github 服务器都在国外,其下载Node依赖包会非常快。
- 配置简单,有完善的机制
- 实现自动化截图的同时还能作为仓库存储每次的截图

Github Action配置很简单,在Github仓库首页点击Actions标签页进入Github Action配置页面
点击进入GithubActions页面
点击 New workflow新建一个工作流
点击新建一个工作流
选择自定义创建工作流
选择自定义创建工作流
会提示在 .github/workflows 目录下新建一个 .yml 文件
工作流配置保存位置
main.yml文件修改为如下代码

# This is a basic workflow to help you get started with Actions
# 工作流名称
name: CI
​
# Controls when the workflow will run
# on 表示在什么时候触发工作流去执行
on:
  # 在push代码到仓库时执行
  push:
  # 定时执行,这里 - cron 配置的就是一个定期执行的表达式
  # 0 0,6,12,18 * * * 表示的在0,6,12,18点整点执行,这里是ISO时间,和国内差8小时
  schedule:
    - cron: '0 0,6,12,18 * * *'
​
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
# 工作流内容
jobs:
  # This workflow contains a single job called "build"
  # 将工作命名为 build
  build:
    # The type of runner that the job will run on
    # 配置运行工作的系统环境,这里是用最新版本的ubuntu来执行
    runs-on: ubuntu-latest
​
    # Steps represent a sequence of tasks that will be executed as part of the job
    # 具体执行d的步骤
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      # 使用checkout@v2 action,也就是检出代码
      - uses: actions/checkout@v2
​
      # 因为执行的环境没有中文字体,抓中文网站会乱码,所以需要添加中文字体
      - name: add chinese fonts
        # run是具体执行的内容,多行的话,需要有这么一个 '|'
        run: |
          # 安装字体的环境
          sudo apt-get install ttf-mscorefonts-installer
          sudo apt-get install fontconfig
          fc-list :lang=zh
          ls -lh /usr/share/fonts/
          # 拷贝仓库里fonts路径下的所有字体文件到系统字体目录下
          # 我在仓库d的fonts目录下放了微软雅黑字体
          cp -rf ./fonts/* /usr/share/fonts/
          mkfontscale
          mkfontdir
          fc-cache
          fc-list :lang=zh

      # 缓存安装的node modules 这里使用的是github action中关于node modules方式
      # 官方文档拷贝直接使用的
      - name: Cache node modules
        uses: actions/cache@v2
        env:
          cache-name: cache-node-modules
        with:
          # npm cache files are stored in `~/.npm` on Linux/macOS
          path: ~/.npm
          key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-build-${{ env.cache-name }}-
            ${{ runner.os }}-build-
            ${{ runner.os }}-

      # 安装node依赖
      - name: Install Dependencies
        run: npm install
​
      # Runs a single command using the runners shell
      # 来时执行具体脚本
      - name: Run script
        run: node index.js

      # 打印截图文件夹内容,包含了新的截图
      - name: Print result
        run: ls -lh ./snapshot
​
      # 将新的截图用git提交到仓库中
      - name: Push snapshot
        run: |
          git config --global user.name 'Jarod2011'
          git config --global user.email 'jarod2011@users.noreply.github.com'
          git add snapshot/
          git commit -am "push new snapshot"
          git push

这个脚本就是一次网站的整体截图并提交到仓库中的过程,其中增加了cache方式,缓存了安装的依赖,并安装了中文字体解决截图乱码问题,最后将截图提交到仓库中保存。

总结

总结下,Puppeteer是一个非常不错的包,优点是可以用它实现程序对浏览器的操作,用于截图、一些固定顺序类操作,可以直接用程序去模拟完成,但它的缺点就是每次浏览其实都是需要将页面所有资源都载入完成,对于做爬虫类要求速度的程序,则有些太慢了。
Github Action 是一个不错的CD/CI工具,也让更多的仓库从 Travis CI 迁移改为 Github Action,而且语法也简单,是个不错的选择。

本文所有代码在 https://github.com/jarod2011/website_snapshot

3 个评论

评论功能已关闭。