这篇文章的作者(我们今天要翻译的译本)说,她的目标是谈论使用Selenium开发Python网络抓取工具,该工具搜索机票价格。 搜索票证时,使用灵活的日期(相对于指定日期为+-3天)。 Scraper将搜索结果保存在Excel文件中,并向启动它的人发送一封电子邮件,其中包含有关他设法找到的内容的一般信息。 该项目的目的是帮助旅行者找到最优惠的价格。

如果您在处理材料时感到迷失,请阅读本文。
我们在找什么?
您可以随意使用此处描述的系统。 例如,我用它来搜索周末旅行和我的家乡的门票。 如果您真的想寻找有利可图的门票,可以在服务器上运行脚本(一个简单的
服务器 ,每月130卢布,非常适合此操作),并使其每天运行一次或两次。 搜索结果将通过电子邮件发送给您。 此外,我建议您进行所有配置,以便脚本将搜索结果保存到Excel文件中,并将其保存在Dropbox文件夹中,以便您可以随时随地查看此类文件。
我尚未发现错误的关税,但我相信这是可能的如前所述,在搜索时使用“灵活日期”,脚本会查找距给定日期三天内的报价。 尽管启动脚本时,它仅在一个方向上搜索商品,但是很容易对其进行优化,以使其可以在多个飞行方向上收集数据。 有了它的帮助,您甚至可以搜索错误的关税,这样的发现可能非常有趣。
为什么需要另一个刮板机?
老实说,当我第一次开始抓取网页时,它并不是特别有趣。 我想在预测建模,财务分析以及可能的文本情感色彩分析领域中进行更多的项目。 但是事实证明,这很有趣-弄清楚如何创建一个程序来收集网站数据。 当我研究这个主题时,我意识到Web抓取是Internet的“引擎”。
您可能会认为这是一个大胆的声明。 但是,请考虑一下Google如何开始使用Larry Page使用Java和Python创建的Web抓取工具。 Googlebot一直在研究和探索互联网,试图为他们的用户提供最佳的答案。 Web抓取具有无限数量的应用程序,即使您在数据科学领域对其他事情感兴趣,那么为了获取数据进行分析,您也将需要一些抓取技能。
我在最近获得的有关网络抓取的精彩
书籍中找到了这里使用的一些技巧。 在其中,您可以找到许多关于被研究者的实际应用的简单示例和想法。 此外,还有关于reCaptcha测试旁路的非常有趣的一章。 对我来说,这是个新闻,因为我不知道有解决这些问题的专用工具甚至整个服务。
你喜欢旅行吗?
对于本节标题中的一个简单且无害的问题,人们经常会听到一个肯定的答案,其中提供了几个与被问到的人有关的旅行故事。 我们大多数人都会同意,旅行是深入新的文化环境并扩大视野的绝佳途径。 但是,如果您问某人有关他是否想购买机票的问题,我敢肯定,答案远非如此肯定。 实际上,Python来了。
在创建机票信息搜索系统的过程中,我们需要解决的第一个任务是选择一个合适的平台来获取信息。 对于我来说,解决这个问题并不容易,但是最终我选择了Kayak服务。 我尝试了Momondo,Skyscanner,Expedia等服务,但是针对这些资源上的机器人的保护机制是不可渗透的。 经过多次尝试,在此期间,为了说服我自己是人类,我不得不应对交通信号灯,人行横道和自行车,我决定让皮艇最适合我,即使在这里,如果在短时间内加载太多页面,检查也会开始。 我设法使漫游器每隔4到6个小时就将请求发送到该站点,并且一切正常。 使用Kayak时,有时会遇到困难,但是如果您开始受到检查的困扰,则需要手动处理它们,然后启动漫游器,或者等待几个小时,检查应该停止。 如有必要,您可以很好地将代码改编为另一个平台,如果这样做,则可以在注释中报告它。
如果您只是刚开始进行网页抓取,并且不知道为什么有些网站为此感到挣扎,那么在开始该领域的第一个项目之前,请帮个忙,并在Google中搜索单词“网页抓取礼节”。 如果您不合理地进行网页抓取,则实验可能比您想象的要早结束。
开始使用
以下是有关我们的网络抓取工具代码中发生的情况的一般概述:
- 导入所需的库。
- 打开Google Chrome浏览器标签。
- 调用启动机器人的函数,并向其传递城市和日期,这将在搜索票证时使用。
- 此功能接收按照最有吸引力(最佳)的条件排序的第一个搜索结果,然后按按钮加载其他结果。
- 另一个函数从整个页面收集数据并返回一个数据帧。
- 前面的两个步骤是使用按机票价格(便宜)和飞行速度(最快)的分类类型执行的。
- 将向电子邮件用户发送一封电子邮件,其中包含机票价格的简要摘要(最便宜的机票和平均价格),并且具有按上述三个指标排序的信息的数据框被保存为Excel文件。
- 所有上述操作在指定的时间段后循环执行。
应当注意,每个Selenium项目都以Web驱动程序开头。 我使用
Chromedriver ,可与Google Chrome一起使用,但还有其他选择。 PhantomJS和Firefox也很受欢迎。 加载驱动程序后,需要将其放置在适当的文件夹中,以完成其使用的准备工作。 脚本的第一行打开一个新的Chrome标签。
请记住,在我的故事中,我并没有尝试开辟新的视野来寻找机票的有利可图。 有很多更先进的技术可以找到这样的报价。 我只是想为读者提供一种简单而实用的方法来解决此问题。
这是我们上面讨论的代码。
from time import sleep, strftime from random import randint import pandas as pd from selenium import webdriver from selenium.webdriver.common.keys import Keys import smtplib from email.mime.multipart import MIMEMultipart
在代码的开头,您可以看到整个项目中使用的包导入命令。 因此,使用
randint
以便该机器人在开始新的搜索操作之前会“睡着”几秒钟。 通常没有一个机器人就不能没有它。 如果您运行上述代码,则会打开一个Chrome窗口,该漫游器将使用该窗口来处理网站。
让我们做一个小实验,在单独的窗口中打开kayak.com网站。 选择我们要飞往的城市,要到达的城市以及航班日期。 选择日期时,我们将验证范围为+ -3天。 我在编写代码时考虑到了网站根据此类请求生成的内容。 例如,如果您只需要搜索给定日期的票证,那么很有可能必须修改机器人代码。 在讨论代码时,我会做出适当的解释,但是如果您感到困惑,请告诉我。
现在,单击搜索开始按钮,然后查看地址栏中的链接。 它看起来像我在下面的示例中使用的链接,其中声明了存储URL的
kayak
变量,并使用了Web驱动程序的
get
方法。 单击搜索按钮后,结果应显示在页面上。
当我在几分钟内多次使用
get
命令两次至三次时,要求我通过reCaptcha通过测试。 您可以手动进行此检查,然后继续进行实验,直到系统决定安排新的检查为止。 当我测试脚本时,我觉得第一个搜索会话总是没有问题,因此,如果您要尝试使用该代码,则只需定期手动对其进行检查,并使代码在搜索会话之间使用较长的时间间隔即可执行。 是的,如果您考虑一下,一个人不太可能需要在搜索操作之间间隔10分钟才能获得的机票价格信息。
使用XPath处理页面
因此,我们打开了窗口并加载了网站。 要获取价格和其他信息,我们需要使用XPath技术或CSS选择器。 我决定停留在XPath上,并且不觉得需要使用CSS选择器,但是这样做很有可能。 使用XPath在页面上移动可能是一项艰巨的任务,即使使用本文中介绍的方法(该方法使用了从页面代码中复制相应的标识符),我也意识到,这实际上并不是最佳的访问方式必要的元素。 顺便说一下,在本书中,您可以找到有关使用XPath和CSS选择器处理页面的基础知识的出色说明。 这是相应的Web驱动程序方法的外观。
因此,我们将继续致力于该机器人。 利用该程序选择最便宜的车票。 在下图中,XPath选择器代码以红色突出显示。 为了查看代码,您需要右键单击感兴趣的页面元素,然后在出现的菜单中选择“检查”命令。 可以针对不同的页面元素调用此命令,其页面代码将在代码查看窗口中显示并突出显示。
查看页面代码为了确认我对从代码中复制选择器的缺点的推理,请注意以下功能。
这是复制代码时得到的:
//*[@id="wtKI-price_aTab"]/div[1]/div/div/div[1]/div/span/span
为了复制类似内容,您需要右键单击您感兴趣的代码部分,然后从出现的菜单中选择“复制”>“复制XPath”。
这是我用来定义“最便宜”按钮的内容:
cheap_results = '//a[@data-code = "price"]'
复制>复制XPath命令很明显,第二种选择看起来简单得多。 使用它时,它将搜索元素a,该元素的
data-code
属性等于
price
。 使用第一个选项,将搜索一个
id
元素,其为
wtKI-price_aTab
,并且该元素的XPath路径类似于
/div[1]/div/div/div[1]/div/span/span
。 对页面的类似XPath请求将完成此操作,但仅执行一次。 我现在可以说,下次加载页面时
id
会更改。 每次加载页面时,
wtKI
字符
wtKI
都会动态变化,因此,在下一页重新加载后,使用它的代码将无用。 因此,花一些时间弄清楚XPath。 这些知识将很好地为您服务。
但是,应该注意的是,在使用非常简单的站点时,复制XPath选择器可能会派上用场,如果适合您的话,那没有什么错。
现在,让我们考虑一下如果需要在列表内的多行中获取所有搜索结果该怎么办。 很简单 每个结果都在带有
resultWrapper
类的对象内。 下载所有结果的过程可以类似于下面所示的循环进行。
应该注意的是,如果您了解上述内容,那么您应该很容易理解我们将解析的大多数代码。 在编写此代码的过程中,我们使用某种机制来指示路径(XPath),以转到所需的内容(实际上,这是包装结果的元素)。 这样做是为了获取元素的文本并将其放置在可以从中读取数据的对象中(首先使用
flight_containers
,然后使用
flights_list
)。
显示前三行,我们可以清楚地看到我们需要的所有内容。 但是,我们有更多有趣的方式来获取信息。 我们需要分别从每个元素中获取数据。
上班!
编写函数以加载其他结果是最简单的,所以让我们开始吧。 我想最大程度地增加程序接收有关航班的航班数量,同时又不会引起服务中导致验证的怀疑,因此,每次显示该页面时,我都会单击“加载更多结果”按钮。 在这段代码中,您应注意
try
块,由于有时按钮无法正常加载,我添加了
try
块。 如果您也遇到这种情况,请在
start_kayak
函数的代码中
start_kayak
函数的调用,我们将在下面讨论。
现在,在对此功能进行了长时间分析之后(有时我可能会被迷住),我们准备声明一个将处理页面抓取的功能。
我已经收集了称为
page_scrape
的下一个函数中的大部分功能。 有时返回的有关路径各阶段的数据被合并在一起,对于它们的分离,我使用一种简单的方法。 例如,我第一次使用变量
section_a_list
和
section_b_list
。 我们的函数返回数据帧
flights_df
,这使我们可以分离使用不同数据排序方法获得的结果,然后将它们组合起来。
def page_scrape(): """This function takes care of the scraping part""" xp_sections = '//*[@class="section duration"]' sections = driver.find_elements_by_xpath(xp_sections) sections_list = [value.text for value in sections] section_a_list = sections_list[::2]
我试图命名变量,以便代码清晰。 请记住,以
a
开头的变量是路径的第一步,而
b
是第二步。 转到下一个功能。
辅助机制
现在,我们有了一个功能,可让您加载其他搜索结果,并具有处理这些结果的功能。 本文可以在此完成,因为这两个功能提供了可独立打开的抓取页面所需的一切。 但是我们尚未考虑上面讨论的一些辅助机制。 例如,这是用于发送电子邮件和其他内容的代码。 所有这些都可以在我们现在考虑的
start_kayak
函数中找到。
要使用此功能,您需要有关城市和日期的信息。 她利用这些信息在
kayak
变量中形成一个链接,该链接用于转到一个页面,该页面将包含按其最佳匹配排序的搜索结果。 在第一次抓取会话之后,我们将使用页面顶部表中的价格。 即,我们找到最低票价和平均价格。 所有这些以及站点发布的预测将通过电子邮件发送。 在页面上,相应的表格应位于左上角。 顺便说一下,使用此表进行操作可能会在使用确切日期进行搜索时导致错误,因为在这种情况下,该表未显示在页面上。
def start_kayak(city_from, city_to, date_start, date_end): """City codes - it's the IATA codes! Date format - YYYY-MM-DD""" kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to + '/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a') driver.get(kayak) sleep(randint(8,10))
我使用Outlook帐户(hotmail.com)测试了此脚本。 我没有检查它是否可以正确运行Gmail帐户,该邮件系统非常流行,但是有很多可能的选择。 如果您使用Hotmail帐户,则为了使所有功能正常工作,您只需要在代码中输入数据即可。
如果您想了解在此函数代码的不同部分中究竟执行了什么操作,则可以复制它们并进行试验。 代码实验是理解它的唯一方法。
准备系统
现在,我们讨论的所有内容都已完成,我们可以创建一个简单的循环,在其中调用我们的函数。 该脚本要求用户提供有关城市和日期的数据。 在不断重启脚本的情况下进行测试时,您不太希望每次都手动输入此数据,因此可以在测试期间通过注释掉下面的严格注释了脚本所需数据的行来注释掉对应的行。
city_from = input('From which city? ') city_to = input('Where to? ') date_start = input('Search around which departure date? Please use YYYY-MM-DD format only ') date_end = input('Return when? Please use YYYY-MM-DD format only ')
这是脚本的测试运行。
测试运行脚本总结
如果您到了这一步,恭喜! 现在,您已经可以使用网络刮板,尽管我已经发现了许多改进方法。 例如,它可以与Twilio集成在一起,以便发送电子邮件而不是电子邮件。 您可以使用VPN或其他方式同时从多个服务器接收结果。 检查站点用户是否为人也是一个反复出现的问题,但是这个问题也可以解决。 无论如何,现在您有了一个可以扩展的基础。 例如,确保将Excel文件作为电子邮件的附件发送给用户。
