我的例子都比較適合新手叹卷,那種老司機(jī)請繞道舌镶,謝謝!
ps
查詢車票接口被更換了豪娜,就是多了一個O而已餐胀,不知道啥時候又要換成什么樣?我tm能說是開發(fā)后臺的那個逼輸錯單詞了不https://kyfw.12306.cn/otn/leftTicket/queryO?leftTicketDTO.train_date=2017-10-31&leftTicketDTO.from_station=GIW&leftTicketDTO.to_station=ZIW&purpose_codes=ADULT
前言
最近學(xué)習(xí)Python瘤载,所以呢否灾?跟大家一樣,都是看看官網(wǎng)鸣奔,看看教程墨技,然后就準(zhǔn)備搞一個小東西來試試,那么我使用的例子是實驗樓中的12306火車票查詢例子挎狸。但是那個是2.7版本的扣汪,并且那個實驗樓的ubuntu系統(tǒng)老是一些包裝不上,沒辦法就在我電腦上搞好了锨匆。
結(jié)果展示:
下面這一段說明我是抄的崭别,哈哈,因為我自己再怎么寫還不是同樣的內(nèi)容。
讓我們先給這個小應(yīng)用起個名字吧恐锣,既然及查詢票務(wù)信息茅主,那就tickets,其實 大家隨意了,需要發(fā)布就需要起一個更好的名字土榴,不然只要自己玩兒的懂诀姚,但是要有程序的特點,所以還是tickets相關(guān)的吧玷禽。方便閱讀和自己記憶赫段。
我們希望用戶只要輸入出發(fā)站呀打,到達(dá)站以及日期就讓就能獲得想要的信息,比如要查看10月31號貴陽-遵義西的火車余票糯笙, 我們只需輸入:
python3.5 lnlr.py 貴陽 遵義西 2017-10-31
注意:上面的日期(包括后面的)是筆者寫文章時確定的日期聚磺,當(dāng)你在做這個項目的時候可能要根據(jù)當(dāng)前時間做適當(dāng)調(diào)整。
轉(zhuǎn)化為程序語言就是:
python3.5 lnlr.py from to date
另外炬丸,火車有各種類型,高鐵蜒蕾、動車稠炬、特快、快速和直達(dá)咪啡,我們希望可以提供選項只查詢特定的一種或幾種的火車首启,所以,我們應(yīng)該有下面這些選項:
-g 高鐵
-d 動車
-t 特快
-k 快速
-z 直達(dá)
這幾個選項應(yīng)該能被組合使用撤摸,所以毅桃,最終我們的接口應(yīng)該是這個樣子的:
python3.5 lnlr.py [options] from to date
接口已經(jīng)確定好了,剩下的就是實現(xiàn)它了准夷。
環(huán)境
Centos 7 linux 系統(tǒng)
Python3.5.2
使用到的庫
docopt------>命令行解釋器(把我玩兒死)
colorama--->一個文本著色器
requests --->爬蟲必備钥飞,http請求庫
prettytable->表格顯示
安裝庫
-
未安裝之前
Linux上面目前的庫
安裝庫:
pip3.5 install requests colorama docopt prettytble
-
安裝之后
安裝完成之后的庫列表
ok,我們環(huán)境有了衫嵌,庫有了读宙,那么應(yīng)該干啥呢?
爬蟲個人分析:
- 制定爬取內(nèi)容
- 選取目標(biāo)
- 準(zhǔn)備環(huán)境楔绞,上面就提前說了结闸,因為這個本來就是在搞爬蟲,所以...
- 分析該網(wǎng)站的html結(jié)構(gòu)酒朵,得到url
- 爬取數(shù)據(jù)
- 分析數(shù)據(jù)
- 封裝數(shù)據(jù)(組裝數(shù)據(jù))桦锄,弄成自己想要的樣子
- coding......
那么我們開始吧
第一步
當(dāng)然是打開12306的官網(wǎng)了,然后進(jìn)行一個余票查詢蔫耽,當(dāng)然首先你得按一下f12结耀,打開控制臺面板哦。
我是查詢的是:貴陽--遵義西 10-31號的車票
我先埋下一個伏筆:
我看到的貴陽-遵義 10-31的車次一共是11個班次饼记。
第二步
首先在控制臺找到Network按鈕,點擊慰枕。然后選擇XHR ---》找到請求到后臺的接口具则,
那么我們先看一個三個接口的請求和返回的數(shù)據(jù):
1.https://ad.12306.cn/sdk/webservice/rest/appService/getAdAppInfo.json?placementNo=0004&clientType=2&billMaterialsId=28e783cd2ec048ee8575cc3e502292c2
查看返回的結(jié)果:
貌似沒有什么有用的信息。
-
https://kyfw.12306.cn/otn/leftTicket/log?leftTicketDTO.train_date=2017-10-31&leftTicketDTO.from_station=GIW&leftTicketDTO.to_station=ZIW&purpose_codes=ADULT
好像也看不到關(guān)于貴陽-遵義的任何信息
哈哈具帮,在里面我看到了貴陽博肋,遵義低斋,那么大膽的猜測這個接口有可能就是我們需要的。
我們繼續(xù)點開看看有什么東西匪凡,這些數(shù)據(jù)都是以json的格式傳遞過來的膊畴。
到這里我相信,聰明的人已經(jīng)知道了這個就是我們所需要的接口了病游,而這些數(shù)據(jù)就絕對是車次信息的數(shù)據(jù)唇跨。
由上面的我f12查看到的數(shù)據(jù)是11條,那么你們就沒有點小激動么衬衬?
說明這個接口就是我們所需要的接口無誤买猖。那么現(xiàn)在我們就要得到它的url咯。
靠滋尉,雙十一快到了玉控,被女朋友抓去看了一會兒衣服,可能今天就不寫了狮惜,明天接著寫高诺。
那么這樣:我就明天開始分析url,然后就coding
請求的接口URL:
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-10-31&leftTicketDTO.from_station=GIW&leftTicketDTO.to_station=ZIW&purpose_codes=ADULT
分析:
1. 查看請求方式 POST/GET(常用的),這里使用的是GET請求
2. 肯定帶有參數(shù)了碾篡,并且GET請求是拼接到url之后的虱而,我們查詢的時候是輸入了起始地點,目的地點开泽,出發(fā)時間薛窥。那么url上也應(yīng)該帶有這三個。
3. 得到參數(shù)名稱:leftTicketDTO.train_date=2017-10-31,leftTicketDTO.from_station=GIW,leftTicketDTO.to_station=ZIW
還有一個參數(shù):purpose_codes=ADULT 根據(jù)ADULT的意思(成人眼姐,成年)大膽猜測這就是學(xué)生票和成人票诅迷。
4.得到了四個參數(shù),但是我們還不知道其中有兩個GIW,ZIW是什么意思众旗。
因為我輸入的是中文罢杉,但是出現(xiàn)的是字母代號。做過前后臺交互的同學(xué)應(yīng)該覺得這種是很常見的贡歧。目的是避免了中文傳輸導(dǎo)致的問題滩租。
分析參數(shù)的獲取
leftTicketDTO.train_date=2017-10-31 時間
leftTicketDTO.from_station=GIW 出發(fā)地
leftTicketDTO.to_station=ZIW 目的地
同學(xué)們請注意:我們輸入的是中文,出來的是地點代碼利朵,說明中間有一層轉(zhuǎn)換律想,那么在常規(guī)的網(wǎng)站中,只有兩種三種方式能這樣處理绍弟?
- 將這個地點-地點代碼字典寫入js中技即,這個地方有可能,因為國內(nèi)的地點太多樟遣,需要手動維護(hù)而叼。
- 將地點-地點代碼字典寫入本地文件文件身笤,做好緩存,每次讀取文件葵陵,然后使用液荸。
- 從遠(yuǎn)端服務(wù)器進(jìn)行獲取,在這里也沒必要脱篙,因為每次都要去請求后臺娇钱,增加服務(wù)器的壓力,這個是沒必要的绊困,因為這個字典的話是基本不會變化的文搂。
ok經(jīng)過上面的分析,我們就能很清楚的知道這個字典絕對是從js獲取的考抄,那么我們就也一樣的使用發(fā)f12來檢查到資源,找到該頁面所引用的所有js,然后進(jìn)行分析蔗彤。
上圖中的矩形中的就是當(dāng)前頁面中的所有js,當(dāng)然每個js的作用我就不一一的說了川梅,各位需要幫助的可以email(leihfein@gmail.com)我,或者在下面留言然遏。
那么我直接查看各個的內(nèi)容贫途,發(fā)現(xiàn)有一個是:
這樣我們就得到了一個js的請求地址哦待侵。https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9028
我們還可以來一個測試:
我在查詢的時候是輸入了丢早,貴陽-遵義,搜索一下看看吧秧倾。
同學(xué)們怨酝,看到這個你們覺得爽不爽,說明這個文件就是我們所需要的那先。
ok农猬,到這里,編碼前期準(zhǔn)備工作售淡,所有的都昨晚了斤葱,我從一步一步的分析,然后截圖揖闸。給大家思路揍堕,方法,步驟汤纸。希望大家能夠更明白衩茸,更多的是學(xué)習(xí)到其中的分析思路哈。
codingwars
- 首先爬取地點-代碼code字典贮泞。
#!/usr/bin/env python3
# coding: utf-8
import requests
import re
from pprint import pprint
"""
獲取到地點-地點code字典
"""
def get_station():
url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9028'
response = requests.get(url,verify=False)
station = re.findall(u'([\u4e00-\u95fa5]+)\|([A-Z]+)',response.text)
pprint(dict(station),indent=4)
if __name__=="__main__":
get_station()
在最后递瑰,我是使用pprint輸出到屏幕的祟牲,大家可以進(jìn)行拷貝,或者是使用Linux的重定向進(jìn)行輸出到別的文件 抖部。
python3.5 get_station_code.py > stations.py
并且需要在stations.py文件中增加一個名字哦说贝,因為輸出來時也沒有名字的。見下圖
在windows中就只能拷貝了慎颗,
好的乡恕,到這里我們的地點-地點代碼就得到了,那么我們就應(yīng)該寫那個爬取車次信息的py了俯萎。
處理輸出的代碼:
from prettytable import PrettyTable
from stations import stations
from colorama import init, Fore
"""
處理爬取出來的車次信息傲宜,并進(jìn)行表格輸出
"""
init()
class TrickCollection(object):
def __init__(self, available_trains, options):
self.header = ('車次 車站 時間 歷時 特等座 一等 二等 高級軟臥 軟臥 動臥 硬臥 '
+ '軟座 硬座 無座 備注').split()
self.available_trains = available_trains
self.options = options
# 將歷時轉(zhuǎn)化為小時和分鐘的形式
def get_duration(self, raw_train):
duration = raw_train[10].replace(':', '小時') + '分'
if duration.startswith('00'):
return duration[4:]
if duration.startswith('0'):
return duration[1:]
return duration
# 返回每個車次的基本信息
def trains(self):
for raw_train in self.available_trains:
# 列車號
train_no = raw_train[3]
# 得到什么列車并小寫
initial = train_no[0].lower()
# 反轉(zhuǎn)station所對應(yīng)的字典
stations_re = dict(zip(stations.values(), stations.keys()))
if not self.options or initial in self.options:
# 將車次的信息保存到列表中
# train 出發(fā)地
begin_station = stations_re.get(raw_train[4])
# train 目的地
end_station = stations_re.get(raw_train[5])
# your 出發(fā)地
from_station = stations_re.get(raw_train[6])
# your 目的地
to_station = stations_re.get(raw_train[7])
# 判斷是起始還是經(jīng)過
begin_flag = self.__check_equals(begin_station, from_station)
end_flag = self.__check_equals(end_station, to_station)
train = [
train_no,
'\n'.join([begin_flag + ' ' + self.__get_color(Fore.GREEN, from_station),
end_flag + ' ' + self.__get_color(Fore.RED, to_station)]),
'\n'.join([self.__get_color(Fore.GREEN, raw_train[8]),
self.__get_color(Fore.RED, raw_train[9])]),
# 時間
self.get_duration(raw_train),
# 歷時
raw_train[32],
# 特等座
self.__show_color(raw_train[31]),
# 一等
self.__show_color(raw_train[30]),
# 二等
self.__show_color(raw_train[22]),
# 高級軟臥
self.__show_color(raw_train[23]),
# 軟臥
self.__show_color(raw_train[33]),
# 硬臥
self.__show_color(raw_train[28]),
# 軟座
self.__show_color(raw_train[24]),
# 硬座
self.__show_color(raw_train[29]),
# 無座
self.__show_color(raw_train[26]),
# 備注
self.__show_color(raw_train[1])
]
# 更改不運行車次的時間和歷時
if raw_train[14] == 'null':
train[2] = '--\n--'
train[3] = '--'
# 將空字符串轉(zhuǎn)化為‘--’
for i, item in enumerate(train):
if not item:
train[i] = '--'
yield train
def __check_equals(self, from_station, to_station):
"""
檢查是否是始、過
檢查你的起始站是否為該車次的起始站
檢查你的終止站是否為該車次的終止站
:param from_station: 出發(fā)位置
:param to_station: 結(jié)束位置
:return: 決定了是使用‘始' 還是 ’過‘
"""
if from_station == to_station:
return '始'
else:
return '過'
def __get_color(self, color, content):
"""
返回顏色內(nèi)容組合夫啊,并且清除該內(nèi)容之后的顏色
:param color: 傳遞的顏色
:param content: 內(nèi)容
:return: 返回值為拼接上顏色
"""
return color + content + Fore.RESET
def __show_color(self, content):
"""
對內(nèi)容進(jìn)行顏色顯示函卒,并且只顯示有入蛆,其余不上色
:param content: 需要顏色顯示的內(nèi)容
:return: 返回設(shè)置結(jié)果
"""
if content == '有':
return Fore.GREEN + content + Fore.RESET
else:
return content
def pretty_print(self):
"""
顯示內(nèi)容
:return:
"""
pt = PrettyTable()
pt._set_field_names(self.header)
for train in self.trains():
pt.add_row(train)
print(pt)
成果展示:
但是我在遠(yuǎn)端上使用xshell沒有顏色土全,這個是什么鬼。最后發(fā)現(xiàn)是自己設(shè)置xshell而已贡未。
好的熊榛,下面就是我們的程序執(zhí)行的結(jié)果锚国。
如果程序輸入的地點在字典中查詢不到,那么就不能查票玄坦。所以我們需要隨時更新stations.py文件(地點-code字典表)
ok血筑,到這里的話,我們的所有的爬蟲就抓取成功煎楣,并且輸出成我們想要的結(jié)果了豺总。我準(zhǔn)備下一步就是搞那個搶票。試試吧择懂。
代碼在后面我會給出github地址的园欣。
總結(jié)
在本次實驗中,我所遇到的問題簡單說一下:
- optdoc的使用休蟹,這個是我遇到的最大的坑沸枯,(usage 下面命令之后必須空一行)
- 在Linux上的縮進(jìn)問題
3.就是對python還不是特別的熟。
github地址
閱讀到本教程的童鞋赂弓,喜歡請點個喜歡绑榴,不求贊賞,只求喜歡盈魁,頂上去∠柙酰現(xiàn)在網(wǎng)上很多都是以前的接口的例子,已經(jīng)跑不起來了,所以我希望讓更多的人看到赤套,能幫助更多還在py的起步階段飘痛,又沒有人練手項目的人。