51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

【随笔】自动化油价推送:GitHub Actions 实战

前言 {#前言}

今年自五月份以来发生了许多事情,其中一个显著的变化是我购买了一辆车。刚开始觉得购车挺便捷的,然而提车的第一个月油费竟然直逼 1300 元,让我对这部分开支感到有些心疼。因此,我决定开发一个油价推送小程序,以便获取当前所在城市油价的实时变化情况。通过这个小程序,我可以方便地了解油价的走势,从而更好地掌握预算。为了使这个小程序更加实用,我将项目上传到了 GitHub,并且利用 Actions 定时在每天下午 4 点半自动推送油价变化。以下是我实现这一想法的具体思路。

获取源数据 {#获取源数据}

在网上找到一个油价网站,http://www.qiyoujiage.com , 定位到自己所在的具体地址,例如:http://www.qiyoujiage.com/hubei/xxx.shtml ,通过 jsoup 抓取关键数据,如 92#-0# 汽油价格等其他自己需要的数据。

项目开始 {#项目开始}

创建一个普通 maven 项目,依赖如下:

<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <hutool-all.version>5.8.23</hutool-all.version>
    <jsoup.version>1.16.1</jsoup.version>
    <thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
    <javax.mail-api.version>1.6.2</javax.mail-api.version>
</properties>
<dependencies>`
`<!-- 工具类 -->`
`<dependency>`
`<groupId>`cn.hutool`</groupId>`
`<artifactId>`hutool-all`</artifactId>`
`<version>`${hutool-all.version}`</version>`
`</dependency>`
`<!-- 爬虫框架 -->`
`<dependency>`
`<groupId>`org.jsoup`</groupId>`
`<artifactId>`jsoup`</artifactId>`
`<version>`${jsoup.version}`</version>`
`</dependency>`
`<!-- 邮件模板 -->`
`<dependency>`
`<groupId>`org.thymeleaf`</groupId>`
`<artifactId>`thymeleaf`</artifactId>`
`<version>`${thymeleaf.version}`</version>`
`</dependency>`
`<dependency>`
`<groupId>`javax.mail`</groupId>`
`<artifactId>`javax.mail-api`</artifactId>`
`<version>`${javax.mail-api.version}`</version>`
`</dependency>`
`<!-- Java Mail -->`
`<dependency>`
`<groupId>`com.sun.mail`</groupId>`
`<artifactId>`javax.mail`</artifactId>`
`<version>`${javax.mail-api.version}`</version>`
`</dependency>`
`</dependencies>

JDK 版本尽量保持一致,毕竟 SpringBoot 3.0 所要求的 JDK 版本最低都是 Java17 了,抱着 Java8 养老都不够用了。

具体的功能实现 {#具体的功能实现}

package com.mobaijun.oil;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;


import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**

`
`
* 
  Description: [油价解析]





* 
  Author: [mobaijun]





* 
  Date: [2023/12/2 19:09]






* `
  `IntelliJ IDEA Version: [IntelliJ IDEA 2023.1.4]
  */
  public class OilParse {
  `
  `

  /**
  `
  `

  * 日志打印
    */
    private static final Log log = LogFactory.get(OilParse.class);


  `
  `

  /**
  `
  `

  * 爬取地址
    */
    private static final String START_URL = "http://www.qiyoujiage.com/hubei/shiyan.shtml";


  `
  `

  /**
  `
  `

  * 汽油说明
    */
    private static final String DE_SCRIPT_GASOLINE_MODEL = """
    92 汽油的平均密度为 0.725kg/L,一升 92 号汽油为 0.725 千克;
    95 号汽油的密度为 0.737g/ml,一升 95 号汽油为 0.737 千克;
    0 号柴油的密度在 0.8400--0.8600g/cm⒊之间,一升 0# 柴油大约是 0.84 千克;""";


  `
  `

  /**
  `
  `

  * 邮件发送


  * 


  * @param args 参数
    */
    public static void main(String[] args) {
    String oilPriceEmail = generateOilPriceEmail(parseOilData());
    MailAccount account = createMailAccount();
    sendMail(account, oilPriceEmail);
    }


  `
  `

  private static Map<String, String> parseOilData() {
  Map<String, String> oilMap = new LinkedHashMap<>();
  try {
  Document document = Jsoup.connect(OilParse.START_URL).timeout(3000).get();
  String you = Objects.requireNonNull(document.getElementById("youjia")).text();
  oilMap.putAll(splitData(you));
  } catch (IOException e) {
  log.error("HTML parsing failed, please try again! error message:{}", e.getMessage());
  }
  return oilMap;
  }
  `
  `

  private static Map<String, String> splitData(String inputData) {
  Map<String, String> keyValuePairs = new LinkedHashMap<>();
  Pattern pattern = Pattern.compile("(\S+)\s(\S+)");
  Matcher matcher = pattern.matcher(inputData);
  while (matcher.find()) {
  String key = matcher.group(1);
  String value = matcher.group(2);
  keyValuePairs.put(key, value);
  }
  return keyValuePairs;
  }
  `
  `

  private static MailAccount createMailAccount() {
  MailAccount account = new MailAccount();
  account.setHost("smtp.163.com");
  account.setPort(465);
  account.setUser("mobaijun8@163.com");
  account.setPass("你的邮箱授权密码 - 具体可 Google");
  account.setFrom("mobaijun8@163.com");
  account.setCharset(StandardCharsets.UTF_8);
  account.setStarttlsEnable(false);
  account.setSslEnable(true);
  return account;
  }
  `
  `

  private static void sendMail(MailAccount account, String oilPriceEmail) {
  try {
  MailUtil.send(account, "mobaijun8@163.com", "湖北今日油价", oilPriceEmail, true);
  log.info("邮件发送成功!");
  } catch (Exception e) {
  log.error("邮件发送失败:" + e.getMessage());
  }
  }
  `
  `

  private static String generateOilPriceEmail(Map<String, String> oilData) {
  // 读取 Thymeleaf 模板
  String template = readTemplate();
  // 使用 Thymeleaf 引擎进行数据填充
  TemplateEngine templateEngine = new TemplateEngine();
  Context context = new Context();
  context.setVariable("title", "湖北今日油价");
  context.setVariable("subTitle", "每日即时更新 单位: 元 / 升");
  context.setVariable("oilData", oilData);
  context.setVariable("oilDeScript", DE_SCRIPT_GASOLINE_MODEL);
  return templateEngine.process(template, context);
  }
  `
  `

  /**
  `
  ``
  `
  * 读取 Template


  * 



  * `@return 内容
    */``
    `private` `static` `String` `readTemplate()` `{`
    `try` `(InputStream` inputStream `=` `new` `ClassPathResource("oil_price_template.html").getStream())` `{`
    `return` `IoUtil.read(`inputStream`,` `StandardCharsets.UTF_8);`
    `}` `catch` `(IOException` e`)` `{`
    log`.error("模板文件读取失败:{}"` `+` e`.getMessage());`
    `return` `"";`
    `}`
    `}`
    `}

新建邮件模板 {#新建邮件模板}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <style>
        body {
            background-image: url('https://tencent.cos.mobaijun.com/img/icon/background.jpg');
            background-size: cover;
            /* 使用深色背景 */
            background-color: #333;
            /* 将文字颜色改为黑色 */
            color: #000; /* 黑色 */
            font-family: Arial, sans-serif;
        }

        <span class="token selector">h1</span> <span class="token punctuation">{</span>
            <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token selector">table</span> <span class="token punctuation">{</span>
            <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
            <span class="token property">border-collapse</span><span class="token punctuation">:</span> collapse<span class="token punctuation">;</span>
            <span class="token property">margin</span><span class="token punctuation">:</span> 20px 0<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token selector">th, td</span> <span class="token punctuation">{</span>
            <span class="token property">border</span><span class="token punctuation">:</span> 1px solid #54d1d5<span class="token punctuation">;</span>
            <span class="token property">padding</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span>
            <span class="token property">text-align</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token selector">th</span> <span class="token punctuation">{</span>
            <span class="token property">background-color</span><span class="token punctuation">:</span> #70a0a4<span class="token punctuation">;</span>
            <span class="token property">color</span><span class="token punctuation">:</span> #030303<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token selector">p</span> <span class="token punctuation">{</span>
            <span class="token property">margin</span><span class="token punctuation">:</span> 20px 0<span class="token punctuation">;</span>
            <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token comment">/* 黑色 */</span>
        <span class="token punctuation">}</span>

        <span class="token selector">h3</span> <span class="token punctuation">{</span>
            <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
            <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token comment">/* 黑色 */</span>
        <span class="token punctuation">}</span>

        <span class="token selector">blockquote</span> <span class="token punctuation">{</span>
            <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
            <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span> <span class="token comment">/* 黑色 */</span>
        <span class="token punctuation">}</span>
    </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">&gt;</span></span>



</head>`
`<body>`
`<h1 th:text="${title}">`湖北今日油价`</h1>`
`<p style="text-align: center;"
th:text="| 当日更新时间: ${#temporals.format(#temporals.createNow().plusHours(8),'yyyy-MM-dd HH:mm:ss')} 单位: 元 / 升 |"></p>`
`<table>`
`<thead>`
`<tr style="background-color: rgba(112, 160, 164, 0.5);">`
`<th>`汽油型号`</th>`
`<th>`油价`</th>`
`</tr>`
`</thead>`
`<tbody>`
`<tr th:each="entry : ${oilData}">`
`<td th:text="${entry.key}">`Type`</td>`
`<td th:text="${entry.value}">`Price`</td>`
`</tr>`
`</tbody>`
`</table>`
`<!-- 型号描述 -->`
`<h3 style="text-align: center; accent-color: #030303;">`汽油型号说明`</h3>`
`<blockquote style="text-align: center;" th:utext="${#vars.oilDeScript.replaceAll(';','</br></br>')}">`汽油型号简介
`</blockquote>`
`</body>`
`</html>

创建 ci.yml 文件 {#创建 -ci-yml- 文件}

name: Run Java Main Method

on:
workflow_dispatch:
schedule:
- cron: '30 8 * * *'


jobs:
run-java-main:
runs-on: ubuntu-latest


    <span class="token key atrule">steps</span><span class="token punctuation">:</span>
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Checkout repository
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/checkout@v2

      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Set up Java
        <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/setup<span class="token punctuation">-</span>java@v2
        <span class="token key atrule">with</span><span class="token punctuation">:</span>
          <span class="token key atrule">distribution</span><span class="token punctuation">:</span> <span class="token string">'adopt'</span>
          <span class="token key atrule">java-version</span><span class="token punctuation">:</span> <span class="token string">'21'</span>
      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Add execute permission to mvnw
        <span class="token key atrule">run</span><span class="token punctuation">:</span> chmod +x mvnw

      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Build with Maven
        <span class="token key atrule">run</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token scalar string">
          ./mvnw -B package --file ./pom.xml</span>

      <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Run Java Main Method
        <span class="token key atrule">run</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token scalar string">
          java -jar target/oil-price-push-jar-with-dependencies.jar</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>



 
配置 maven 插件 {#配置 -maven- 插件}
----------------------------


 
```markup
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>21</source>
                <target>21</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.mobaijun.oil.OilParse</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <!-- 这个 jar-with-dependencies 是 assembly 预先写好的一个,组装描述引用 -->
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <!-- 工程名 -->
                <finalName>${project.name}</finalName>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

```


 
最终效果 {#最终效果}
------------


 
![](http://static.51tbox.com/static/2025-01-10/col/11abd3882e99e440ec27e77361a94b21/8ef5f02fa73f436e966ce00402d67681.gif.jpg)

 
>   通过以上步骤就完成了一个简单的油价推送小程序,如有什么问题,欢迎在评论区留言,讨论!
>
>  

赞(0)
未经允许不得转载:工具盒子 » 【随笔】自动化油价推送:GitHub Actions 实战