點擊上方藍色字關(guān)注 [ 啃餅思錄 ]~ 12306火車車次信息爬取分析 本篇我們要進行的是12306火車車次信息的爬取分析。都說12306是目前反爬措施最強的網(wǎng)站,的確如此。博主于2017年專門研究過如何爬取并進行了購票分析,費了很大功夫終于成功地搶到了票,但是很不幸,沒過多久12306就進行了大改版,寫過的爬蟲代碼幾乎成為了擺設(shè)。這里只是爬取車次信息,并不進行購票操作,后續(xù)可能會出專門的教程介紹這一塊,本篇文章的重點不在于此。 ![]() 分析與爬取過程車站名稱信息爬取12306網(wǎng)址: 通過觀察,很容易發(fā)現(xiàn)出發(fā)地和到達地都是一個彈窗,由JS控制。我們思考一下,如果想要爬取所有的車站名稱,是不是爬取這個JS彈窗內(nèi)的信息就可以?是的,我們就是這樣做的。首先我們需要登錄賬號,接著進行下面的操作。我們按F12或者直接開啟開發(fā)者模式,先在出發(fā)地隨便選一個車站,然后觀察network處的變化,我們發(fā)現(xiàn)了一個名為station_name_v10026.js的文件,就是這樣:
接著我們就點擊這個js文件,頁面就跳轉(zhuǎn)到:
接下來就是將里面的車站名稱都爬取下面。先點擊這里:漢字 Unicode 編碼范圍,我們發(fā)現(xiàn)一般車站都是常見字,因此使用4E00-9FA5區(qū)間的unicode編碼即可。我們可以使用 import requestsimport re headers = { 'Cookie': '', 'Host': 'www.12306.cn', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}def getStationName(): # 爬取12306網(wǎng)站所有車站名稱信息 url = 'https://www.12306.cn/index/script/core/common/station_name_v10026.js' # 車站信息控件 r = requests.get(url) pattern = '([\u4e00-\u9fa5]+)\|([A-Z]+)' # 正則匹配規(guī)則 result = re.findall(pattern, r.text) stationName = dict(result) # 所有車站信息,轉(zhuǎn)換為字典 print(stationName) # return stationNamegetStationName() 運行結(jié)果如下(部分信息): {'北京北': 'VAP', '北京東': 'BOP', '北京': 'BJP', '北京南': 'VNP'} 兩站之間火車票信息查詢在前面我們查詢到了所有的車站名稱,接下來我們就是查詢兩站之間火車票的信息了。我們隨意輸入兩個城市,上海和天津,時間更是隨意,如下所示: 我們注意一下此時url欄的變化,此時url變?yōu)椋?/p> 我們嘗試輸入北京到上海,再次確認一下url的變化: 說明后面的 所以你可以簡化前面爬取的內(nèi)容,但是我這里就不了,因為我個人比較喜歡這種方式。 接下來就是構(gòu)建查詢的代碼了,結(jié)合上面查詢到的車站信息,我們就能得到如下代碼: import requestsimport re headers = { 'Cookie': '', 'Host': 'www.12306.cn', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36'}def getStationName(): # 爬取12306網(wǎng)站所有車站名稱信息 url = 'https://www.12306.cn/index/script/core/common/station_name_v10026.js' # 車站信息控件 r = requests.get(url) pattern = '([\u4e00-\u9fa5]+)\|([A-Z]+)' # 正則匹配規(guī)則 result = re.findall(pattern, r.text) stationName = dict(result) # 所有車站信息,轉(zhuǎn)換為字典 # print(stationName) return stationName text =getStationName()def get_query_url(text, date, from_station, to_station): # 構(gòu)建用于查詢列車車次信息的url # 參數(shù):日期,出發(fā)地,到達地 # key為車站名稱, value為車站代號 date = date from_station = from_station+","+text[from_station] to_station = to_station+","+text[to_station] # 新的url url = ( "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&" "fs={}" "&ts={}" "&date={}" "&flag=N,N,Y" ).format(from_station, to_station, date) print(url) get_query_url(text,'2019-04-20','上海','天津') 該程序運行結(jié)果為: https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc&fs=上海,SHH&ts=天津,TJP&date=2019-04-20&flag=N,N,Y 拿著這個url去輸入欄訪問一下,發(fā)現(xiàn)可以正常訪問了,頁面顯示票務(wù)信息。 獲取火車票詳情現(xiàn)在我們就是獲取剛才鏈接頁面出現(xiàn)的票務(wù)詳情信息了,我們需要明確獲取的字段信息:車次、出發(fā)站、到達站、出發(fā)時間、到達時間、歷時、商務(wù)/特等座、一等座、二等座、軟臥、硬臥、硬座和無座等。我們拿到剛才生成的url,訪問一下(開啟開發(fā)者模式): 我們發(fā)現(xiàn)有一個query,然后右邊就是我們查詢的結(jié)果,因此我們可以從這個結(jié)果中取出信息: https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2019-04-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=TJP&purpose_codes=ADULT 看到這里,你或許會明白,我們其實構(gòu)造的url最后就是以這個url的查詢結(jié)果的形式返回,那么我們可不可以直接構(gòu)造這個url呢?答案是可以的,那么我們對上面的代碼進行修改一下: def get_query_url(text, date, from_station, to_station): # 構(gòu)建用于查詢列車車次信息的url # 參數(shù):日期,出發(fā)地,到達地 # key為車站名稱, value為車站代號 date = date from_station = text[from_station] to_station = text[to_station] # 新的url url = ( "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}" "&leftTicketDTO.from_station={}" "&leftTicketDTO.to_station={}" "&purpose_codes=ADULT" ).format(date, from_station, to_station) print(url) return url 運行結(jié)果如下: https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2019-04-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=TJP&purpose_codes=ADULT 截圖就是這樣: 也就說結(jié)果是一個json形式的dict,我們先根據(jù)data這個key取出全部信息,再根據(jù)result這個key取出車次信息。圖中密密麻麻的就是我們需要的車次信息: 從這個信息中遍歷一下,就能得到我們需要的信息,0代表某一輛車的信息,再從0這個一行中取出我們需要的字段信息即可:
我們發(fā)現(xiàn)每個字段都是被 import requests url="https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2019-04-20&leftTicketDTO.from_station=SHH&leftTicketDTO.to_station=TJP&purpose_codes=ADULT"r =requests.get(url) all_trains = r.json()['data']['result'] # 獲取所有車次信息for one_train in all_trains: # 遍歷取出每輛車的信息 data_list = one_train.split('|') train_number = data_list[3] # 車次 from_station_code = data_list[6] # 出發(fā)站代號 from_station_name = '上海' # 出發(fā)站名稱 to_station_code = data_list[7] # 到達站代號 to_station_name = '天津' # 到達站名稱 go_time = data_list[8] # 出發(fā)時間 arrive_time = data_list[9] # 到達時間 cost_time = data_list[10] # 歷時 special_class_seat = data_list[32] or '--' # 商務(wù)/特等座 first_class_seat = data_list[31] or '--' # 一等座 second_class_seat = data_list[30] 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 '--' # 無座 print(train_number,from_station_code,from_station_name,to_station_code,to_station_name,go_time,arrive_time,cost_time,special_class_seat,first_class_seat,second_class_seat,soft_sleep, hard_sleep,hard_seat,no_seat)
G108 AOH 上海 TIP 天津 07:22 12:45 05:23 5 有 有 -- -- -- --G1232 AOH 上海 TXP 天津 07:34 13:45 06:11 無 無 有 -- -- -- --G120 AOH 上海 TIP 天津 07:51 12:49 04:58 16 有 有 -- -- -- --G112 AOH 上海 TIP 天津 08:05 13:24 05:19 無 有 有 -- -- -- --G212 AOH 上海 TXP 天津 08:42 14:26 05:44 有 有 有 -- -- -- --G1258 AOH 上海 TXP 天津 09:08 14:34 05:26 -- 19 有 -- -- -- -- 可以看到數(shù)據(jù)吻合,這樣我們就完成了本篇文章的要求。 |
|