Python爬虫教程:查找最廉价的航班!

2019-03-13 23:37:00来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

介绍

在本教程中,我将向大家展示如何使用Python每小时自动浏览Expedia等网站,查找航班并每小时向你发送电子邮件,内容为特定路线的最佳航班价格。

最终结果是类似这封电子邮件:


 

我们的工作清单如下:

这里的,教,程大家一起学习!在我的Python学习 qun:【前面9 8 4 】【中间6 3 2 】【后面5 7 9 】动动你的小指头把数字连起来即可,欢迎加入到学习队伍里,有许多正在提升自己的小伙伴, 大家对学习 Python编程有问题,可以随时问,大家多交流!

将Python连接到我们的浏览器并访问该网站(此处的示例中为Expedia.com)。

根据我们的偏好选择机票类型(往返,单程等)。

选择出发国家/地区。

选择抵达国家(包括往返)。

选择出发和返回日期。

以结构化格式编译所有可用航班(针对那些喜欢进行探索性数据分析的人!)。

连接SMTP邮件服务器。

发送当前的最优惠价格。

让我们开始吧!

导入库

让我们继续导入库:

Selenium(用于访问网站和自动化测试):

from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.common.keys import Keys

Pandas(我们将主要用Pandas来构建数据):

import pandas as pd

时间和日期时间(使用延迟和返回当前时间我们将在后面看到原因):

import time

import datetime

我们需要那些用于连接邮件服务器并发送邮件:

import smtplib

from email.mime.multipart import MIMEMultipart

编码

连接到Web浏览器

browser = webdriver.Chrome(executable_path='/chromedriver')


 

选择线路

接下来,我将快速访问Expedia以查看可供选择的界面与选项。

我单击右键单击+检查线路类型(往返,单向等)以查看与其相关的标签。

正如我们在下面看到的,它有一个带有'id = flight-type-roundtrip-label-hp-flight'的'label'标签。


 

因此,我将使用它们来存储三种不同票证类型的标签和ID,如下所示:

#Setting ticket types paths

return_ticket = "//label[@id='flight-type-roundtrip-label-hp-flight']"

one_way_ticket = "//label[@id='flight-type-one-way-label-hp-flight']"

multi_ticket = "//label[@id='flight-type-multi-dest-label-hp-flight']"

然后定义一个函数来选择线路类型:

def ticket_chooser(ticket):

   try:

       ticket_type = browser.find_element_by_xpath(ticket)

       ticket_type.click()

   except Exception as e:

       pass

选择出发和到达国家

下面我定义一个选择出发国家的函数。

def dep_country_chooser(dep_country):

   fly_from = browser.find_element_by_xpath("//input[@id='flight-origin-hp-flight']")

   time.sleep(1)

   fly_from.clear()

   time.sleep(1.5)

   fly_from.send_keys('  ' + dep_country)

   time.sleep(1.5)

   first_item = browser.find_element_by_xpath("//a[@id='aria-option-0']")

   time.sleep(1.5)

first_item.click()

我们遵循以下逻辑:

使用其标记和属性查找元素。

清除国家/地区字段中写入的任何值。

使用输入我想要的国家(将传递给函数).sendkeys。

从下拉菜单中选择出现的第一个选项(也可以使用其标签和ID,当出现下拉菜单时,右键单击+检查可以找到该标签和ID)。

单击第一个选项。

请注意,time.sleep 在步骤之间使用  ,以使页面的元素有机会在步骤之间更新/加载。没有time.sleep,  有时我们的脚本比页面加载更快,因此尝试访问未加载的元素,导致我们的代码中断执行。

让我们为抵达国家做同样的事情,代码如下:

def arrival_country_chooser(arrival_country):

   fly_to = browser.find_element_by_xpath("//input[@id='flight-destination-hp-flight']")

   time.sleep(1)

   fly_to.clear()

   time.sleep(1.5)

   fly_to.send_keys('  ' + arrival_country)

   time.sleep(1.5)

   first_item = browser.find_element_by_xpath("//a[@id='aria-option-0']")

   time.sleep(1.5)

   first_item.click()

选择出发和返回日期

出发日期:

def dep_date_chooser(month, day, year):

   dep_date_button = browser.find_element_by_xpath("//input[@id='flight-departing-hp-flight']")

   dep_date_button.clear()

   dep_date_button.send_keys(month + '/' + day + '/' + year)

非常直截了当:

像以前一样在网页上找到元素。

清除之前写的任何内容。

使用在函数中输入的月,日和年填充元素作为参数+日期格式的斜杠。

返回日期:

def return_date_chooser(month, day, year):

   return_date_button = browser.find_element_by_xpath("//input[@id='flight-returning-hp-flight']")

   for i in range(11):

       return_date_button.send_keys(Keys.BACKSPACE)

   return_date_button.send_keys(month + '/' + day + '/' + year)

对于返回日期,清除所写的内容由于某种原因不起作用(可能是由于页面将其作为自动填充而不允许我覆盖它.clear())

我解决这个问题的方法是使用Keys.BACKSPACE 它简单地告诉Python单击退格键(删除日期字段中写入的内容)。我把它放在一个for循环中,单击退格键11次,删除该字段中日期的所有字符。

获取结果

定义将单击搜索按钮的函数。

defsearch():

search = browser.find_element_by_xpath("//button[@class='btn-primary btn-action gcw-submit']")

   search.click()

time.sleep(15)

print('Results ready!')

这里最好使用15秒左右的时长延迟,以确保在我们继续下一步之前加载所有结果。

生成的网页如下(我感兴趣的字段标记):


 

编译数据

我们将使用此序列来编译数据:

首先,创建一个Pandas DataFrame来保存我们的数据。

为要存储在列表中的所有航班属性(在上图中突出显示)创建变量。

查找属性的所有元素(例如,所有出发时间)。

将它们存储在我们创建的相关变量中作为List。

将所有这些列表并排放在DataFrame中的列中。

将DataFrame保存到Excel工作表(以备以后对数据进行分析)。

以下是代码:

df = pd.DataFrame()

def compile_data():

   global df

   global dep_times_list

   global arr_times_list

   global airlines_list

   global price_list

   global durations_list

   global stops_list

   global layovers_list

   #departure times

   dep_times = browser.find_elements_by_xpath("//span[@data-test-id='departure-time']")

   dep_times_list = [value.text for value in dep_times]

   #arrival times

   arr_times = browser.find_elements_by_xpath("//span[@data-test-id='arrival-time']")

   arr_times_list = [value.text for value in arr_times]

   #airline name

   airlines = browser.find_elements_by_xpath("//span[@data-test-id='airline-name']")

   airlines_list = [value.text for value in airlines]

   #prices

   prices = browser.find_elements_by_xpath("//span[@data-test-id='listing-price-dollars']")

   price_list = [value.text.split('

)[1] for value in prices]

   #durations

   durations = browser.find_elements_by_xpath("//span[@data-test-id='duration']")

   durations_list = [value.text for value in durations]

   #stops

   stops = browser.find_elements_by_xpath("//span[@class='number-stops']")

   stops_list = [value.text for value in stops]

   #layovers

   layovers = browser.find_elements_by_xpath("//span[@data-test-id='layover-airport-stops']")

   layovers_list = [value.text for value in layovers]

   now = datetime.datetime.now()

   current_date = (str(now.year) + '-' + str(now.month) + '-' + str(now.day))

   current_time = (str(now.hour) + ':' + str(now.minute))

   current_price = 'price' + '(' + current_date + '---' + current_time + ')'

   for i in range(len(dep_times_list)):

       try:

           df.loc[i, 'departure_time'] = dep_times_list[i]

       except Exception as e:

           pass

       try:

           df.loc[i, 'arrival_time'] = arr_times_list[i]

       except Exception as e:

           pass

       try:

           df.loc[i, 'airline'] = airlines_list[i]

       except Exception as e:

           pass

       try:

           df.loc[i, 'duration'] = durations_list[i]

       except Exception as e:

           pass

       try:

           df.loc[i, 'stops'] = stops_list[i]

       except Exception as e:

           pass

       try:

           df.loc[i, 'layovers'] = layovers_list[i]

       except Exception as e:

           pass

       try:

           df.loc[i, str(current_price)] = price_list[i]

       except Exception as e:

           pass

   print('Excel Sheet Created!')

值得一提的是,对于价格列,我每次使用此代码片段运行代码时都会重新命名:

now = datetime.datetime.now()

current_date = (str(now.year) + '-' + str(now.month) + '-' + str(now.day))

current_time = (str(now.hour) + ':' + str(now.minute))

current_price = 'price' + '(' + current_date + '---' + current_time + ')'

这是因为我希望列的标题说明该特定运行的时间,以便能够在以后查看价格如何随时间变化,以备以后我想这样做。

设置我们的电子邮件

在这部分中,我将设置三个功能:

一个连接到邮件服务器。

一个来创建消息。

最后实际发送它。

首先,我还需要将电子邮件登录凭据存储在两个变量中,如下所示:

#email凭证

用户名 = 'myemail@hotmail.com'

密码 = 'XXXXXXXXXXX'

连接服务器

defconnect_mail(username, password):

globalserver

server = smtplib.SMTP('smtp.outlook.com',587)

   server.ehlo()

   server.starttls()

   server.login(username, password)

创建邮件消息

#Create message template for email

defcreate_msg():

globalmsg

msg ='\nCurrent Cheapest flight:\n\nDeparture time: {}\nArrival time: {}\nAirline: {}\nFlight duration: {}\nNo. of stops: {}\nPrice: {}\n'.format(cheapest_dep_time,

                      cheapest_arrival_time,

                      cheapest_airline,

                      cheapest_duration,

                      cheapest_stops,

                      cheapest_price)

在这里,我使用占位符“{}”创建消息,以便在每次运行期间传递实际值。

此外,像这里使用的变量  cheapest_arrival_time,  cheapest_airline等会后,当我们开始运行函数,以保持值为每个特定运行时被定义。

发送消息

defsend_email(msg):

   global message

   message = MIMEMultipart()

message['Subject'] ='Current Best flight'

message['From'] ='myemail@hotmail.com'

message['to'] ='myotheremail@hotmail.com'

server.sendmail('myemail@hotmail.com','myotheremail@hotmail.com', msg)

运行我们的代码!

现在将要运行我们的函数。使用以下逻辑:

数据抓取部分:

访问Expedia链接睡眠5秒钟以加载完成页面。

选择“仅限航班”,因为我目前不对其他优惠感兴趣,如航班和酒店。

运行我们的票选择器功能以获得回程票。

运行我们的离境国家选择器(比如开罗)。

运行我们的到达国家选择器(比如我们去纽约)。

调整我们的出发日期选择器(例如,最好在您的月份或日期之前将零做为前置,如1月份为01,这是Expedia使用的格式)。

运行我们的返回日期选择器。

运行我们的搜索和编译功能。

电子邮件部分:

访问我们的DataFrame的第一行,因为通常第一个航班是Expedia上最便宜和最好的一个,但如果我们想要更深入,可以按最低价格过滤并获得该行。

在分配我们选择到变量行的每一列中的值在电子邮件消息中使用类似(cheapest_dep_time,  cheapest_arrival_time,  等)

运行电子邮件函数创建消息,连接并发送电子邮件。

最后,我们将DataFrame保存到Excel工作表并休眠3600秒(1小时)。

此循环将以一小时为间隔运行8次,因此将运行8小时。您可以根据自己的喜好调整时间。

foriinrange(8):

link ='https://www.expedia.com/'

   browser.get(link)

time.sleep(5)

#choose flights only

flights_only = browser.find_element_by_xpath("//button[@id='tab-flight-tab-hp']")

   flights_only.click()

   ticket_chooser(return_ticket)

dep_country_chooser('Cairo')

arrival_country_chooser('New york')

dep_date_chooser('04','01','2019')

return_date_chooser('05','02','2019')

   search()

   compile_data()

#save values for email

current_values = df.iloc[0]

cheapest_dep_time = current_values[0]

cheapest_arrival_time = current_values[1]

cheapest_airline = current_values[2]

cheapest_duration = current_values[3]

cheapest_stops = current_values[4]

cheapest_price = current_values[-1]

print('run {} completed!'.format(i))

   create_msg()

   connect_mail(username,password)

   send_email(msg)

print('Email sent!')

df.to_excel('flights.xlsx')

time.sleep(3600)

现在,我将在接下来的8小时内每小时收到一封电子邮件:


 

我还有这个包含所有航班的精美Excel表格,并且每小时都会更新当前价格的新列:

在,您可以通过应用许多其他想法来进一步实现这一点,例如:

访问多个网站,并从每个网站发送当前最优惠的价格。

为多个日期范围运行循环并检查哪些日期在哪些网站上提供最优惠的价格。

检查每家航空公司的价格随时间的变化情况。

如果您有其他想法,请不要犹豫,欢迎分享之!


原文链接:https://www.cnblogs.com/qingdeng6359/p/10525145.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:前后端分离djangorestframework—— 接入微信模板消息推送

下一篇:学Python还是Java,8张漫画带你全面分析