Python學(xué)好了,飛機(jī)票你還買不到么?

項(xiàng)目:機(jī)票數(shù)據(jù)采集

使用模塊:requests(請(qǐng)求模塊),js2py(js執(zhí)行模塊),json(解析json),xpath(解析網(wǎng)頁)。

小編推薦大家可以加我的扣扣群 735934841 。里面有海量視頻教程和學(xué)習(xí)資料免費(fèi)領(lǐng)取,不失為是一個(gè)學(xué)習(xí)的好地方,歡迎你的到來。一起交流學(xué)習(xí)!共同進(jìn)步?。?/b>

項(xiàng)目流程:

分析網(wǎng)站數(shù)據(jù)來源。

編寫爬蟲腳本。

驗(yàn)證數(shù)據(jù)準(zhǔn)確性。

js逆向破解參數(shù)生成。

更換請(qǐng)求參數(shù)城市(飛機(jī)起飛城市和落地城市或日期)測(cè)試結(jié)果是否正常。

1.分析網(wǎng)站數(shù)據(jù)來源

進(jìn)入藝龍機(jī)票列表搜索頁,附上鏈接?http://flight.elong.com/flightsearch/list?departCity=bjs&arriveCity=sha&departdate=2018-12-2?4,鏈接參數(shù)日期自行更改。

一般情況數(shù)據(jù)為調(diào)用接口獲得,或是在頁面中嵌入,這里很明顯是調(diào)用了接口。

F12打開開發(fā)者工具(谷歌瀏覽器),選擇network中的xhr,然后刷新頁面或重新搜索,查看調(diào)用的接口。(這一步也可以使用抓包工具,推薦使用Fiddler,網(wǎng)上有許多漢化版的,看個(gè)人習(xí)慣吧。)

調(diào)用了四個(gè)接口,點(diǎn)擊接口查看返回結(jié)果,確定數(shù)據(jù)來源。

看到出發(fā)機(jī)場(chǎng),航空公司名稱之類的英文,ok,就是這個(gè)了,點(diǎn)擊進(jìn)入Headers。

數(shù)據(jù)來源已經(jīng)確定,下面我們來構(gòu)造爬蟲請(qǐng)求接口。

2.編寫爬蟲腳本

快速上手requests模塊,鏈接已備好http://docs.python-requests.org/zh_CN/latest/user/quickstart.html

直接上代碼(提示:代碼中的請(qǐng)求參數(shù)grabcode的值需要自己抓取,有時(shí)效性,過期無返回結(jié)果導(dǎo)致代碼報(bào)錯(cuò)):

import requests #導(dǎo)入requests模塊

#請(qǐng)求鏈接

url = 'http://flight.elong.com/search/ly/rest/list'

#構(gòu)造請(qǐng)求頭 接口中請(qǐng)求頭有的參數(shù)最好全寫上,之后再了解這些請(qǐng)求頭信息是干什么的,這里不做介紹

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',

}

#請(qǐng)求參數(shù)

data = {

"p": '{"departCode":"bjs","arriveCityCode":"sha","departDate":"2018-12-24","searchType":"0","classTypes":null,"isBaby":0}',

"grabCode":'6793819',

}

#發(fā)起請(qǐng)求

html = requests.post(url, headers=headers,data=data).text

print(html)

有返回結(jié)果并且有數(shù)據(jù)證明我們請(qǐng)求成功了,但是我們還得進(jìn)一步驗(yàn)證數(shù)據(jù)準(zhǔn)確性。

3.驗(yàn)證數(shù)據(jù)是否準(zhǔn)確

使用json進(jìn)一步提取關(guān)鍵數(shù)據(jù)如航班號(hào),最低價(jià)等。

import requests #導(dǎo)入requests模塊

import json #導(dǎo)入json

#請(qǐng)求鏈接

url = 'http://flight.elong.com/search/ly/rest/list'

#構(gòu)造請(qǐng)求頭 接口中請(qǐng)求頭有的參數(shù)最好全寫上,之后再了解這些請(qǐng)求頭信息是干什么的,這里不做介紹

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',

}

#請(qǐng)求參數(shù)

data = {

"p": '{"departCode":"bjs","arriveCityCode":"sha","departDate":"2018-12-24","searchType":"0","classTypes":null,"isBaby":0}',

"grabCode":'9151048',

}

#發(fā)起請(qǐng)求

html = requests.post(url, headers=headers,data=data).text

#json.loads轉(zhuǎn)化json為一個(gè)字典 然后我們可以用字典方法取鍵和值

html = json.loads(html)["flightSelections"]

#創(chuàng)建結(jié)果列表

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)

和網(wǎng)頁價(jià)格對(duì)比:

結(jié)果正確,證明爬取成功。還沒完,上面2,3過程提到grabCode參數(shù)的時(shí)效性,參數(shù)過期會(huì)導(dǎo)致接口無返回結(jié)果,json解析就會(huì)拋出異常。

4.js逆向分析加密請(qǐng)求參數(shù)grabCode的生成

接口請(qǐng)求參數(shù)中的加密參數(shù)都是有跡可循的,前端和后端必須使用相同的加密算法來保證參數(shù)的有效性。

后端代碼我們不可能看得到,所以就要從前端來分析,前端通過js調(diào)用接口,調(diào)用接口的寫法有很多種方式,如原生js,ajax,jquery等。

查找調(diào)用接口js位置:

通過關(guān)鍵字grabCode,來查找js調(diào)用接口的位置。(這里也可以通過其他方法如請(qǐng)求方式Post來搜索位置)

F12打開開發(fā)者工具,使用全局搜索search。

搜索參數(shù)名稱grabCode

找到了,點(diǎn)擊第一個(gè)搜索結(jié)果,進(jìn)入查看js,點(diǎn)擊左下角的圖標(biāo)格式化js。

使用ctrl+f搜索grabCode的位置

很清晰的可以看到這里就是使用了ajax調(diào)用list接口的方法url(接口地址),params(請(qǐng)求參數(shù)),methods(請(qǐng)求方式)。

grabCode的值是調(diào)用了abcdefg函數(shù)。(下面我們可以用js斷點(diǎn)調(diào)試來獲取函數(shù)abcdefg的位置,或是按照剛才的方法使用全局搜索來查找也可以,調(diào)試更方便一點(diǎn))

js斷點(diǎn)調(diào)試:

如圖,在grabCode調(diào)用方法的行標(biāo)點(diǎn)擊,變成藍(lán)色,表示斷點(diǎn)成功,然后刷新頁面。

搜索結(jié)果正在加載被截?cái)?,進(jìn)一步證實(shí)了參數(shù)生成就是調(diào)用函數(shù)abcdefg。

這個(gè)小圖標(biāo)的功能叫”逐語句執(zhí)行“或者叫”逐步執(zhí)行“,這是我個(gè)人理解的一個(gè)叫法,意思就是,每點(diǎn)擊它一次,js語句就會(huì)往后執(zhí)行一句,它還有一個(gè)快捷鍵,F(xiàn)10。

我們點(diǎn)擊一下,發(fā)現(xiàn)剛才斷點(diǎn)的代碼已被執(zhí)行。鼠標(biāo)箭頭懸停在abcdefg函數(shù)上,點(diǎn)擊方法可以直接跳過去。

上圖對(duì)abcdefg函數(shù)做了一個(gè)解析,了解生成過程,總結(jié)一下就是調(diào)用網(wǎng)頁源代碼中的id為tsd的元素的屬性值value,替換字符串中的某個(gè)值,并調(diào)用eval把字符串執(zhí)行。

取消剛才的斷點(diǎn),在如圖所示位置打上新斷點(diǎn),刷新頁面。F10執(zhí)行下一句。

和網(wǎng)頁源代碼對(duì)比一下,ok,正確。

不難看出上面的value值實(shí)際為js代碼,eval函數(shù)會(huì)執(zhí)行這些js代碼。

模擬參數(shù)生成過程:

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

復(fù)制value的值,可以去網(wǎng)頁,也可以在js中復(fù)制(這里復(fù)制出來的格式會(huì)有錯(cuò)誤,導(dǎo)致js不能執(zhí)行成功,我們直接去網(wǎng)頁抓取好了)。

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)

我們?cè)侔堰@個(gè)封裝成一個(gè)函數(shù)來供第二步進(jìn)行調(diào)用,搜索url中的三字碼和日期可以用一樣的(防止出錯(cuò))。

更換搜索參數(shù)城市三字碼或日期測(cè)試代碼是否能正常運(yùn)行并返回航班及其價(jià)格

下面附上全部代碼,僅供參考學(xué)習(xí)。

import requests #導(dǎo)入requests模塊

import json #導(dǎo)入json

import js2py #導(dǎo)入js執(zhí)行模塊

from lxml import etree #xpath使用lxml的etree解析

def ticket_api(a,b,c):

#請(qǐng)求鏈接

url = 'http://flight.elong.com/search/ly/rest/list'

#構(gòu)造請(qǐng)求頭 接口中請(qǐng)求頭有的參數(shù)最好全寫上,之后再了解這些請(qǐng)求頭信息是干什么的,這里不做介紹

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',

}

#請(qǐng)求參數(shù)

data = {

"p": '{"departCode":"%s","arriveCityCode":"%s","departDate":"%s","searchType":"0","classTypes":null,"isBaby":0}'%(a,b,c),

"grabCode":grabCode(a,b,c),

}

#發(fā)起請(qǐng)求

html = requests.post(url, headers=headers,data=data).text

#json.loads轉(zhuǎn)化json為一個(gè)字典 然后我們可以用字典方法取鍵和值

html = json.loads(html)["flightSelections"]

#創(chuàng)建結(jié)果列表

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)

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容