Python爬蟲實現12306火車票查詢
昨天早上,突發奇想想要給基友弄一個火車票查詢工具,順便熟悉一下html、json、js格式,為之後製作微信小程式做準備,於是便開始了爬蟲的道路。
12306網站想要爬跟之前爬靜態網頁並不一樣,首先由於是一個查詢工具,必須要先把網頁設定為查詢頁,才能夠爬取網頁的資訊
開啟12306網站 查詢北京到上海的火車票
看起來網頁地址並沒有任何變化
這個時候就需要用到瀏覽器的一些工具,這裡使用的是chrome瀏覽器
開啟工具檢視到XHR請求處出現了日期、出發站、到達站的資訊
此時便可以通過複製貼上這段網址來獲取相應的火車資訊
但是會發現我們輸入的站點為中文,而網頁程式碼是站點的英文編號,那麼如何進行站點的中英文切換呢
這個時候就需要去查詢中英文對應的“字典”
在這裡的976行找到了station_version=1.9050,這是火車站版本號,複製這個字尾,在12306網址後加上,便可以到達這個頁面
已經可以看到火車站中文名與英文編號,此時就要使用requests庫來提取網頁資訊
關於requests庫:https://www.cnblogs.com/zhaof/p/6915127.html
對於資訊的提取可以看到我們要提取的資訊是大寫英文字母以及中文,因此可以使用正則表示式(Re)
關於Re庫:https://blog.csdn.net/i_chaoren/article/details/62264414
除此以外此網站會有如此提示:
requests\packages\urllib3\connectionpool.py:843: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification
因此加入
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
這段程式碼來禁止出現警告提示
這就是提取火車站中英文對照的完整程式碼
import requests
import re
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9050'
r = requests.get(url,verify=False) #提取網頁資訊,不判斷證書
pattern = u'([\u4e00-\u9fa5]+)\|([A-Z]+)' #正則表示式提取中文以及大寫英文字母
result = re.findall(pattern,r.text) #進行所需要的資訊的提取
station = dict(result) #把所獲資訊設定為一一對應(有點像是c++裡的map)
print(station.keys()) #隨後列印
print(station.values())
隨後將輸出的資料儲存到另一個檔案(stations1.py)中
這個時候就可以使用這個檔案來進行中文站名和英文程式碼的切換了
由於涉及到中文,在使用這個station1.py檔案時有可能報以下錯誤:
此時只需要在檔案的開頭加入
# coding=gbk
即可
除此以外定義兩函式進行中文名字和英文程式碼的對應獲取
在這個過程中還有其他很多奇怪的錯誤,時間都用來debug就對了
chrome也開滿了各種查錯誤進行debug的網頁
隨後使用一個PrettyTable庫來進行資訊對齊表格美化(這個庫要注意大小寫)
關於PrettyTable庫:https://blog.csdn.net/codeway3d/article/details/52798804
隨後要在查詢到的網址中找到不同火車票的資訊
這真是找得生無可戀...
找到以後進行資訊的對照,一個一個對應網頁上的坐席進行對照,確定每一個對應的是什麼坐席
pt = PrettyTable()
pt._set_field_names('車次 車站 時間 歷時 商務座 一等座 二等座 動臥 軟臥 硬臥 硬座 無座'.split())
for raw_train in raw_trains:
data_list = raw_train.split('|')
train_no = data_list[3]
initial = train_no[0].lower()
if not options or initial in options:
from_station_code = data_list[6]
to_station_code = data_list[7]
from_station_name = ''
to_station_name = ''
start_time = data_list[8]
arrive_time = data_list[9]
time_duration = data_list[10]
business_seat = data_list[32] or '--'
first_class_seat = data_list[31] or '--'
second_class_seat = data_list[30] or '--'
pneumatic_sleep = data_list[33] or '--'
soft_sleep = data_list[23] or '--'
hard_sleep = data_list[28] or '--'
hard_seat = data_list[29] or '--'
no_seat = data_list[26] or '--'
pt.add_row([train_no,
'\n'.join([stations1.get_name(from_station_code), stations1.get_name(to_station_code)]),
'\n'.join([start_time, arrive_time]),
time_duration,
business_seat,
first_class_seat,
second_class_seat,
pneumatic_sleep,
soft_sleep,
hard_sleep,
hard_seat,
no_seat
])
關鍵的PrettyTable模組
以下是所有程式碼:
import requests
import stations1
from prettytable import PrettyTable
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def cli():
str = input("請輸入出發站: ");
str1 = input("請輸入到達站: ");
date = input("請輸入出發日期(格式xxxx-xx-xx): ");
options = input("請輸入列車型別(格式g、d、t、k、z): ");
from_station = stations1.get_telecode(str)
to_station = stations1.get_telecode(str1)
url = ('https://kyfw.12306.cn/otn/leftTicket/queryO?'
'leftTicketDTO.train_date={}&'
'leftTicketDTO.from_station={}&'
'leftTicketDTO.to_station={}&'
'purpose_codes=ADULT').format(date,from_station,to_station)
r = requests.get(url, verify=False)
raw_trains = r.json()['data']['result']
pt = PrettyTable()
pt._set_field_names('車次 車站 時間 歷時 商務座 一等座 二等座 動臥 軟臥 硬臥 硬座 無座'.split())
for raw_train in raw_trains:
data_list = raw_train.split('|')
train_no = data_list[3]
initial = train_no[0].lower()
if not options or initial in options:
from_station_code = data_list[6]
to_station_code = data_list[7]
from_station_name = ''
to_station_name = ''
start_time = data_list[8]
arrive_time = data_list[9]
time_duration = data_list[10]
business_seat = data_list[32] or '--'
first_class_seat = data_list[31] or '--'
second_class_seat = data_list[30] or '--'
pneumatic_sleep = data_list[33] or '--'
soft_sleep = data_list[23] or '--'
hard_sleep = data_list[28] or '--'
hard_seat = data_list[29] or '--'
no_seat = data_list[26] or '--'
pt.add_row([train_no,
'\n'.join([stations1.get_name(from_station_code), stations1.get_name(to_station_code)]),
'\n'.join([start_time, arrive_time]),
time_duration,
business_seat,
first_class_seat,
second_class_seat,
pneumatic_sleep,
soft_sleep,
hard_sleep,
hard_seat,
no_seat
])
print(pt)
if __name__ == '__main__':
cli()
其中colorama庫執行不成功,不知道原因是什麼,會出來一些亂碼
執行結果(由於打算端午節自己去黃山溜溜,於是查的是到黃山的火車)
加上中午出去吃飯以及晚上回鄉下吃飯的時間,製作用時10h(自己真的菜)
製作參考文章:
Python爬取12306實現火車票查詢——https://blog.csdn.net/tigaoban/article/details/78558417?locationNum=4&fps=1
從零開始寫Python爬蟲 --- 爬蟲應用: 12306火車票資訊查詢——https://zhuanlan.zhihu.com/p/27969976
製作參考視訊:
【公開課】實驗樓帶你學爬蟲——Python實現火車票爬蟲工具——https://www.bilibili.com/video/av12380578/