前言
因为自己写的demo需要历史天气的统计数据,但是国内很难找到免费的api接口,很多都需要付费和审核。而国外的网站虽然免费但需要提前知道观测站,城市id等信息。所以就有了这么一篇文章的诞生。
准备工作
| 作用 |
---|---|
superagent | 发送请求 |
superagent-charset | 设置请求的编码 |
cheerio | 让解析html文档像jquery一样简单 |
实现思路
- 找到目标网站
东方天气网(本文以此为例)
天气网 - 分析网站结构
通过分析,可以绘制如下流程图
- 代码编写思路
从流程图可以看出,需要先异步请求基址拿到城市的根地址。
拿到根地址后拼接时间和城市,接着异步访问才能拿到我们要的数据。
这种嵌套异步可以使用Promise来实现(eventProxy基本已不用)。 - 代码实例
var express = require('express');
var router = express.Router();
var superagent = require("superagent");
var charset = require("superagent-charset"); //解决编码问题
var cheerio = require("cheerio");/* GET users listing. */
var mysql = require("mysql");
var responseJson = require("../util/responseJson"); //导入mysql模块
var dbConfig = require("../db/DBconfig");//使用DBConfig.js的配置信息创建一个MySQL连接池
var pool = mysql.createPool(dbConfig.mysql);charset(superagent); //需要遍历的信息
var BaseUrl = "http://tianqi.eastday.com";
var Cities = ["成都"]; //需要获取的城市
var indexArr = ['cd'];
var Years = ["2018"]; //年份,因为2018年以前dom结构不一样,所以这里只取2018
var Months = ["01", "02", "03", "04", "05", "06", "07", "08"]; //月份function getCityUrl (city) {//返回Promisereturn new Promise((resolve, reject) => {superagent.get(BaseUrl + "/history.html").charset("utf-8").end((err, sres) => {if (err) {next(err);return;}let $ = cheerio.load(sres.text);//后续继续遍历的基址let href = $(".letter-box").find("a[title='" + city + "']").attr("href");resolve(href);});})
}//获取指定城市在指定时间的数据
function getData (href, city) {let year = Years[0];return Months.map(month => {let url = BaseUrl + href.replace(".html", "_" + year + month + '.html' );//获取天气数据return new Promise((resolve, reject1) =>{superagent.get(url).charset("utf-8").end((err1, sres1) => {if (err1) {reject1(err1);return;}let $ = cheerio.load(sres1.text);let arr = [];$("#weaDetailContainer").find(".weatherInfo-item").each((index, item) => {let $item = $(item);arr.push({time: year + "-" + month + "-" + $item.find(".dateBox").text().substr(0, 2),wea: $item.find(".weather-name").text(),tempL: $item.find(".low-temp").text(),tempH: $item.find(".high-temp").text(),wind: $item.find(".wind").text(),});});resolve(arr);});});});
}function dispatch(groups) {var results = []return (function () {var fun = arguments.callee, group = groups.shift()if (!group) {return Promise.resolve(results)}var promises = []group.forEach(function (task) {promises.push(Promise.resolve(task))})return Promise.all(promises).then(function (rets) {results.push(rets)return fun()})}())
} function query (sql) {return new Promise((resolve, reject) => {pool.getConnection((err, conn) => {if(err){reject(err);} else {conn.query(sql, (err1, rows, fields) => {conn.release();if(err1){reject(err1);} else {resolve({rows: rows,fields: fields});}});}});});
}function makeSql (item, index) {let sql = "INSERT INTO weather_" + indexArr[index] + " (time, wea, tempH, tempL, wind) values ";let arr = [].concat.apply([], item);arr.map(group => {sql += "('"+ group.time + "', '"+ group.wea + "', '"+ group.tempH + "', '"+ group.tempL + "', '"+ group.wind+ "'),";});return sql.substring(0, sql.length-1);
} router.get('/', function(req, res, next) {let promiseArr = [];promiseArr = Cities.map(city => {//遍历城市return getCityUrl(city);});Promise.all(promiseArr).then(hrefArr => {return hrefArr.map(href => {return getData(href);});}).then(arr => {return dispatch(arr);}).then(data => {let arr = data.map((item, index) => {return query(makeSql(item, index))});Promise.all(arr).then(() => {res.json({status: true,msg: 'success'})}).catch(e => {res.json({status: false,msg: e.message})})}).catch(e => {res.send(e.message);});
});module.exports = router;
不足
虽然能够实现需求,但是感觉我的Promise在这里用着好像挺乱,没有完全解决嵌套问题。后续会增进学习,对这一部分更加完善。也希望大家能够给出宝贵意见~~