好想工作室每天都有很多學員或社群夥伴到這邊來,也因為他在大橋火車站旁邊,所以很多人都是直接搭電車過來。常常聊得太開心,一不小心就錯過了末班車,所以每天都會時常查詢台鐵時刻表,今天就來做一隻爬蟲,讓我們能夠知道最近幾班的台鐵電車時刻。
ps. 不過台鐵時刻這個議題其實已經有很多人實做過,甚至有 open data 或 open api,不過我們會以最原始的網頁爬蟲來實作最基礎的做法。
台鐵的網站是 http://twtraffic.tra.gov.tw/twrail/TW_Quicksearch.aspx ,打開後真的是滿滿的大平台。先選擇啟程站,先點選台南區域再點選大橋車站,再選擇到達車站,選擇沙崙支線的沙崙站,然後再點選時間部分,其他項目我們就先留預設就好。(老實說,這個步驟真的很麻煩)
查詢完後,我們就會得到列車的資訊,這部分看起來就是 select dom 爾已,感覺應該沒什麼問題,我們就開始研究吧。
我們會將這隻爬蟲分解成三個動作:
我們先來觀察一下查詢的 request 會送出什麼。參數看起來基本上都還蠻直覺的,比較值得注意的,大概就是 FromStation、ToStation、TrainClass、searchdate、FromTimeSelect、ToTimeSelect、Timetype 這幾個。若不知道他是什麼意思,我們也能直接從網頁物件來做檢查,這樣就能 match 參數的意義。根據我們的需求來看,我們可以很容易的將參數組合出來,比較需要注意的大概就只有日期和時間,因為我們要送的是當下查詢的時間。
接下來我們用 postman 來測試看看,咦?乍看之下以為沒有得到列車班表,但凡事不能看表面,其實是有資料吐回來的。再仔細觀察一下,發現他把查詢結果寫在 html 裡面的 javascript,也就是說,我們必須模擬這個畫面的 js 才能取得這個變數。
既然他給我們的 data 是 json format,那也就不用解析了,只需要了解各個 key 所代表的現實意義就可以了。而知道他其實是用前端 render 的方式來組合 result table,那我們也可以根據一些 keyword 來查詢看看 render 的程式碼在哪邊,進而更切確的知道欄位對應的狀況。
我們先準備好我們要做查詢的資料,包含站名、車種、方向等等,同時我們使用 moment 來抓取現在的時間,時間區間為一個小時。(若你在半夜看到這篇爬蟲並且跟著做,那你將拿不到查詢資料...因為這個時間沒有火車啊啊啊!)
var data = {
FromCity: 9,
FromStation: 1239,
FromStationName: 0,
ToCity: 18,
ToStation: 5102,
ToStationName: 0,
TrainClass: 2,
searchdate: moment().format('YYYY - MM - DD'),
FromTimeSelect: moment().format('HHmm'),
ToTimeSelect: moment().add(1, 'hours').format('HHmm'),
Timetype: 1
}
接下來我們 post request,拿到查詢的回傳頁面。
var options = {
url: 'http://twtraffic.tra.gov.tw/twrail/TW_SearchResult.aspx',
method: 'POST',
form: data,
}
request(options, (err, res, body)=>{
console.log(body)
})
就我們剛剛研究的結果,發現 data 是在回傳頁面的內容裡面,因此需要模擬 js 的執行才會有 data,所以我們會使用 jsdom 這個 library 來模擬,並且要加上 {runScripts: "dangerously"}
這個參數,這樣他才會執行裡面的 js。
request(options, (err, res, body)=>{
var dom = new JSDOM(body, { runScripts: "dangerously" });
console.log(dom.window.JSONData);
})
const request = require('request');
const cheerio = require('cheerio');
const moment = require('moment');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
var data = {
FromCity: 9,
FromStation: 1239,
FromStationName: 0,
ToCity: 18,
ToStation: 5102,
ToStationName: 0,
TrainClass: 2,
searchdate: moment().format('YYYY-MM-DD'),
FromTimeSelect: moment().format('HHmm'),
ToTimeSelect: moment().add(1, 'hours').format('HHmm'),
Timetype: 1
}
var options = {
url: 'http://twtraffic.tra.gov.tw/twrail/TW_SearchResult.aspx',
method: 'POST',
form: data,
}
request(options, (err, res, body)=>{
var dom = new JSDOM(body, { runScripts: "dangerously" });
console.log(dom.window.JSONData);
})
台鐵的網站其實有很多優化的方向,但因為我們不是台鐵的 IT 人員,所以我們並沒有辦法直接修改他們的網頁,可是我們可以透過很多方式來讓整個服務提升,像是我們製作出 api 出來供其他服務應用,或是我們製作 browser extension 來改善使用體驗也行。
若你在生活上覺得有什麼不方便的,作為一個自幹型工程師,那你該做的不是去習慣它,而是應該動手解決它。