kumoRail項目概述01:GetLate模塊


kumoRail預覽圖及公衆號二維碼

   kumoRail是我接觸web編程之後的第一個idea,其目的在於給鐵道迷們提供運轉和拍車上的一些便利,從一年前斷斷續續的做到現在,總算成型了5個功能:正晚點歷史、交路、配屬、時刻和電報碼 查詢。至此稍作一個停歇,也是做一個總結,好好理一下做這個項目的思路體會,爲自己以後再來回顧和重構它的時候,留存一點記憶,也讓更多希望了解這個項目的朋友能夠更容易的看懂它。

   kumoRail最早只有正晚點歷史一個部分。12306網站只提供1小時前和3小時后的正晚點信息查詢,所以我們的目標就是要設計一個長時間一直運行的爬蟲來不斷在車到站時候收集該車的整晚點信息,然後寫入數據庫裏面。我將這個進程,或者是叫模塊,命名為GetLate,不過,不是睡覺起晚了的意思哈。不過呢現在看起來,它結構過於原始,也不符合Object-oriented的思想,不過總有留存一些方法和思想,得以在再次重構的時候進行參考罷。

   getlate.py裏面,loadModule函數只是爲了導入模塊,真正起作用的是getData函數。他會不斷的運行以收集數據。

   在getData中,首先導入了schqdb、staqdb、ressdb三個讀寫數據庫的對象,log是寫log文件的對象,因爲supervisor的寫日志會有延遲,故而爲之。queryCache是一個list,會把需要進行查詢的到站信息緩存在這裏,待車到站之後再進行查詢的。

   這裏有一個“窗口”的思想。“窗口”有兩個,指的就是在cache中加入待查到站信息和進行查詢的時間範圍。提前加入信息,在過了預定的到站時間3分鐘后開始進行查詢,若查詢成功,沒有晚點則drop掉該條,長時間晚點則更新預定的到站時間,在那個時間再進行查詢,錯誤的話亦會保留,再進行再次查尋。

   我本來的想法是在車到站之後3-5分鐘内進行多次查詢,但是這實際上非常不合適。第一個是已經查到正確值的車次會進行再次的查詢,會在數據庫裏面形成重複的數據,浪費流量和數據庫資源。第二個問題是某些車次會產生長時間的晚點,但是在這幾分鐘内,我們收集過後,它的晚點再發變數,我們就收集不到它正確的信息。後來我也想過,事實上最理想的方法是提前在數據庫裏面建立一個該日該車次于該站的到站信息,後面再進行填充和更新。不過想來,在不改變數據庫模型的情況下,建立一個cache列表也是不失爲一種比較好的方式。但他也有個主要缺點,就是在程式崩潰,再次啓動時候,以前的cache就丟失了。這還有待改進。

   在while(True)裏面是主循環。在這其中的前8行作用為獲取當前的時間,然後轉化為Int。需要留意的地方只是在於服務器所在的時區和GMT+8(北京時間)的時差。目前的模式是使用config中的zoneDelata來設置,以後會改進為直接獲取系統時區設置再進行適配。

   接下來的步驟是從數據庫裏面獲取在待查時間窗口裏面的到站信息。還好在撰寫對數據庫的接口的時候有一個時間按時間範圍查詢的方法,使查詢方便了許多。需要留意的是窗口的開始時間與結束時間如果發生跨日的話,就將之分割成兩半進行查詢,這在後面也是常用的手段。

   下面是添加查詢到的到站信息到cache中。我們首先需要遍歷從數據庫中獲取的到站信息,判斷cache中是否有存在該信息。這裏面只是用了車次來驗證是否重複,實際上是默認該車不會在3分鐘内不會有兩次到站,隔日時會顯露,需糾正。若確實沒有,則添加,同時設置到達時間為1441,以備後續修改,查詢狀態status為0。在後面會將cache的信息寫入log中。

   下面開始進行查詢。首先設立了一個i變量作爲cache的索引,proxyUsed為proxy的索引。之所以不使用for循環,是因爲需要在循環時不斷改變索引的值。

   判斷schedule的到站時間是否在待查窗口内。不在,會跳過本條,并將i+1。如果是到站信息中的實際到站時間為1441,則證明沒有進行過查詢,應使用原本時刻表中的到站時間,否則證明有大晚點,使用前一次查詢的到站時間。

  對與需要進行查詢的到站信息,我們從數據庫裏面獲取它的中文信息並轉換成兩個由utf8和gbk片段組成的字符串,它們會在查詢晚點信息的url中使用到。轉換之後就把相關信息扔到hook的getLate對象中去初始化。然後調用該對象的proxyGet,即使用代理的訪問和localGet,不使用代理的訪問,兩個方法去獲取整晚點信息。因爲在整個程序中,涉及大量對該頁面的訪問,弄得不好會被ban IP,故而設計了使用HTTP代理的訪問這樣一個可以使用別人的IP的方法。HTTP代理的資源,在網絡上可以找到。我將到站信息分成了若干個組,每個組使用3個代理,一個不成換下一個,查詢成功了就break,3個都不成功則不使用代理進行訪問。在proxyGet和localGet使用了try機制,在代理不可用導致錯誤的時候將該代理的錯誤次數更新。比較難以處理的地方在於,連接不上可能導致的情況種類繁多,包括但不限於403、Timeout 等,他們實際涉及到的錯誤不僅在於urllib2,亦在於socket,甚至httplib等庫,在except中難以完全囊括進去,所以在出現未囊括的錯誤時候仍然沒有被記錄,這仍然是一個比較棘手的問題。

   getLate對象裏面最後一個方法就是將更新後,主要是將更新了錯誤次數的的proxy信息寫入文件裏。

   調用了兩個get之後,會返回從頁面上獲取到的字符串,類似於“预计K594/K595次列车,兰州站到达时间为19:28”,也有可能是“目前暂无K594次列车西安站到达时间,请稍候重新查询”。我爲了後面匹配方便,在字符串的最後加了一個“99:99”。然後就是用正則表達式去匹配時間,兩個數字加冒號加兩個數字的即是。

   然後會向log寫入一條日志,包含車次、到站、應到、實到。

   然後是驗證,是否已經得到了的到站數據,因爲我們加了一個99:99進去,所以查詢沒有獲得數據的話,正則表達式匹配到的字符串為99:99。此時將status+1,表示發生了一次未查詢到,放在下一輪窗口再進行查詢。未查詢到,有可能是發生了不可以預料的超長晚點或者是其他錯誤,總之是錯誤連接。如果連續3次未查詢到,則放棄這個到站的查詢。實際上,如果是發生不可預料的長晚點(猜測為3小時以上),應該用遍歷來找到這個車的位置先。這還有待改進。

   如果驗證發現拿到了正確的數據,我們則依照晚點情況來進行下一步的操作。actInt為獲取到的到站時間數,即時間轉化成從零點開始的分鐘數,小時*60+分鐘,nowInt為現在的時間。明顯,儅nowInt-actInt爲正的時候,發生了早點,數據已經不會有後續的變化,可以寫入數據庫了。後面一個小於60的判斷,是爲了限制這個越界或者越天的問題,如果說23:55的車,在23:58進行查詢,發現晚點到了00:08,nowInt-actInt值就爲1430,但是并沒有發生早點,於是我限制了這個差值不會大於60。不過這個其實是默認了早點時間不會60min以上,這個範圍控制如何,待後面也會按照實際情況再加調節(事實上,調節很重要!)。elif後面的判斷的是發生了零點交越的早點,例如説00:03早點到23:54,會有一個相反的差值,不過其邏輯和普通早點是大同小異的。判斷符合早點的條件之後,都會寫入數據庫,然後把這個到站信息從cache裏面剔除掉。

   否則,就是最后的else,產生了晚點的情況。這時候會先寫一條log,然後將i+1。在前面兩個刪除了項的情況下,cache這個list縮短了,因此并不需要+1。

   後面到了130行的延時48秒。其實這也是一個需要調整的值,應該確保各種情況在這個3分鐘内能得到徹底的演繹。其中包括:可能出現3次99:99的情況、因timeout的設置導致的時延,等等(需要補充)。

  最後面就沒有什麽了,在主函數中進行運行即是。

评论

此博客中的热门博文

又一个建站碎碎念

寫了一個回收利用某站視頻緩存的程序

【これ】我收集的Hackathon時間表