1. 程式人生 > >Python:爬蟲助你回家,12306余票監測!

Python:爬蟲助你回家,12306余票監測!

登錄 val lac repl str 調用 pst 數據 格式

技術分享圖片

寫在前面

一年一度的春運即將來臨,各位看官回家的票有沒有買好呢?反正小編已經按捺不住激動的心情,開始蠢蠢欲動了。但是作為技術控,就應該有技術控的搶票姿態,鑒於12306逆天的驗證碼,小編放棄了控制12306自動搶票的騷操作,開始走向自動余票提醒:有余票=>微信推送余票信息的道路。

正文

技術分享圖片

以徐州到滕州為例,我們想爬取1月26號的余票信息,登錄12306官網查詢余票頁面(https://kyfw.12306.cn/otn/leftTicket/init),在下圖所示的紅框內輸入出發地、目的地以及出發日期。

技術分享圖片

由於余票信息是異步加載的,我們需要通過谷歌瀏覽器開發者工具找到中間請求的URL,可以發現我們需要的信息就在

https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=2019-01-14&leftTicketDTO.from_station=XCH&leftTicketDTO.to_station=TXK&purpose_codes=ADULT 下的result裏面。

技術分享圖片

下面我們運用selenium庫驅動PhantomJS來獲取網頁源碼信息,其中selenium是一個自動化測試工具,大家可以通過pip install下載安裝。利用它可以驅動瀏覽器執行特定動作,同時還可以獲取瀏覽器當前呈現的源碼,做到“可見可爬”,回避了各種反爬措施。最近小編在學習這個庫,所以暫且用它牛刀小試一下。由於selenium不自帶瀏覽器,所以我們這裏選擇小巧的無界面瀏覽器PhantomJS

首先介紹一下,所需要加載的模塊。

Selenium庫:模擬瀏覽器運行的庫。

Pandas庫:Python科學計算庫。

Json庫:解析JSON後將其轉為Python字典或者列表。

Wxpy庫:可用來實現各種個人微信號的自動化操作,我們這裏用來實現微信自動發送余票信息。

time模塊: Python標準庫中的模塊,用來實現時間間隔控制,循環獲得余票信息。

加載程序如下:

fromselenium importwebdriver

importpandas aspd

importjson

importnumpy asnp

fromwxpy import*

importtime

下面開始進入正題,第一步爬取余票信息,還是以徐州到滕州為例。首先獲取網頁源碼,程序如下:

driver = webdriver.PhantomJS( ‘D:webdriverphantomjs-2.1.1-windowsbinphantomjs.exe‘)

#phantomjs所在位置

url= ‘https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=‘+ ‘2019-01-14‘+ ‘&leftTicketDTO.from_station=‘+ ‘XCH‘+ ‘&leftTicketDTO.to_station=‘+ ‘TXK‘+ ‘&purpose_codes=ADULT‘

driver.get(url)html=driver.page_sourcedriver.quit() #關閉phantomjs

最終獲取的文本信息如下:

技術分享圖片

可以發現,我們需要的信息以json數據的形式存在,把網頁的標簽刪除,就可以直接將json數據轉換成字典的形式。程序如下:

html = html.replace( ‘‘‘<html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">‘‘‘, ‘‘) .replace( ‘</pre></body></html>‘, ‘‘) #刪除網頁標簽信息

html=json.loads(html) #剩下的網頁源碼是json數據,json.loads將json格式數據轉換為字典

這樣就把網頁的信息,存儲到了名為html的字典中,而余票信息(即上面原網頁的表格內容)就被已列表的形式,存在了result中。

技術分享圖片

技術分享圖片

技術分享圖片

自然,我們接下來需要做的就是將余票信息從result中提取出來。通過觀察上圖能發現,車次信息清晰地用“|”分隔開,這裏就可以通過split函數將字符串分割,再根據信息位置,提取我們需要的信息。

程序如下:

data={

‘車次‘: ‘‘,

‘始發站‘: ‘‘,

‘終點站‘: ‘‘,

‘出發時間‘: ‘‘,

‘到達時間‘: ‘‘,

‘全程時間‘: ‘‘,

‘商務座‘: ‘‘,

‘一等座‘: ‘‘,

‘二等座‘: ‘‘,

‘高級軟臥‘: ‘‘,

‘軟臥‘: ‘‘,

‘動臥‘: ‘‘,

‘硬臥‘: ‘‘,

‘軟座‘: ‘‘,

‘硬座‘: ‘‘,

‘無座‘: ‘‘} #生成一個空的字典,將我們需要的數據信息定義為鍵名

result=pd.DataFrame(data,index=[ 0]) #將字典轉成一個dataframe,方便數據處理
fori inhtml[ ‘data‘][ ‘result‘]: #我們需要的數據在html字典鍵名為result中
?item = i.split( ‘|‘) #用"|"進行分割data[ ‘車次‘] = item[ 3] #車次在3號位置data[ ‘始發站‘] = item[ 6] #始發站信息在6號位置data[ ‘終點站‘] = item[ 7] #終點站信息在7號位置data[ ‘出發時間‘] = item[ 8] #出發時間信息在8號位置data[ ‘到達時間‘] = item[ 9] #抵達時間在9號位置data[ ‘全程時間‘] = item[ 10] #經歷時間在10號位置data[ ‘商務座‘] = item[ 32] oritem[ 25] # 特別註意:商務座在32或25位置data[ ‘一等座‘] = item[ 31] #一等座信息在31號位置data[ ‘二等座‘] = item[ 30] #二等座信息在30號位置data[ ‘高級軟臥‘] = item[ 21] #高級軟臥信息在31號位置data[ ‘軟臥‘] = item[ 23] #軟臥信息在23號位置data[ ‘動臥‘] = item[ 27] #動臥信息在27號位置data[ ‘硬臥‘] = item[ 28] #硬臥信息在28號位置data[ ‘軟座‘] = item[ 24] #軟座信息在24號位置data[ ‘硬座‘] = item[ 29] #硬座信息在29號位置data[ ‘無座‘] = item[ 26] #無座信息在26號位置df1=pd.DataFrame(data,index=[ 0]) #將賦值後的字典轉換成datafram,命名為df1frames=[result,df1] #result,df1構成一個列表,再使用concatresult = pd.concat(frames,axis= 0, ignore_index= True) #將兩個列表按行合並,然後沖著索引
columns=[ ‘車次‘, ‘始發站‘, ‘終點站‘, ‘出發時間‘, ‘到達時間‘, ‘商務座‘, ‘一等座‘, ‘二等座‘, ‘高級軟臥‘, ‘軟臥‘, ‘動臥‘, ‘硬臥‘, ‘軟座‘, ‘硬座‘, ‘無座‘]result=result.reindex(columns=columns) #將result的列名按columns排列,我們想先看到車次和時間嘛.

通過數據清洗,原網頁表格信息就規整的放到了名為result的dataframe中。如下圖所示:

技術分享圖片

技術分享圖片

這樣就完成了1月26號滕州到徐州的余票信息的爬取,但是大家需要查詢的出發地、目的地各不相同,所以為了方便使用和程序移植,我們可以把上述過程,封裝到一個函數中。

我們重新看上面的URL。如下圖表示的那樣,URL中trian_data=後接出發時間,from_station=後接出發城市城市代碼(XCH是徐州的城市代碼),to_station=後接目的城市代碼(TXK是滕州的城市代碼)。我們只要把這三個信息改成函數參數,就可以方便地查詢不同出發地、目的地以及出發時間的余票信息了。

技術分享圖片

關於城市和代碼的對照表,將其存儲在了一個名為地名代碼對照表.txt中,並將其上傳到雲中,可以將其讀取到python,轉換成字典形式,程序如下:

f = open( ‘地名代碼對照表.txt‘, ‘r‘) #打開txt

dict1 = eval(f.read()) #將txt載入成字典形式

f.close() #關閉txt

最終字典dict1中的文件形式如下,鍵名為城市名,鍵值為城市代碼。

技術分享圖片

這樣,我們就可以直接通過輸入城市的名字,來提取對應的代碼信息,比如要提取滕州的城市代碼,直接輸入:

dict1[‘滕州‘]

就能提取相應的城市代碼:

以上就解決了封裝函數的所有障礙,直接將出發城市、目的城市、出發日期設成參數即可。我們將函數名命名為get_news(start,end,day)。Start,end,day對應上述參數,返回值為一各包含所有列車余票信息的dataframe 。

程序如下:

defget_news(start,end,day):driver = webdriver.PhantomJS( ‘D:webdriverphantomjs-2.1.1-windowsbinphantomjs.exe‘) url= ‘https://kyfw.12306.cn/otn/leftTicket/queryZ?leftTicketDTO.train_date=‘+day.title() + ‘&leftTicketDTO.from_station=‘+dict1[start.title()] + ‘&leftTicketDTO.to_station=‘+dict1[end.title()] + ‘&purpose_codes=ADULT‘driver.get(url) html=driver.page_source html = html.replace( ‘‘‘<html><head></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">‘‘‘, ‘‘).replace( ‘</pre></body></html>‘, ‘‘) html=json.loads(html) data={

‘車次‘: ‘‘,

‘始發站‘: ‘‘,

‘終點站‘: ‘‘,

‘出發時間‘: ‘‘,

‘到達時間‘: ‘‘,

‘全程時間‘: ‘‘,

‘商務座‘: ‘‘,

‘一等座‘: ‘‘,

‘二等座‘: ‘‘,

‘高級軟臥‘: ‘‘,

‘軟臥‘: ‘‘,

‘動臥‘: ‘‘,

‘硬臥‘: ‘‘,

‘軟座‘: ‘‘,

‘硬座‘: ‘‘,

‘無座‘: ‘‘} ?result=pd.DataFrame(data,index=[ 0])

??fori inhtml[ ‘data‘][ ‘result‘]: item = i.split( ‘|‘) #用"|"進行分割data[ ‘車次‘] = item[ 3] #車次在3號位置data[ ‘始發站‘] = item[ 6] #始發站信息在6號位置data[ ‘終點站‘] = item[ 7] #終點站信息在7號位置data[ ‘出發時間‘] = item[ 8] #出發時間信息在8號位置data[ ‘到達時間‘] = item[ 9] #抵達時間在9號位置data[ ‘全程時間‘] = item[ 10] #經歷時間在10號位置data[ ‘商務座‘] = item[ 32] oritem[ 25] # 特別註意:商務座在32或25位置data[ ‘一等座‘] = item[ 31] #一等座信息在31號位置data[ ‘二等座‘] = item[ 30] #二等座信息在30號位置data[ ‘高級軟臥‘] = item[ 21] #高級軟臥信息在31號位置data[ ‘軟臥‘] = item[ 23] #軟臥信息在23號位置data[ ‘動臥‘] = item[ 27] #動臥信息在27號位置data[ ‘硬臥‘] = item[ 28] #硬臥信息在28號位置data[ ‘軟座‘] = item[ 24] #軟座信息在24號位置data[ ‘硬座‘] = item[ 29] #硬座信息在29號位置data[ ‘無座‘] = item[ 26] #無座信息在26號位置df1=pd.DataFrame(data,index=[ 0]) frames=[result,df1] result = pd.concat(frames,axis= 0, ignore_index= True) columns=[ ‘車次‘, ‘始發站‘, ‘終點站‘, ‘出發時間‘, ‘到達時間‘, ‘商務座‘, ‘一等座‘, ‘二等座‘, ‘高級軟臥‘, ‘軟臥‘, ‘動臥‘, ‘硬臥‘, ‘軟座‘, ‘硬座‘, ‘無座‘] result=result.reindex(columns=columns)
returnresult driver.quit()

下面,調用函數查詢武漢到滕州的1月26號火車票的情況:

a=get_news( ‘武漢‘, ‘滕州‘, ‘2019-01-26‘) #將函數返回結果賦給a

最終輸出結果如下,1月26日從武漢到滕州有一班高鐵和一班火車,並已全部告罄。想哭~~~

技術分享圖片

寫在最後

小夥伴你是否搶到票了呢?需要余票檢測提醒麽?

Python:爬蟲助你回家,12306余票監測!