暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

使用 GitHub Actions 制作定时任务​

Golang学习杂记 2021-11-22
2549

介绍

GitHub Actions
是官方提供的一套 DevOps
工具,可以完成软件开发周期的任务,比如可以实现自动化测试,部署等,简单来理解,我们认为它给我们提供了一个服务器,在上面可以执行一系列的指令(不过会有一些限制),这就意味着我们可以用来白嫖,比如做定时任务,又不需要自己买服务器。

下面介绍一下如果使用 GitHub Actions
制作一个定时任务,每天定时发布天气情况,当然你学会了之后可以用于签到啊🤭。

前置准备

很简单,只要创建一个 GitHub
仓库,不妨叫做 scheduler
, 然后将仓库 git clone
到本地编辑器进行编写即可。

逻辑代码实现

这里我们做一个天气预报,每天都可以定时发送天气预报到我们的邮件里面去。

首先第一步,去哪里获取到天气的信息?

网上有很多天气查询的网站,部分网站提供了对应的 API
接口,但是需要密钥或者限制了调用次数,对于简单的使用来说,比较适合,这里采用爬虫技术从百度天气[1]上获取数据,不用受限于密钥和调用次数。

网站资源找到了,如何拉取数据下来?

一般来说,写爬虫的时候想要获取数据的时候,我们会使用两种方式进行查看:

  • 右键查看网页源代码
  • 分析网络请求,F12 即可

这里使用第一种方法就可以获取到了,如下为网页源代码,直接包含天气数据:


然后我们可以使用正则表达式,将对应的数据部分获取下来,并且解析为 JSON
数据,大概代码如下:

var dataPattern = regexp.MustCompile("window.tplData = (.*?);</script>")
// 省略天气数据定义....

func getWeather(city string) (*WeatherData, error) {
    // 发送网络请求,获取源代码
 resp, err := http.Get(fmt.Sprintf("http://weathernew.pae.baidu.com/weathernew/pc?query=%s&srcid=4982", city))
 if err != nil {
  return nil, err
 }
 defer resp.Body.Close()
    // 读取网页源码
 data, err := io.ReadAll(resp.Body)
 if err != nil {
  return nil, err
 }
 // 进行匹配,获取数据
 match := dataPattern.FindSubmatch(data)
 if len(match) == 0 {
  return nil, errors.New("Do not find weather data")
 }
 
    // 反序列化数据
 var res WeatherData
 err = json.Unmarshal(match[1], &res)
 if err != nil {
  return nil, err
 }
 return &res, err
}

解析之后,我们按照自己的想法拼接信息,然后将数据发送过去即可,这里采用邮件发送,代码如下,当然你可以使用包装更好的一些 package
,这里直接使用标准库。

func sendMail(to, content string) error {
 // 这里采用的是 126 邮箱,不同的邮箱 host 设置不同
 auth := smtp.PlainAuth("", emailUsername, emailPassword, "smtp.126.com")
 msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: %s; charset=UTF-8\r\n\r\n %s",
  emailUsername,
  to,
  "每日天气",
  "text/plain",
  content,
 )
 err := smtp.SendMail("smtp.126.com:25", auth, emailUsername, []string{to}, []byte(msg))
 if err != nil {
  fmt.Println("send email failed: ", err)
  return err
 }
 return nil
}

// 简单的示例,可以进行加工
const tpl = `{{.city}}天气情况:

今天是{{.date}},农历{{.lunar}},天气{{.weather}},伴有{{.wind_direction}},预测今日{{.precipitation_type}}
紫外线{{.uv}}, {{.uv_info}},pm2.5指标为{{.pm25}}, 属于{{.pm25_level}}
`


func main(){
    // ...
   
    t, err := template.New("weather").Parse(tpl)
    if err != nil {
        fmt.Println("Parse template failed: ", err)
        return
    }

    var buf = &strings.Builder{}
 
    // 将数据添加到模板中
    err = t.Execute(buf, map[string]string{
        "city":               city,
        "date":               data.Base.Date,
        "lunar":              data.Base.Lunar,
        "weather":            data.Weather["weather"],
        "wind_direction":     data.Weather["wind_direction"],
        "uv":                 data.Weather["uv"],
        "uv_info":            data.Weather["uv_info"],
        "precipitation_type": data.Weather["precipitation_type"],
        "pm25":               data.PSPm25.PSPm25,
        "pm25_level":         data.PSPm25.Level,
    })
    
    sendMail(to, buf.String())
}

定时任务设置

定时任务是这次的重点,设置起来其实也挺简单的。

首先我们需要创建文件夹 .github/workflows
,文件夹下面可以创建工作流程文件,只要是 .yml
文件都会作为一个单独的工作流程文件,每一个文件都会在事件触发的时候执行,比如在提交代码,提PR的时候等。

# 比如仅在push的时候执行
on: push
# push 或者 pull_request 的时候执行
on: [push, pull_request]
# 同时也可以指定分支
on:
  push:
    branches:
      - main

更加具体的语法可以参加官方文档[2]介绍,这里不再赘述,如果我们想要定义一个定时任务,那么触发事件可以类比下面的方式

on:
  schedule:
    - cron:  '30 5,17 * * *' # 每天5:30, 17:30执行,时间为国际标准时间,非北京时间!!

其中 cron
语法和 Linux
中的一致,如下

┌───────────── 分钟 (0 - 59)
│ ┌───────────── 小时 (0 - 23)
│ │ ┌───────────── 日期 (1 - 31)
│ │ │ ┌───────────── 月 (1 - 12 or JAN-DEC)
│ │ │ │ ┌───────────── 星期 (0 - 6 or SUN-SAT)
│ │ │ │ │
│ │ │ │ │
│ │ │ │ │
* * * * *

如果你不清楚输出的表示什么时间,那么你可以在 crontab guru[3] 中进行测试。而对于我们的天气预报来说,我们的工作流程文件可以定义为:

name: weather-report # 工作流程名称,会在网页中进行显示

on:
  push:  # 为了调试方便,修改代码之后push上去即可以看到效果
    paths: # 指定只有哪些文件修改了才会触发该工作流程
      - weather-report/**
      - .github/workflows/weather-report.yml
  schedule: # 定时任务
    - cron: "0 0 * * *" # 每天 0 点跑 => 东八区 8点

jobs:
  weather-report:
    runs-on: "ubuntu-latest" # 在什么机器上跑
    env: # 设置的一些 secret,在 settings 中可以设置
      EMAIL_USERNAME: ${{ secrets.EMAIL_USERNAME }}
      EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }}
      TO: ${{ secrets.TO }}
    steps: # 指定的步骤
      - uses: actions/checkout@v2 
      - uses: actions/setup-go@v2  # 使用 golang 
        with:
          go-version: "^1.17" # 版本号
      - run: go run weather-report/main.go -eu $EMAIL_USERNAME -ep $EMAIL_PASSWORD -to $TO  # 指定的命令

所有代码编写完成之后,push 到仓库中,便可以查看到正在执行的行为。

我们也可以进入具体的 workflow
 查看对应的信息。

总结

核心步骤其实很简单:

  • 创建仓库,clone
    下来
  • 进行逻辑代码编写
  • 创建 .github/workflows
    目录,并编写工作流程文件
  • push
    代码上去,事件触发时执行
工作流程文件的编写并不是很复杂,如果我们会 docker-compose.yml
的编写,很容易就清楚基本内容的编写规则,其他需要的语法和具体使用方法到时候翻翻官方文档即可,此外,官方也提供了大量的模板供使用。

上述的这个定时任务其实并没有什么很大的使用之处,毕竟人手一台手机,天气点一点就是知道的事了,而且比这个方便美观多了,其实更重要是学习怎么利用爬虫去获取简单的数据,以及 GitHub Actions
的使用,如果把签到作为定时任务,那么定会得到极大的解放🧐。

不过,GitHub Actions
的使用当然不仅限于此,自动化测试,自动化部署都可以通过它实现,说不定还有更多好玩的等着你呢~

代码实现见 GitHub[4] 

链接

[1]

百度天气: http://weathernew.pae.baidu.com/weathernew/pc?query=成都&srcid=4982

[2]

官方文档: https://docs.github.com/en/actions

[3]

crontab guru: https://crontab.guru/

[4]

GitHub: https://github.com/junhaideng/scheduler

文章转载自Golang学习杂记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论