爲抗擊新肺炎貢獻一份技術力量

寫在開頭

我不擅長寫一些有意思的話,技術也如個人博客名字同樣很普通,可是在這個春節,新肺炎肆虐武漢,更不停有擴散在全國的案例消息。html

我不是什麼專業人士,也沒法貢獻醫療力量,只能盡我所能看是否能爲這件事作一點點的貢獻。node

此次的新肺炎專家組說了可防可控,但前提是全部人都有防禦意識。常常接觸互聯網的同窗們可能防禦意識都已經提高起來了,但我相信有很多同窗必定據說過或者正在面臨着老人防禦意識不強,抵制戴口罩,說破嘴皮子,也說服不了平時最愛養生的爸爸媽媽們,不要走親戚,不要串門,即便有事要出門必定記得戴口罩等等。ios

我在想這些老人聽不進去,講不通道理,歸根結底是信息獲取不到位,沒法感知到形式有多麼的嚴峻。幾乎每隔幾分鐘、十幾分鍾全國各地都會不停有新的應急措施啓動,新的案例產生,全國都處於高度繃緊的狀態,這種態勢卻並無被老人們清晰的感知到。git

若是他們能意識到這些問題的嚴重性,相信每一個人都知道生命是珍貴的,應該重視注意。github

獲取實時動態

如今網上謠言漫天飛,可靠的官方信息來源是很重要的。人民日報和丁香醫生作了一個實時動態的頁面,發佈的都是通過可靠驗證的實時最新信息-實時播報web

可是,這是一個web頁面,想要給長輩講解問題的嚴重性,及事態的發展,作到有理有據,須要不停地查看這個頁面,變得很是的不方便。json

個人想法很簡單,把這個頁面的信息作成接口,這樣就能夠很方便的做出不少拓展方案。例如,我想經過微信機器人實時通知到羣裏(因爲沒有閒置微信老號作機器人未完成),後來又想經過郵件實時發送新動態(這個作了,後面會說),甚至我還想作一個app,將新動態推送的手機上,用語音播放自動朗讀出來,成爲一個行走的態勢宣傳喇叭 o(╥﹏╥)o等等,都是須要一個方便的接口才能完成。axios

因此,就有了下面的內容。api

網頁轉接口

我第一想法是用 Puppeteer 簡單粗暴抓取一下,一般狀況下是簡單快捷。打開頁面分析了一下,發現數據其實都直接放在頁面的 <script> 裏了,就是 JavaScript 對象。服務器

這種狀況下,其實直接取對象是更快捷的方式。因此採起了 axios + cheerio + node vm 的方案。

網頁數據提取

代碼很簡單直接貼了:

  • app.js
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 對外提供接口。

  • api.js
//獲取全部信息
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

測試: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

項目代碼在這

郵件通知服務

因爲沒有閒置的微信老號,製做微信機器人進行通知的想法沒有實現,實現了相對成本最低的郵件通知方案。

若是有須要郵件通知的同窗,能夠留言你的郵箱,我幫你添加上,就可以收到郵箱通知了。

效果:

相關文章
相關標籤/搜索