1. 程式人生 > >Python學好了,飛機票你還買不到麼?

Python學好了,飛機票你還買不到麼?

Python學好了,飛機票你還買不到麼?

 

專案:機票資料採集

使用模組:requests(請求模組),js2py(js執行模組),json(解析json),xpath(解析網頁)。

學習Python中有不明白推薦加入交流裙
                                                             號:735934841


                                                                                      群裡有志同道合的小夥伴,互幫互助,

                                                                                                                                                  群裡有免費的視訊學習教程和PDF!

專案流程:

  • 分析網站資料來源。
  • 編寫爬蟲指令碼。
  • 驗證資料準確性。
  • js逆向破解引數生成。
  • 更換請求引數城市(飛機起飛城市和落地城市或日期)測試結果是否正常。

1.分析網站資料來源

進入藝龍機票列表搜尋頁,附上鍊接 http://flight.elong.com/flightsearch/list?departCity=bjs&arriveCity=sha&departdate=2018-12-2 4,連結引數日期自行更改。

Python學好了,飛機票你還買不到麼?

 

一般情況資料為呼叫介面獲得,或是在頁面中嵌入,這裡很明顯是呼叫了介面。

F12開啟開發者工具(谷歌瀏覽器),選擇network中的xhr,然後重新整理頁面或重新搜尋,檢視呼叫的介面。(這一步也可以使用抓包工具,推薦使用Fiddler,網上有許多漢化版的,看個人習慣吧。)

Python學好了,飛機票你還買不到麼?

 

呼叫了四個介面,點選介面檢視返回結果,確定資料來源。

Python學好了,飛機票你還買不到麼?

 

看到出發機場,航空公司名稱之類的英文,ok,就是這個了,點選進入Headers。

Python學好了,飛機票你還買不到麼?

 

Python學好了,飛機票你還買不到麼?

 

資料來源已經確定,下面我們來構造爬蟲請求介面。

2.編寫爬蟲指令碼

快速上手requests模組,連結已備好http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

直接上程式碼(提示:程式碼中的請求引數grabcode的值需要自己抓取,有時效性,過期無返回結果導致程式碼報錯):

import requests #匯入requests模組
#請求連結
url = 'http://flight.elong.com/search/ly/rest/list'
#構造請求頭 介面中請求頭有的引數最好全寫上,之後再瞭解這些請求頭資訊是幹什麼的,這裡不做介紹
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
}
#請求引數
data = {
"p": '{"departCode":"bjs","arriveCityCode":"sha","departDate":"2018-12-24","searchType":"0","classTypes":null,"isBaby":0}',
"grabCode":'6793819',
}
#發起請求
html = requests.post(url, headers=headers,data=data).text
print(html)
Python學好了,飛機票你還買不到麼?

 

有返回結果並且有資料證明我們請求成功了,但是我們還得進一步驗證資料準確性。

3.驗證資料是否準確

使用json進一步提取關鍵資料如航班號,最低價等。

import requests #匯入requests模組
import json #匯入json
#請求連結
url = 'http://flight.elong.com/search/ly/rest/list'
#構造請求頭 介面中請求頭有的引數最好全寫上,之後再瞭解這些請求頭資訊是幹什麼的,這裡不做介紹
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
}
#請求引數
data = {
"p": '{"departCode":"bjs","arriveCityCode":"sha","departDate":"2018-12-24","searchType":"0","classTypes":null,"isBaby":0}',
"grabCode":'9151048',
}
#發起請求
html = requests.post(url, headers=headers,data=data).text
#json.loads轉化json為一個字典 然後我們可以用字典方法取鍵和值
html = json.loads(html)["flightSelections"]
#建立結果列表
list = []
for i in html:#遍歷所有航班
if len(i["Segments"]) == 1: #只提取單程,多程排除
flightnumber = i["Segments"][0]["FlightNumber"]
price = i["Segments"][0]["Price"]
#航班資訊字典
item = {
"flight": flightnumber,
"price": price,
}
list.append(item)
print(list)
Python學好了,飛機票你還買不到麼?

 

和網頁價格對比:

Python學好了,飛機票你還買不到麼?

 

結果正確,證明爬取成功。還沒完,上面2,3過程提到grabCode引數的時效性,引數過期會導致介面無返回結果,json解析就會丟擲異常。

4.js逆向分析加密請求引數grabCode的生成

介面請求引數中的加密引數都是有跡可循的,前端和後端必須使用相同的加密演算法來保證引數的有效性。

後端程式碼我們不可能看得到,所以就要從前端來分析,前端通過js呼叫介面,呼叫介面的寫法有很多種方式,如原生js,ajax,jquery等。

查詢呼叫介面js位置:

通過關鍵字grabCode,來查詢js呼叫介面的位置。(這裡也可以通過其他方法如請求方式Post來搜尋位置)

F12開啟開發者工具,使用全域性搜尋search。

Python學好了,飛機票你還買不到麼?

 

搜尋引數名稱grabCode

Python學好了,飛機票你還買不到麼?

 

找到了,點選第一個搜尋結果,進入檢視js,點選左下角的圖示格式化js。

Python學好了,飛機票你還買不到麼?

 

使用ctrl+f搜尋grabCode的位置

Python學好了,飛機票你還買不到麼?

 

很清晰的可以看到這裡就是使用了ajax呼叫list介面的方法url(介面地址),params(請求引數),methods(請求方式)。

grabCode的值是呼叫了abcdefg函式。(下面我們可以用js斷點除錯來獲取函式abcdefg的位置,或是按照剛才的方法使用全域性搜尋來查詢也可以,除錯更方便一點)

js斷點除錯:

如圖,在grabCode呼叫方法的行標點選,變成藍色,表示斷點成功,然後重新整理頁面。

Python學好了,飛機票你還買不到麼?

 

搜尋結果正在載入被截斷,進一步證實了引數生成就是呼叫函式abcdefg。

Python學好了,飛機票你還買不到麼?

 

這個小圖示的功能叫”逐語句執行“或者叫”逐步執行“,這是我個人理解的一個叫法,意思就是,每點選它一次,js語句就會往後執行一句,它還有一個快捷鍵,F10。

我們點選一下,發現剛才斷點的程式碼已被執行。滑鼠箭頭懸停在abcdefg函式上,點選方法可以直接跳過去。

Python學好了,飛機票你還買不到麼?

 

Python學好了,飛機票你還買不到麼?

 

上圖對abcdefg函式做了一個解析,瞭解生成過程,總結一下就是呼叫網頁原始碼中的id為tsd的元素的屬性值value,替換字串中的某個值,並呼叫eval把字串執行。

取消剛才的斷點,在如圖所示位置打上新斷點,重新整理頁面。F10執行下一句。

Python學好了,飛機票你還買不到麼?

 

和網頁原始碼對比一下,ok,正確。

Python學好了,飛機票你還買不到麼?

 

不難看出上面的value值實際為js程式碼,eval函式會執行這些js程式碼。

模擬引數生成過程:

我們來使用python模擬一下他的過程:1.獲取網頁id==“tsd”的屬性value的值。2.替換字元使用replace("/)^-1/gm", ")&-1")。3.執行js程式碼。

複製value的值,可以去網頁,也可以在js中複製(這裡複製出來的格式會有錯誤,導致js不能執行成功,我們直接去網頁抓取好了)。

Python學好了,飛機票你還買不到麼?

 

import requests,js2py
from lxml import etree
url_list ='http://flight.elong.com/flightsearch/list?departCity=BJS&arriveCity=SHA&departdate=2018-12-24&backdate=&searchType=0'
html_list = requests.get(url_list).text
html_list = etree.HTML(html_list)
js = html_list.xpath('//input[@id="tsd"]/@value')[0]
js = js.replace("/)^-1/gm", ")&-1")
code = js2py.eval_js(js)
print(code)

我們再把這個封裝成一個函式來供第二步進行呼叫,搜尋url中的三字碼和日期可以用一樣的(防止出錯)。

更換搜尋引數城市三字碼或日期測試程式碼是否能正常執行並返回航班及其價格

下面附上全部程式碼,僅供參考學習。

import requests #匯入requests模組
import json #匯入json
import js2py #匯入js執行模組
from lxml import etree #xpath使用lxml的etree解析
def ticket_api(a,b,c):
#請求連結
url = 'http://flight.elong.com/search/ly/rest/list'
#構造請求頭 介面中請求頭有的引數最好全寫上,之後再瞭解這些請求頭資訊是幹什麼的,這裡不做介紹
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36',
}
#請求引數
data = {
"p": '{"departCode":"%s","arriveCityCode":"%s","departDate":"%s","searchType":"0","classTypes":null,"isBaby":0}'%(a,b,c),
"grabCode":grabCode(a,b,c),
}
#發起請求
html = requests.post(url, headers=headers,data=data).text
#json.loads轉化json為一個字典 然後我們可以用字典方法取鍵和值
html = json.loads(html)["flightSelections"]
#建立結果列表
list = []
for i in html:#遍歷所有航班
if len(i["Segments"]) == 1: #只提取單程,多程排除
flightnumber = i["Segments"][0]["FlightNumber"]
price = i["Segments"][0]["Price"]
#航班資訊字典
item = {
"flight": flightnumber,
"price": price,
}
list.append(item)
print(list)
def grabCode(a,b,c):
url_list ='http://flight.elong.com/flightsearch/list?departCity=%s&arriveCity=%s&departdate=%s&backdate=&searchType=0'%(a,b,c)
html_list = requests.get(url_list).text
html_list = etree.HTML(html_list)
js = html_list.xpath('//input[@id="tsd"]/@value')[0]
js = js.replace("/)^-1/gm", ")&-1")
code = js2py.eval_js(js)
return code
if __name__ == '__main__':
a = "bjs"
b = "czx" #常州czx,上海sha
c = "2018-12-24"
ticket_api(a,b,c)
Python學好了,飛機票你還買不到麼?

 

到這一步,基本上算是完成了。