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

我们的工作清单如下:
将Python连接到我们的浏览器并访问该网站(此处的示例中为Expedia.com)。
根据我们的偏好选择机票类型(往返,单程等)。
选择出发国家/地区。
选择抵达国家(包括往返)。
选择出发和返回日期。
以结构化格式编译所有可用航班(针对那些喜欢进行探索性数据分析的人!)。
连接SMTP邮件服务器。
发送当前的最优惠价格。
让我们开始吧!
导入库
让我们继续导入库:
Selenium(用于访问网站和自动化测试):
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import KeysPandas(我们将主要用Pandas来构建数据):
import pandas as pd时间和日期时间(使用延迟和返回当前时间我们将在后面看到原因):import timeimport datetime我们需要那些用于连接邮件服务器并发送邮件:
import smtplibfrom 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 pathsreturn_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次,删除该字段中日期的所有字符。
获取结果
定义将单击搜索按钮的函数。
def search(): 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'连接服务器
def connect_mail(username, password): global server server = smtplib.SMTP('smtp.outlook.com', 587) server.ehlo() server.starttls() server.login(username, password)创建邮件消息#Create message template for emaildef create_msg(): global msg 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等会后,当我们开始运行函数,以保持值为每个特定运行时被定义。
发送消息
def send_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小时。您可以根据自己的喜好调整时间。
for i in range(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表格,并且每小时都会更新当前价格的新列:
在,您可以通过应用许多其他想法来进一步实现这一点,例如:
访问多个网站,并从每个网站发送当前最优惠的价格。
为多个日期范围运行循环并检查哪些日期在哪些网站上提供最优惠的价格。
检查每家航空公司的价格随时间的变化情况。
如果您有其他想法,请不要犹豫,欢迎分享之!
以上内容,希望对您有用。
作者:老夏

本教程演示如何使用Python结合Selenium和Pandas,每小时自动浏览Expedia网站,查找最便宜的航班并发送电子邮件。教程涵盖了连接浏览器、选择航班类型、日期,以及使用SMTP发送邮件的整个过程。
805

被折叠的 条评论
为什么被折叠?



