diff --git a/docs/forecast.md b/docs/forecast.md
index cea9fb737..32f74aeaa 100644
--- a/docs/forecast.md
+++ b/docs/forecast.md
@@ -42,6 +42,12 @@ pageClass: routes
+## 上海市生态环境局
+
+### 空气质量
+
+
+
## 停电通知
### 南京市
diff --git a/lib/router.js b/lib/router.js
index 4372e7750..65f4287b7 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -2867,4 +2867,7 @@ router.get('/ems/news', require('./routes/ems/news'));
// 场库
router.get('/changku', require('./routes/changku/index'));
+// 上海市生态环境局
+router.get('/gov/shanghai/sthj', require('./routes/gov/shanghai/sthj'));
+
module.exports = router;
diff --git a/lib/routes/gov/shanghai/sthj.js b/lib/routes/gov/shanghai/sthj.js
new file mode 100644
index 000000000..0b71ceb5b
--- /dev/null
+++ b/lib/routes/gov/shanghai/sthj.js
@@ -0,0 +1,26 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+
+module.exports = async (ctx) => {
+ const link = 'https://link.sthj.sh.gov.cn/aqi/index.jsp';
+ const response = await got({
+ method: 'get',
+ url: link,
+ });
+ const $ = cheerio.load(response.data);
+ const aqi = $('div.val').text();
+ const time = new Date().getFullYear() + '-' + $('span.t-time').eq(0).text().replace(/\s+/g, '').replace('月', '-').replace('日', ' ').replace('时', ':00:00');
+
+ ctx.state.data = {
+ title: '上海市生态环境局 - 空气质量',
+ link: link,
+ item: [
+ {
+ title: aqi + ' - ' + time,
+ link: link,
+ description: $('table.table').html() + $('div.air-yb').html(),
+ pubDate: new Date(time + ' GMT+8').toUTCString(),
+ },
+ ],
+ };
+};