各位同學大家好,我是強子,好久沒跟大家帶來最新的技術文章了,最近有好幾個同學問我12306自動搶票能否實現,我就趁這兩天有時間用Python做了個12306自動搶票的項目,在這里我來帶著大家一起來看看到底如何一步一步攻克萬惡的12306,嘻嘻~~
我們要做12306搶票而官方又沒有提供相應的接口(也不可能提供),那么我們就只能通過自己尋找12306的數據包和買票流程來模擬瀏覽器行為實現自動化操作了,說直白一點就是爬蟲,接下來進入正題,前方高能,請系好好全帶~~
首先在買票前我們需要先確認是否有票,那么進行正常的查票,打開12306查票網站https://kyfw.12306.cn/otn/leftTicket/init
輸入出發地和目的地進行搜索
那么一般在看到這個頁面的時候我們能想到的獲取車次及相關信息的方式是什么呢?對于零基礎的同學而言第一時間就會想到在源代碼里面找,但這里事實上源代碼里面根本沒有相關內容,因為該請求是采用的js中ajax異步請求的方式動態加載的,并不包含在源代碼里面,所以我們只能夠通過抓包的方式來查看瀏覽器與服務器的數據交互情況,我用的是谷歌瀏覽器所以打開開發者工具的快捷鍵是F12
注意選中紅線框出來的那一個選項,此時只要是瀏覽器和服務器發生數據交互都會在下面列表框顯示出來,我們再次點擊查詢
按鈕
結果發現列表當中有了兩個請求,也就是說我們點擊查詢
按鈕以后瀏覽器向服務器發起了兩次請求,那么我們來通過返回值分析下那個請求才是真正獲取到車次相關數據的請求,以便我們用Python來模擬瀏覽器操作
第一次請求:
很明顯第一次請求返回的值沒有我們需要的車次信息
第二次請求:
第二次請求里面看到了很多數據,雖然我們暫時還沒看到車次信息,但是我們發現它有個特性,就是有個列表的值里面有6個元素,而剛好我們搜索出來的從長沙到成都的車輛也是6條數據,所以這兩者肯定有一定關系,那么我們先用Python來獲取到這些數據再進行下一步分析
# -*- coding: utf-8 -*-
import urllib2
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
def getList():
req = urllib2.Request('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT')
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
html = urllib2.urlopen(req).read()
return html
print getList()
首先定義一個函數來獲取車次列表信息
從抓包數據中獲取到該請求的url:https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT
為了防止被12306檢測到屏蔽我們的請求那么我們可以簡單的增加個頭信息來模擬瀏覽器的請求
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
其中的
ssl._create_default_https_context = ssl._create_unverified_context
是因為12306采用的是https協議,而ssl證書是它自己做的并沒有得到瀏覽器的認可,所以Python默認是不會請求不受信任的證書的網站的,我們可以通過這行代碼來關閉掉證書的驗證
那么我們先來看看能不能正常獲取到我們想要的信息
事實證明我們的操作沒有問題,接下來先拿到包含有6條數據的這個列表再說
返回的數據是json格式,但是Python標準數據類型中沒有json這個類型,所以對于Python而言它就是個字符串,如果要非常方便的操作這個json我們就可以借助Python中的json這個包來把json這個字符串變成dict類型,然后通過dict的鍵值對操作方法把列表取出來并進行返回
# -*- coding: utf-8 -*-
import urllib2
import ssl
import json
ssl._create_default_https_context = ssl._create_unverified_context
def getList():
req = urllib2.Request('https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT')
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36')
html = urllib2.urlopen(req).read()
dict = json.loads(html)
result = dict['data']['result']
return result
最終返回的是一個list數據,我們先把這個數據for出來再看看每一條數據都有些什么東西
for i in getList():
print i
for出來之后我們先來看看第一條數據是什么樣的:
|預訂|76000G131805|G1318|ICW|IZQ|ICW|CWQ|07:54|18:54|11:00|N|UHESFcaIDeX22Z0zWfqttDuZXJFuWPdIa148i6TNk5spIqfp|20170710|3|W2|01|16|0|0|||||||||||無|無|無||O0M090|OM9
其實我們稍微留一下就會發現里面有包含G1318,07:54,18:54,無
這樣的車次信息的,只不過看起來比較亂,但是他們都有一個特點,每個數據都是由|
這個符號分開的,所以我們可以通過用|
分割看看能發現什么呢?
for i in getList():
for n in i.split('|'):
print n
break
可以看到所有的值都打印出來了,我們再在前面加上一個序號就能清楚到看到每個序號所對應的值到底是什么了,比如有輛火車硬座還剩3張票,軟臥還剩8張票,那我們就查看哪個序號對應的值是3哪個序號對應的值是8就搞清楚了哪個序號是代表什么座次或者其他參數了
c = 0
for i in getList():
for n in i.split('|'):
print '[%s] %s' %(c,n)
c += 1
c = 0
break
#索引3=車次
#索引8=出發時間
#索引9=到達時間
到了這里不知道同學們有沒有發現一個問題,就是我用的這個函數只能夠獲取到從長沙
到成都
的數據,而別人不一定是買這個方向的火車,所以我們還得搞清楚請求的url當中的出發站和到達站的值是怎么來的
https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date=2017-07-10&leftTicketDTO.from_station=CDW&leftTicketDTO.to_station=CSQ&purpose_codes=ADULT
先找到出發站和到達站的參數分別是
leftTicketDTO.from_station=CDW
leftTicketDTO.to_station=CSQ
然而通過查找和分析我并沒有發現這兩個參數有規律,那么也就是說這兩個值是在之前的請求里面就已經獲取到了的,通過檢查網頁源代碼沒有找到,那么又只能通過抓包的方式來找
在抓包過程中找到了一個包的返回值是附帶有各城市的代號的,url如下:
https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9018
那么我們把這里面的城市數據復制出來單獨新建一個cons.py的文件保存起來
然后我們通過把參數做成通過輸入出發城市和到達城市就可以直接在這個數據里面匹配到相應的城市代號,代碼如下:
station = {}
for i in cons.station_names.split('@'):
if i:
tmp = i.split('|')
station[tmp[1]] = tmp[2]
#print station
train_date = raw_input('請輸入出發時間')
from_station = station[raw_input('請輸入出發城市')]
to_station = station[raw_input('請輸入到達城市')]
到這里就已經能夠通過輸入時間,城市
獲取相應的車次信息了
那么我們再進行一些簡單的判斷,就能實現檢查相應的時間,地點,車次
是否有余票了
同時再結合登錄,購票
等流程,通過自動判斷是否有票,如果無票就繼續刷新,直到有票之后自動登錄下單后通過短信或者電話等方式全自動聯系購票人手機就可以了,如下圖
由于涉及到的知識點太多,僅僅用文字的方式很難表現的面面俱到,所以有時間的同學可以到我的課堂做客,現場直播講解更加的生動易懂!
好了,本次分享就到這里了,我是強子,我們下次再見!
主題大綱
在學習Python的過程中很多時候對于ajax異步加載和驗證碼的爬蟲束手無策,其實只要了解了原理就容易解決了,一個爬蟲高手一定是一個web開發高手,因為爬蟲和反爬是相輔相成的,能夠完成12306網站的自動購票基本能搞定互聯網80%以上的爬蟲
知識點1:Python爬蟲包的使用
知識點2:文本處理,提取指定信息,過濾無用信息
知識點3:爬蟲開發思想和原則,爬取深度定義
知識點4:ajax異步的處理
知識點5:項目完整業務邏輯實現
聽眾收益
- 了解爬蟲開發流程
- 熟練使用相應的包來實現目標數據爬取
- 編碼的原理及處理方法,獨立處理所有的編碼問題
- 學習高級爬蟲的分析和開發