Express + Node 爬取网站数据

前言

因为自己写的demo需要历史天气的统计数据,但是国内很难找到免费的api接口,很多都需要付费和审核。而国外的网站虽然免费但需要提前知道观测站,城市id等信息。所以就有了这么一篇文章的诞生。


准备工作

作用
superagent发送请求
superagent-charset设置请求的编码
cheerio让解析html文档像jquery一样简单


实现思路

  1. 找到目标网站
    东方天气网(本文以此为例)
    天气网
  2. 分析网站结构
    通过分析,可以绘制如下流程图
Created with Raphaël 2.1.2访问基址(异步),拿到城市的根地址按城市和年月拼成新的地址访问新地址(异步)拿到城市在指定月的数据处理数据
  1. 代码编写思路
    从流程图可以看出,需要先异步请求基址拿到城市的根地址。
    拿到根地址后拼接时间和城市,接着异步访问才能拿到我们要的数据。
    这种嵌套异步可以使用Promise来实现(eventProxy基本已不用)。
  2. 代码实例
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在这里用着好像挺乱,没有完全解决嵌套问题。后续会增进学习,对这一部分更加完善。也希望大家能够给出宝贵意见~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/549753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ajax省级联动 回发或回调参数无效

今天在做项目有个修改内容的,有个地方用到省级联动,现在一般都用ajax,很少用auto什么的那个属性了 想想ajax做省级联动也很简单,就开始做了,没想到在修改的时候提示回发或回调参数无效,然后去网上找了好久 …

虚拟局域网(VLAN)的管理

什么是虚拟局域网?虚拟局域网是一种用逻辑的定义方法,把两个或更多的连在交换网络上的终端规划在一起。 这种逻辑定义方法可以延伸到多个交换机。被规划在一起的终端,可以通过几种网络设置来规划。好像任何一种网络技术一样,了解…

学习antd-design-pro

学习使用react-antd-pro框架(一篇学习中的问题思考记录) 框架介绍 react-antd-pro 大体上等于 react antd pro。官网对于相关技术栈的描述如下: 我们的技术栈基于 ES2015、React、UmiJS、dva、g2 和 antd UmiJS: 可插拔的企业级 react 应用框架 dva: React and r…

SqlZoo.net习题答案:The Join 【Movie】

习题地址:http://sqlzoo.net/3.htm 表结构:  movie(id, title, yr, director)      actor(id, name)      casting(movieid, actorid, ord) 1b.Give year of Citizen Kane. select yr from movie where title Citizen Kane 1c.List all …

汉语编程++

没想到汉语编程又有人开始网上对骂了。一方指另一方骗人,一方吹自已伟大。今天群里头有人又把它翻出来了,刚好无聊,也就发明了一个汉语编程语言,集成到visual studio 2005的IDE中,名字就叫汉语编程,欢迎同样…

hightopo学习系列:hightopo介绍(一)

起因 新的软件主管来公司以后,有整整2周的时间没有搭理前端开发。就在这周一快下班的时候,突然和我说话了。问了我一些以前用的图形库,并让我开始了解hightopo。甩给了我一个全拼,就拂袖而去,留下一脸懵逼的我。 没办…

mongo指令

use admin db.runCommand({logRotate:1}) show dbs; db.currentOp(); db.serverStatus().connections db.killOp(26593769); use adbname db.tblname.ensureIndex({"sendtime":1}) db.tblname.getIndex() 转载于:https://www.cnblogs.com/ccgblog/p/8427972.html

女人,向《奋斗》中的夏琳米莱们学习什么

被称为赵宝刚导演的转型之作电视连续剧《奋斗》,看得我有些意犹未尽。据说佟大为与马伊俐都是本色表演,但是我却更喜欢剧中米莱的角色。 刚开始看《奋斗》的时候,以为夏琳是许晴演的呢,从长相性格表情连发型都像,像缩小…

vue项目积累

工作记录 1.修饰符及其使用 最近项目上看到这样的代码: child组件隐藏模态框触发以下事件 closeHandler () {this.$emit(update:open, false)},查阅资料发现,这是以修饰符的方式实现了“双向绑定”,避免了真正的双向绑定会带来维护上的问题。…

OpenCV4Android Tutorial0解析

Tutorial0是一个纯粹的android程序,没有opencv部分,是其他程序的框架基础。 有Sample0Base.java, Sample0View.java, SampleViewBase.java.三个文件。 Sample0Base 是程序入口,主要设置surfaceview和菜单。 requestWindowFeature(…

NPM管理

npm 发布流程 npm loginnpm versionnpm publish npm version npm version <update_type> // types: patch, major, or minor主版本号&#xff08;Major&#xff09;.次版本号&#xff08;Minor&#xff09;.修订号&#xff08;Patch&#xff09; 版本号递增规则: 主版…

网络工程师应掌握的50个路由器知识要点

1、什么时候使用多路由协议?  当两种不同的路由协议要交换路由信息时&#xff0c;就要用到多路由协议。当然&#xff0c;路由再分配也可以交换路由信息。下列情况不必使用多路由协议:  从老版本的内部网关协议( Interior Gateway Protocol&#xff0c;I G P)升级到新版本的…

Unity工程无代码化

目的 Unity默认是将代码放入工程&#xff0c;这样容易带来一些问题。1. 代码和资源混合&#xff0c;职能之间容易互相误改。2. 当代码量膨胀到一定程度后&#xff0c;代码的编译时间长到无法忍受。新版的unity支持通过asmdef来将代码分成多个dll工程&#xff0c;有所缓解。所以…

曾国藩传 读后感

转载于:https://www.cnblogs.com/eat-too-much/p/11335113.html

hdu 1102 pku 2421 解题报告

这题很简单&#xff0c;我差不多15分钟就写好代码了&#xff0c;运行结果也是正确的。可提交就是RE&#xff0c;百思不得其解&#xff0c;调了两个小时的时候&#xff0c;我才忽然发现我存边的时候数组开小了&#xff0c;我当时也想到肯定是数组问题&#xff0c;但是我却忽律了…

深入C#学习系列一:序列化(Serialize)、反序列化(Deserialize)

深入C#学习系列一&#xff1a;序列化(Serialize)、反序列化(Deserialize) 序列化又称串行化&#xff0c;是.NET运行时环境用来支持用户定义类型的流化的机制。其目的是以某种存储形成使自定义对象持久化&#xff0c;或者将这种对象从一个地方传输到另一个地方。.NET框架提供了两…

分别用 数组和链表处理约瑟夫环问题

#include <stdio.h> #include <stdlib.h> int main() { int k0,n0,s0,m0; //k为1,2,3报数时的计数变量,m为退出人数 int num [100]; int *pnum; int i; printf("Enter the number of person and the key:"); scanf("%d%d",&n,…

QT_环境搭建

QT_环境搭建 Qt软件安装&#xff1a;https://www.jianshu.com/p/65bc892829a0 Qt软件下载&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/qt/5.13/5.13.0/ 转载于:https://www.cnblogs.com/panda-w/p/11338742.html

十一月·飘·立冬

十一月的南粤叶依然青翠在枝头与秋风和舞落叶遍地的诗意画面在博客生活逝如流年 渐走渐淡回忆飘然而来又飘然而去秋的最后一天放下回忆 飘去天涯飘不要说也不要问目光交错的一瞬注定了今生缘分此情可以见真心春风急 秋风也狠乱乱纷纷 是红尘浮浮沉沉 似幻似真金枝玉叶的结…

OCP-052考试题库汇总(27)-CUUG内部解答版

Which two of these must be available READ/WRITE to keep a database open? A)all copies of the control file. B)the password file. C)all members of the current redo log group. D)spfile. E)TEMP tablespace F)SYSAUX tablespace Answer: AC 转载于:https://www.cnbl…