python學習之 12306的一個小爬蟲
本文思路主要來源於實驗樓的教程,但是一些具體的一些細節是我自己發現的,比如哪裡獲得站點對應的3位英文編號,怎麼獲得這個查詢的url
本文用到的庫主要有requests(獲取url的內容),prettytable(讓文字輸出美觀),argparse(命令列引數解析)
關於這些庫怎麼使用,可以參見我之前的博文
1、首先開啟12306餘票查詢的介面
https://kyfw.12306.cn/otn/lcxxcx/init
我們想要的資訊當然就是在輸入了始發站、終點站和日期之後各車次的時間和車票餘量,那麼我們嘗試在始發站使用檢查元素,觀察一下它是怎麼上傳始發站的資訊的,那麼我們不妨隨便輸入出發地、目的地和資訊,使用抓包工具來看看它是怎麼發包的(使用瀏覽器也可以,因為我們只需要檢視包的內容,不需要更改包)
2、
在chrome的network中我們可以檢視到我們點選之後瀏覽器傳送的所有包(關於http包的知識不熟悉的同學,可以看看《圖解http》這本書)
點選查詢之後我們馬上就會注意到以query開頭的這個包,顯然這就是一個查詢指令,我們看看這個包的url
'https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate=2016-10-04&from_station=BJP&to_station=XKS'
然後我們看看它的response
仔細觀察就能發現它其實是一串json格式的字串(要非常有經驗。。。。)
3、經過以上這些過程,我們大致就能知道我們需要做的是什麼了,我們只需要更改url中的data,fromstaion,tostaion後面的內容,然後用requests獲得response,然後解析這一串json字元就行了。
但是我們會發現,日期還好說,對於fromstation和tostaion的程式碼,我們該怎麼辦呢?
4、有兩種可能,一中可能是這些檔案在伺服器上,每回改變站點網頁都會從伺服器請求這個站點的程式碼,還有一種可能是這個已經下載到本地了,如何判斷呢?我們不妨改變一下始發站,然後用抓包軟體(或者瀏覽器)觀察我們的瀏覽器是否向12306傳送了包
把北京改成了上海,但是我們發現瀏覽器並沒有傳送包
這樣我們基本可以肯定這個車站編號資訊是存在本地了(已經從伺服器下載下來)
5、我們這時候,就需要分析html來發現這個編號資訊到底儲存在了那裡
我們試著檢查一下出發地附近的html標籤, 在‘熱門’上面點選檢查,我們很容易發現這個標籤上面帶了一個onclick方法
我們發現這個onclick方法指向了一個js檔案,並且名字是‘Stationfor12306’,基本我們可以確定這個js檔案就是我們需要的站點資訊檔案了。
6、我們嘗試在這個html(12306餘票查詢介面)裡面搜一下stationfor,我們馬上就能發現,它就在<head>標籤的<script>元素裡,並且指向了一個url
進入這個url看看,我們馬上就發現站點資訊已經被我們找到啦(注意這是一個相對URL,絕對url需要在前面補上https://kyfw.12306.cn/)
關於怎麼獲取三位數的車站程式碼,用正則,字串查詢都是可以的啦,由於這裡是固定的3位車站程式碼,我就用簡單的字串查詢來提取這個程式碼了。
7、剩下的工作,基本就是程式碼實現了,關於具體怎麼實現,大家可以參看實驗樓的程式碼,我也把我的程式碼貼在下面了。
實驗樓:https://www.shiyanlou.com/courses/623/labs/2072/document
我的程式碼
#coding=utf-8
import requests
import argparse
import datetime
import re
from prettytable import PrettyTable
now = datetime.datetime.now()
tomorrow = now+datetime.timedelta(days=1)
tomorrow = tomorrow.strftime('%Y-%m-%d')
print tomorrow
argument = argparse.ArgumentParser()
argument.add_argument('--fromcity','-f',default='hangzhoudong')
argument.add_argument('--tocity','-t',default='xiamen')
argument.add_argument('--date','-d',default=tomorrow)
# argument.add_argument('-d',action='store_true')
args =argument.parse_args()
from_station = args.fromcity
to_station = args.tocity
Date = args.date
stationlist_url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js'
r = requests.get(stationlist_url, verify=False)
stationlist = r.content
ToStation = ''
FromStation = ''
placea = stationlist.find(from_station)
placeb = stationlist.find(to_station)
for i in range(-4,-1):
FromStation += stationlist[placea+i]
for i in range(-4,-1):
ToStation += stationlist[placeb+i]
query_url='https://kyfw.12306.cn/otn/lcxxcx/query?purpose_codes=ADULT&queryDate='+Date+'&from_station='+FromStation+'&to_station='+ToStation
r = requests.get(query_url,verify=False)
with open('json.txt','w') as fp:
fp.write(str(r.json()))
if 'datas' in r.json()["data"]:
rj = r.json()["data"]["datas"]
pt = PrettyTable()
header = '車次 車站 到站時間 時長 一等座 二等座 軟臥 硬臥 硬座 無座'.split()
pt._set_field_names(header)
for x in rj:
ptrow = []
ptrow.append(x["station_train_code"])
ptrow.append('\n'.join([x["from_station_name"],x["to_station_name"]]))
ptrow.append('\n'.join([x["start_time"], x["arrive_time"]]))
ptrow.append(x["lishi"].replace(':','h')+'m')
ptrow.append(x['zy_num'])
ptrow.append(x['ze_num'])
ptrow.append(x['rw_num'])
ptrow.append(x['yw_num'])
ptrow.append(x['yz_num'])
ptrow.append(x['wz_num'])
pt.add_row(ptrow)
print pt
else :
print '這兩個站點沒有直達列車'