使用模塊:requests(請求模塊),js2py(js執行模塊),json(解析json),xpath(解析網頁)。html
項目流程:前端
進入藝龍機票列表搜索頁,附上連接http://flight.elong.com/flightsearch/list?departCity=bjs&arriveCity=sha&departdate=2018-12-24,連接參很多天期自行更改。python
通常狀況數據爲調用接口得到,或是在頁面中嵌入,這裏很明顯是調用了接口。jquery
F12打開開發者工具(谷歌瀏覽器),選擇network中的xhr,而後刷新頁面或從新搜索,查看調用的接口。(這一步也可使用抓包工具,推薦使用Fiddler,網上有許多漢化版的,看我的習慣吧。)ajax
調用了四個接口,點擊接口查看返回結果,肯定數據來源。算法
看到出發機場,航空公司名稱之類的英文,ok,就是這個了,點擊進入Headers。json
數據來源已經肯定,下面咱們來構造爬蟲請求接口。後端
快速上手requests模塊,連接已備好 http://docs.python-requests.org/zh_CN/latest/user/quickstart.html
api
直接上代碼(提示:代碼中的請求參數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)
有返回結果而且有數據證實咱們請求成功了,可是咱們還得進一步驗證數據準確性。
使用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)
和網頁價格對比:
結果正確,證實爬取成功。還沒完,上面2,3過程提到grabCode參數的時效性,參數過時會致使接口無返回結果,json解析就會拋出異常。
接口請求參數中的加密參數都是有跡可循的,前端和後端必須使用相同的加密算法來保證參數的有效性。
後端代碼咱們不可能看獲得,因此就要從前端來分析,前端經過js調用接口,最多見的是jquery庫來實現ajax請求。(js原生也能夠實現ajax請求)
經過關鍵字grabCode,來查找js調用接口的位置。(這裏也能夠經過其餘方法如請求方式Post來搜索位置)
F12打開開發者工具,使用全局搜索search。
搜索參數名稱grabCode
找到了,點擊第一個搜索結果,進入查看js,點擊左下角的圖標格式化js。
使用ctrl+f搜索grabCode的位置
很清晰的能夠看到這裏就是使用了ajax調用list接口的方法url(接口地址),params(請求參數),methods(請求方式)。
grabCode的值是調用了abcdefg函數。(下面咱們能夠用js斷點調試來獲取函數abcdefg的位置,或是按照剛纔的方法使用全局搜索來查找也能夠,調試更方便一點)
如圖,在grabCode調用方法的行標點擊,變成藍色,表示斷點成功,而後刷新頁面。
搜索結果正在加載被截斷,進一步證明了參數生成就是調用函數abcdefg。
這個小圖標的功能叫」逐語句執行「或者叫」逐步執行「,這是我我的理解的一個叫法,意思就是,每點擊它一次,js語句就會日後執行一句,它還有一個快捷鍵,F10。
咱們點擊一下,發現剛纔斷點的代碼已被執行。鼠標箭頭懸停在abcdefg函數上,點擊方法能夠直接跳過去。
上圖對abcdefg函數作了一個解析,瞭解生成過程,總結一下就是調用網頁源代碼中的id爲tsd的元素的屬性值value,替換字符串中的某個值,並調用eval把字符串執行。
取消剛纔的斷點,在如圖所示位置打上新斷點,刷新頁面。F10執行下一句。
和網頁源代碼對比一下,ok,正確。
不難看出上面的value值實際爲js代碼,eval函數會執行這些js代碼。
咱們來使用python模擬一下他的過程:1.獲取網頁id==「tsd」的屬性value的值。2.替換字符使用replace("/\)\^-1/gm", ")&-1")。3.執行js代碼。
複製value的值,能夠去網頁,也能夠在js中複製(這裏複製出來的格式會有錯誤,致使js不能執行成功,咱們直接去網頁抓取好了)。
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)
到這一步,基本上算是完成了。
原文出處:https://www.cnblogs.com/lyxdw/p/10168690.html