1. 程式人生 > >python學習之 12306的一個小爬蟲

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 '這兩個站點沒有直達列車'