1. 程式人生 > >使用python+selenium爬取同城旅遊網機票資訊

使用python+selenium爬取同城旅遊網機票資訊

 

最近使用python+selenium爬取了同城旅遊網機票資訊

相關主要程式碼如下,通過模擬人為操作,拿下了這個機票列表的html程式碼,然後就可以使用xpath或者re等方式從中提取需要的欄位資訊了。

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from spider.comm.spider_communal import is_same_month
from selenium.webdriver.chrome.options import Options
import platform
import time
import re
from lxml import etree

'''
使用selenium自動化測試工具爬取同城旅遊網機票資訊
爬取URL:https://www.ly.com
author:liu-yanlin
依賴環境:python3.6.1
pip install selenium=3.13.0
pip install lxml=4.2.1
Chrome驅動下載地址:https://pan.baidu.com/s/1564mrLmlT7vPdLBntm8hlQ 提取碼:fq33
'''
class LySpider():

    '''
    @:param date_str 查詢日期
    @:param start_city 查詢起始城市
    @:param arrive_city 查詢抵達城市
    '''
    def __init__(self,date_str,start_city,arrive_city):
        self.date_str=date_str
        self.start_city=start_city
        self.arrive_city=arrive_city
        # 判斷如果系統是Windows測試效果則彈窗模式,否則Linux部署環境下開啟無頭模式
        sys_str = platform.system()
        if sys_str == "Windows":
            # self.driver = webdriver.Chrome()
            options = Options()
            options.add_argument('--headless')
            self.driver = webdriver.Chrome(chrome_options=options)
        elif sys_str == "Linux":
            options = Options()
            options.add_argument('--headless')
            self.driver = webdriver.Chrome(chrome_options=options)

    '''
    通過selenium控制Chrome驅動,完成模擬人工輸入查詢地址和日期然後點選提交獲取查詢結果html的流程
    '''
    def get_query_results(self):

        # 隱性等待和顯性等待可以同時用,但要注意:等待的最長時間取兩者之中的大者
        self.driver.implicitly_wait(10)
        self.driver.get('https://www.ly.com/FlightQuery.aspx')
        locator = (By.ID, 'txtAirplaneCity1')
        try:
            #顯性等待
            WebDriverWait(self.driver, 20, 0.5).until(EC.presence_of_element_located(locator))
            # 起始地城市input元素獲取並清空值,然後填入城市名稱,輸入之後模擬按回車鍵
            txtAirplaneCity1 = self.driver.find_elements_by_id("txtAirplaneCity1")[0]
            txtAirplaneCity1.clear()
            txtAirplaneCity1.send_keys(self.start_city)
            txtAirplaneCity1.send_keys(Keys.ENTER)
            # 抵達地城市input元素獲取並清空值,然後填入城市名稱,輸入之後模擬按回車鍵
            txtAirplaneCity2 = self.driver.find_elements_by_id("txtAirplaneCity2")[0]
            txtAirplaneCity2.clear()
            txtAirplaneCity2.send_keys(self.arrive_city)
            txtAirplaneCity2.send_keys(Keys.ENTER)

            # 如果所查詢的日期在當月範圍內,則定位到日曆外掛中第1個div否則定位到第2個div,div1 表示當月,div2表示下一個月
            if is_same_month(self.date_str):
                # 定位到日曆外掛
                element_calendar = self.driver.find_elements_by_xpath(
                    "/html/body/div[17]/div/div[1]/div[1]/div/table/tbody/tr/td/span")
                for item in element_calendar:
                    if item.text == str(int(self.date_str.split("-")[2])):
                        item.click()
            else:
                element_calendar = self.driver.find_elements_by_xpath(
                    "/html/body/div[17]/div/div[1]/div[2]/div/table/tbody/tr/td/span")
                for item in element_calendar:
                    if item.text == str(int(self.date_str.split("-")[2])):
                        item.click()
            # 定位搜尋按鈕並模擬點選提交
            airplaneSubmit = self.driver.find_elements_by_id("airplaneSubmit")[0]
            airplaneSubmit.click()
            # 顯性等待後,定位到機票查詢結果div,然後獲取div內的html
            locator_content = (By.ID, 'allFlightListDom_1')
            WebDriverWait(self.driver, 20, 0.5).until(EC.presence_of_element_located(locator_content))
            flight_list_html=self.get_flight_list_dom()
            #返回結果
            data_list=[]
            '''
            此處判斷返回的flight_list_html裡面是否包含有機票資訊,如果有直接返回此html程式碼,否則使用for迴圈
            從新嘗試10次,每迴圈一次暫停一秒(這裡為啥要這樣寫,因為實際情況中可能會存在網路延遲載入慢等原因
            導致獲取不到內容)
            '''
            if flight_list_html:
                for item in flight_list_html:
                    data_list.append(item.get_attribute('innerHTML'))
            else:
                for x in range(10):
                    flight_list_html = self.get_flight_list_dom()
                    if flight_list_html:
                        for item in flight_list_html:
                            data_list.append(item.get_attribute('innerHTML'))
                        break
                    time.sleep(1)
            return data_list

        except Exception as ex:
            print(ex)
        finally:
            self.driver.close()
    '''
    定位到機票查詢結果div,然後獲取div內的html
    '''
    def get_flight_list_dom(self):
        # ---顯性等待後,定位到機票查詢結果div,然後獲取div內的html
        #通過觀察頁面發現這個機票列表資料有三種格式,所以將它們都提取出來拼接成一個List返回
        flight_list_html_n=self.driver.find_elements_by_xpath('//div[@class="clearfix flightList"]//div[@class="flist_box"]')
        flight_list_html_top=self.driver.find_elements_by_xpath('//div[@class="clearfix flightList"]//div[@class="flist_box f_m_top flist_boxat"]')
        flight_list_html_boxbot = self.driver.find_elements_by_xpath('//div[@class="clearfix flightList"]//div[@class="flist_box flist_boxbot"]')
        return flight_list_html_n+flight_list_html_top+flight_list_html_boxbot

    '''
    提取資料
    @:param respone get_query_results()方法中返回的結果內容
    '''
    def extract(self,respone):
        try:
            data_list=[]
            for item in respone:
                data = {}
                html = etree.HTML(item)
                # 航司
                airline = html.xpath('/html/body/table/tbody/tr/td[1]/div[1]/text()')
                data["airline"] = airline[0] if airline else ""
                # 航班號
                flight_number = re.findall("[a-zA-Z]{2}\d+", airline[0])
                data["flight_number"] = flight_number[0] if flight_number else ""
                # 出發時間
                dep_time = html.xpath('/html/body/table/tbody/tr/td[2]/div[1]/text()')
                data["dep_time"] = dep_time[0] if dep_time else ""
                # 出發機場
                dep_airport = html.xpath('/html/body/table/tbody/tr/td[2]/div[2]/text()')
                data["dep_airport"] = dep_airport[0] if dep_airport else ""
                # 飛機型別
                aircraft_type = html.xpath('/html/body/table/tbody/tr/td[1]/div[2]/a/text()')
                data["aircraft_type"] = aircraft_type[0] if aircraft_type else ""
                # 抵達時間
                arr_time = html.xpath('/html/body/table/tbody/tr/td[4]/div[1]/text()')
                data["arr_time"] = arr_time[0] if arr_time else ""
                # 抵達機場
                arr_airport = html.xpath('/html/body/table/tbody/tr/td[4]/div[2]/text()')
                data["arr_airport"] = arr_airport[0] if arr_airport else ""
                # 價格
                price = html.xpath('/html/body/table/tbody/tr/td[8]/div[1]/span[1]/em[1]/text()')
                data["price"] = price[0] if price else ""
                data_list.append(data)
            return data_list
        except Exception as ex:
            print(ex)
            return None
    '''
    儲存資料
    @:param data 要儲存的資料,預設是儲存extract()方法所返回的資料
    '''
    def save(self,data=None):
        try:
            #以下將資料儲存到kafka中
            if data:
                pass
            else:
                results=self.extract(self.get_query_results())
        except Exception as ex:
            pass

if __name__ == "__main__":

    ly_spider=LySpider("2019-01-02","成都","北京")
    res=ly_spider.get_query_results()
    data_list=ly_spider.extract(res)
    for item in data_list:
        print(item)

執行效果截圖: