我不擅长写一些有意思的话,技术也如个人博客名字同样很普通,可是在这个春节,新肺炎肆虐武汉,更不停有扩散在全国的案例消息。html
我不是什么专业人士,也没法贡献医疗力量,只能尽我所能看是否能为这件事作一点点的贡献。node
此次的新肺炎专家组说了可防可控,但前提是全部人都有防御意识。常常接触互联网的同窗们可能防御意识都已经提高起来了,但我相信有很多同窗必定据说过或者正在面临着老人防御意识不强,抵制戴口罩,说破嘴皮子,也说服不了平时最爱养生的爸爸妈妈们,不要走亲戚,不要串门,即便有事要出门必定记得戴口罩等等。ios
我在想这些老人听不进去,讲不通道理,归根结底是信息获取不到位,没法感知到形式有多么的严峻。几乎每隔几分钟、十几分钟全国各地都会不停有新的应急措施启动,新的案例产生,全国都处于高度绷紧的状态,这种态势却并无被老人们清晰的感知到。git
若是他们能意识到这些问题的严重性,相信每一个人都知道生命是珍贵的,应该重视注意。github
如今网上谣言漫天飞,可靠的官方信息来源是很重要的。人民日报和丁香医生作了一个实时动态的页面,发布的都是通过可靠验证的实时最新信息-实时播报。web
可是,这是一个web页面,想要给长辈讲解问题的严重性,及事态的发展,作到有理有据,须要不停地查看这个页面,变得很是的不方便。json
个人想法很简单,把这个页面的信息作成接口,这样就能够很方便的做出不少拓展方案。例如,我想经过微信机器人实时通知到群里(因为没有闲置微信老号作机器人未完成),后来又想经过邮件实时发送新动态(这个作了,后面会说),甚至我还想作一个app,将新动态推送的手机上,用语音播放自动朗读出来,成为一个行走的态势宣传喇叭 o(╥﹏╥)o等等,都是须要一个方便的接口才能完成。axios
因此,就有了下面的内容。api
我第一想法是用 Puppeteer
简单粗暴抓取一下,一般状况下是简单快捷。打开页面分析了一下,发现数据其实都直接放在页面的 <script>
里了,就是 JavaScript
对象。服务器
这种状况下,其实直接取对象是更快捷的方式。因此采起了 axios
+ cheerio
+ node vm
的方案。
代码很简单直接贴了:
const url = `https://3g.dxy.cn/newh5/view/pneumonia_peopleapp?from=timeline&isappinstalled=0`;
async function getData(){
let response = await Axios.get(url);
let html = await response.data;
let $ = Cheerio.load(html);
let script = $('body > script');
console.log(script.length);
var global = {
window:{}
};
for(let i = 0; i < script.length; i++){
if(script[i] && script[i].children.length>0){
let scriptContent = script[i].firstChild.data;
vm.createContext(global);
vm.runInContext(scriptContent, global);
}
}
return global.window;
}
async function main(){
let data = await getData();
await fs.writeJSON('data/data.json',data)//保存数据
}
main().catch((error) => {
console.log(error);
process.exit();
});
复制代码
为了防止对数据源形成压力,这里直接将数据保存到了本地 json
存储,经过 pm2
控制2分钟刷新一次。开放接口直接访问本地的 json
数据,这样对数据源彻底没有任何影响。
在咱们经过技术手段去采集一些信息时,尽量避免对数据源产生影响是一种基本的技术道德。
使用 koa
对外提供接口。
//获取全部信息
router.get('/data/all', async (ctx, next) => {
let data = await fs.readJSON('data/data.json');
ctx.response.body = data;
});
//获取指定省份的信息
router.get('/data/getAreaStat/:provice', async (ctx, next) =>{
var provice = ctx.params.provice;
console.log(ctx.params)
let data = await fs.readJSON('data/data.json');
let areaStat = data.getAreaStat;
if(provice){
let body = [];
for(let i = 0; i<areaStat.length; i++){
let area = areaStat[i];
if(area.provinceName == provice || area.provinceShortName == provice){
body.push(area);
break;
}
}
ctx.response.body = body;
}else{
ctx.response.body = areaStat;
}
});
//获取信息时间线
router.get('/data/getTimelineService', async (ctx,next) => {
let data = await fs.readJSON('data/data.json');
let timeline = data.getTimelineService;
ctx.response.body = timeline;
});
//获取总体统计信息
router.get('/data/getStatisticsService', async (ctx,next) => {
let data = await fs.readJSON('data/data.json');
let statistics = data.getStatisticsService;
ctx.response.body = statistics;
});
// add router middleware:
app.use(router.routes());
app.listen(3001);
复制代码
/data/getTimelineService
按时间线获取事件
/data/getStatisticsService
获取总体统计信息
/data/getAreaStat/:provice
获取指定省份信息 例如:/data/getAreaStat/山东
/data/all
获取全部信息
/data/getNewest/:lastid
获取最新事件 lastid 表明上次获取到的最后的id
例如:/data/getNewest/281
将会返回id为281的事件以后发生的事件集合。
我在服务器上跑了一份,方便有须要的同窗使用:
测试:http://49.232.173.220:3001/data/getTimelineService
带参数例子:
http://49.232.173.220:3001/data/getAreaStat/山东
http://49.232.173.220:3001/data/getNewest/281
项目代码在这
因为没有闲置的微信老号,制做微信机器人进行通知的想法没有实现,实现了相对成本最低的邮件通知方案。
若是有须要邮件通知的同窗,能够留言你的邮箱,我帮你添加上,就可以收到邮箱通知了。
效果: