Python3爬蟲基礎實戰篇之機票數據採集

項目:藝龍國內機票實時數據爬蟲

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

項目流程:前端

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

1.分析網站數據來源

 進入藝龍機票列表搜索頁,附上連接http://flight.elong.com/flightsearch/list?departCity=bjs&arriveCity=sha&departdate=2018-12-24,連接參很多天期自行更改。python

通常狀況數據爲調用接口得到,或是在頁面中嵌入,這裏很明顯是調用了接口。jquery

F12打開開發者工具(谷歌瀏覽器),選擇network中的xhr,而後刷新頁面或從新搜索,查看調用的接口。(這一步也可使用抓包工具,推薦使用Fiddler,網上有許多漢化版的,看我的習慣吧。)ajax

 

調用了四個接口,點擊接口查看返回結果,肯定數據來源。算法

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

 

 數據來源已經肯定,下面咱們來構造爬蟲請求接口。後端

2.編寫爬蟲腳本

快速上手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)

有返回結果而且有數據證實咱們請求成功了,可是咱們還得進一步驗證數據準確性。

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)

和網頁價格對比:

結果正確,證實爬取成功。還沒完,上面2,3過程提到grabCode參數的時效性,參數過時會致使接口無返回結果,json解析就會拋出異常。

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

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

後端代碼咱們不可能看獲得,因此就要從前端來分析,前端經過js調用接口,最多見的是jquery庫來實現ajax請求。(js原生也能夠實現ajax請求)

查找調用接口js位置:

經過關鍵字grabCode,來查找js調用接口的位置。(這裏也能夠經過其餘方法如請求方式Post來搜索位置)

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

 

 搜索參數名稱grabCode

找到了,點擊第一個搜索結果,進入查看js,點擊左下角的圖標格式化js。

使用ctrl+f搜索grabCode的位置

很清晰的能夠看到這裏就是使用了ajax調用list接口的方法url(接口地址),params(請求參數),methods(請求方式)。

grabCode的值是調用了abcdefg函數。(下面咱們能夠用js斷點調試來獲取函數abcdefg的位置,或是按照剛纔的方法使用全局搜索來查找也能夠,調試更方便一點)

js斷點調試:

如圖,在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

相關文章
相關標籤/搜索