feat: 完成基本功能
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
data/
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "oil",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "查询当前油价",
|
||||||
|
"keywords": [
|
||||||
|
"oil"
|
||||||
|
],
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"author": "hbk01",
|
||||||
|
"type": "module",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "nodemon ./src/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.9.0",
|
||||||
|
"cheerio": "^1.0.0",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"nodemon": "^3.1.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/index.html
Normal file
82
src/index.html
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-cmn-Hans">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>油价</title>
|
||||||
|
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(() => {
|
||||||
|
$.getJSON('/query', (data) => {
|
||||||
|
const tableBody = $('#table-body');
|
||||||
|
data.forEach(item => {
|
||||||
|
if (item.name == "更新时间") {
|
||||||
|
$('#update-time').text(item.time.replace("T", " "))
|
||||||
|
} else {
|
||||||
|
const row = `<tr>
|
||||||
|
<td>${item.name}</td>
|
||||||
|
<td>${item["92#"]}</td>
|
||||||
|
<td>${item["95#"]}</td>
|
||||||
|
<td>${item["0#"]}</td>
|
||||||
|
</tr>`
|
||||||
|
tableBody.append(row)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).fail(() => {
|
||||||
|
alert('数据加载失败,请稍后再试。')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
thead tr th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
color: black;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid gray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>地区</th>
|
||||||
|
<th>92#</th>
|
||||||
|
<th>95#</th>
|
||||||
|
<th> 0#</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="table-body">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div style="margin-top: 50px;">
|
||||||
|
<p>
|
||||||
|
数据来源:小熊油耗<br>
|
||||||
|
更新时间:<span id="update-time"></span>
|
||||||
|
<p>
|
||||||
|
<span style="color: red;">免责声明:本数据仅供参考,具体油价请以当地加油站实际价格为准。</span>
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
127
src/index.js
Normal file
127
src/index.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import * as cheerio from 'cheerio'
|
||||||
|
import express from 'express'
|
||||||
|
import path from 'path'
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
const __dirname = path.resolve()
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, 'src', 'index.html'))
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/query', async (req, res) => {
|
||||||
|
let { name, force } = req.query
|
||||||
|
if (force == undefined) {
|
||||||
|
force = false
|
||||||
|
}
|
||||||
|
const data = getOilPriceJson(force)
|
||||||
|
if (name) {
|
||||||
|
const filteredData = data.filter(item => item.name.includes(name))
|
||||||
|
res.json(filteredData)
|
||||||
|
} else {
|
||||||
|
res.json(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(1933, () => {
|
||||||
|
console.log('Server is running on port 1933')
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取油价数据,若本地数据存在则读取本地数据,否则请求网络数据,并保存到本地以供后续使用。
|
||||||
|
// force 参数用于强制刷新本地数据
|
||||||
|
const getOilPriceJson = (force) => {
|
||||||
|
const filename = new Date().format().split("T")[0].replaceAll("-", "").concat(".json")
|
||||||
|
const filepath = path.join(__dirname, "data", filename)
|
||||||
|
if (fs.existsSync(filepath) && !force) {
|
||||||
|
const data = fs.readFileSync(filepath, "utf-8")
|
||||||
|
return JSON.parse(data)
|
||||||
|
} else {
|
||||||
|
// 如果目录不存在,则创建目录
|
||||||
|
if (!fs.existsSync(path.join(__dirname, "data"))) {
|
||||||
|
fs.mkdirSync(path.join(__dirname, "data"), { recursive: true })
|
||||||
|
}
|
||||||
|
// 如果文件存在,则需要删除文件
|
||||||
|
if (fs.existsSync(filepath)) {
|
||||||
|
fs.rmSync(filepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
getOilPrice((data) => {
|
||||||
|
data.push({
|
||||||
|
name: "更新时间",
|
||||||
|
time: new Date().format()
|
||||||
|
})
|
||||||
|
fs.writeFileSync(filepath, JSON.stringify(data), "utf-8")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOilPrice = async (callback) => {
|
||||||
|
const url = "https://www.xiaoxiongyouhao.com/fprice/"
|
||||||
|
let data = []
|
||||||
|
const response = await axios.get(url)
|
||||||
|
const $ = cheerio.load(response.data)
|
||||||
|
|
||||||
|
// 解析 html 页面
|
||||||
|
$("table").each((i, elem) => {
|
||||||
|
const table = $(elem)
|
||||||
|
const rows = table.find("tr")
|
||||||
|
rows.each((j, row) => {
|
||||||
|
const cells = $(row).find("td")
|
||||||
|
let rowData = {}
|
||||||
|
cells.each((k, cell) => {
|
||||||
|
switch (k) {
|
||||||
|
case 0:
|
||||||
|
rowData["name"] = rename($(cell).text())
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
rowData["92#"] = $(cell).text()
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
rowData["95#"] = $(cell).text()
|
||||||
|
break
|
||||||
|
case 3:
|
||||||
|
rowData["0#"] = $(cell).text()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
rowData["other"] = $(cell).text()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 过滤掉空行
|
||||||
|
if (Object.keys(rowData).length > 0) {
|
||||||
|
data.push(rowData);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
callback(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一地区名称
|
||||||
|
const rename = (name) => {
|
||||||
|
name = name.replace("省", "")
|
||||||
|
name = name.replace("市", "")
|
||||||
|
name = name.replace("内蒙古自治区", "内蒙古")
|
||||||
|
name = name.replace("广西壮族自治区", "广西")
|
||||||
|
name = name.replace("西藏自治区", "西藏")
|
||||||
|
name = name.replace("宁夏回族自治区", "宁夏")
|
||||||
|
name = name.replace("新疆维吾尔自治区", "新疆")
|
||||||
|
name = name.replace("香港特别行政区", "香港")
|
||||||
|
name = name.replace("澳门特别行政区", "澳门")
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// 为 Date 创建日期格式化方法
|
||||||
|
Date.prototype.format = function () {
|
||||||
|
let fillZero = (num) => {
|
||||||
|
return num < 10 ? "0" + num : num
|
||||||
|
}
|
||||||
|
let year = fillZero(this.getFullYear())
|
||||||
|
let month = fillZero(this.getMonth() + 1)
|
||||||
|
let day = fillZero(this.getDate())
|
||||||
|
let hour = fillZero(this.getHours())
|
||||||
|
let minute = fillZero(this.getMinutes())
|
||||||
|
let second = fillZero(this.getSeconds())
|
||||||
|
return `${year}-${month}-${day}T${hour}:${minute}:${second}`
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user