使用python+selenium爬取同城旅遊網機票資訊
阿新 • • 發佈:2018-12-25
最近使用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)
執行效果截圖: