O autor do artigo cuja tradução publicamos hoje afirma que seu objetivo é discutir o desenvolvimento de um web scraper em Python usando Selenium, que realiza buscas de preços de passagens aéreas. Durante a pesquisa, são utilizadas datas flexíveis (+- 3 dias em relação às datas especificadas). O scraper salva os resultados da pesquisa em um arquivo Excel e envia um e-mail para quem o executou com um resumo do que foi encontrado. O objetivo deste projeto é ajudar os viajantes a encontrar as melhores ofertas.
Se ao trabalhar com o material, você sentir que se perdeu, dê uma olhada em artigo.
O que vamos procurar?
Você está livre para usar o sistema descrito aqui da maneira que preferir. Eu, por exemplo, utilizei-o para encontrar pacotes de fim de semana e passagens para minha cidade natal. Se você está seriamente determinado a encontrar passagens vantajosas, pode executar o script em um servidor (um simples , por 130 rublos por mês, serve muito bem para isso) e programá-lo para rodar uma ou duas vezes ao dia. Os resultados da pesquisa serão enviados para o seu e-mail. Além disso, recomendo configurar tudo para que o script salve um arquivo Excel com os resultados da pesquisa em uma pasta do Dropbox, o que permitirá que você visualize esses arquivos de qualquer lugar e a qualquer momento.

Ainda não encontrei tarifas com erros, mas acredito que isso seja possível.
Na pesquisa, como já mencionado, é utilizada a "data flexível", o script encontra ofertas que estão dentro de três dias das datas especificadas. Embora ao executar o script a busca seja feita apenas em uma direção, não é difícil adaptá-lo para coletar dados sobre vários destinos de voos. É até possível procurar tarifas erradas, esses achados podem ser bem interessantes.
Para que serve mais um web scraper?
Quando comecei a trabalhar com web scraping, para ser honesto, não achei particularmente interessante. Eu queria fazer mais projetos na área de modelagem preditiva, análise financeira e, possivelmente, na área de análise de sentimentos nos textos. Mas descobri que é muito interessante entender como criar um programa que coleta dados de sites. À medida que me aprofundei no assunto, percebi que o web scraping é o "motor" da internet.
Talvez você ache que isso seja uma afirmação ousada. Mas pense que a empresa Google começou com um web scraper que Larry Page criou usando Java e Python. Os robôs do Google exploraram e ainda exploram a internet, tentando fornecer as melhores respostas aos seus usuários. O web scraping tem infinitas possibilidades de aplicação, e mesmo que você, na área de Data Science, esteja interessado em outra coisa, você precisará de algumas habilidades de scraping para obter dados para análise.
Algumas das técnicas usadas aqui eu encontrei em um extraordinário livro sobre web scraping que adquiri recentemente. Nele, você pode encontrar muitos exemplos simples e ideias sobre aplicações práticas do que foi aprendido. Além disso, há um capítulo muito interessante sobre como contornar verificações de reCaptcha. Para mim, isso foi uma novidade, pois eu não sabia que existiam ferramentas especiais e até serviços inteiros para resolver problemas semelhantes.
Você gosta de viajar?!
A uma pergunta simples e bastante inofensiva, que é o título desta seção, muitas vezes pode-se ouvir uma resposta positiva, acompanhada de algumas histórias de viagens da pessoa a quem foi feita. A maioria de nós concorda que viajar é uma maneira maravilhosa de se imergir em novas culturas e expandir nossos horizontes. No entanto, se perguntar a alguém se gosta de procurar passagens aéreas, estou certo de que a resposta não será tão positiva. Na verdade, é aí que o Python pode nos ajudar.
A primeira tarefa que precisamos resolver ao criar um sistema de busca de informações sobre passagens aéreas é escolher a plataforma adequada de onde retiraremos as informações. Resolver essa questão não foi fácil para mim, mas, no final, escolhi o serviço Kayak. Tentei os serviços Momondo, Skyscanner, Expedia e outros, mas os mecanismos de proteção contra robôs nesses sites eram intransponíveis. Após várias tentativas, nas quais, tentando convencer os sistemas de que sou humano, enfrentei semáforos, faixas de pedestres e bicicletas, decidi que o Kayak era o mais adequado para mim, mesmo sabendo que aqui, se sobrecarregar o site com muitas páginas em um curto período, também começam as verificações. Consegui que o bot enviasse os pedidos ao site em intervalos de 4 a 6 horas, e tudo funcionou bem. Ocasionais dificuldades surgem ao trabalhar com o Kayak, mas se você for incomodado por verificações, precisa ou resolver isso manualmente e depois iniciar o bot, ou esperar algumas horas, e as verificações devem parar. Você pode, se necessário, adaptar o código para outra plataforma e, se fizer isso, sinta-se à vontade para informar nos comentários.
Se você está apenas começando sua jornada no web scraping e não sabe por que alguns sites lutam tanto contra isso, antes de começar seu primeiro projeto nessa área — faça um favor a si mesmo e pesquise no Google materiais sobre "etiqueta de web scraping". Seus experimentos podem acabar mais rápido do que você pensa, caso você conduza o web scraping de maneira imprudente.
Início
Aqui está uma visão geral do que acontecerá no código do nosso web scraper:
- Importação das bibliotecas necessárias.
- Abertura de uma aba no Google Chrome.
- Chamada da função que inicia o bot, passando as cidades e datas que serão usadas na busca de passagens.
- Essa função obtém os primeiros resultados da busca, classificados pelo critério de maior atratividade (best), e clica no botão para carregar resultados adicionais.
- Outra função coleta dados de toda a página e retorna um dataframe.
- Os dois passos anteriores são realizados utilizando tipos de classificação por preço das passagens (cheap) e pela duração do voo (fastest).
- O usuário do script recebe um e-mail contendo um resumo sobre os preços dos bilhetes (os bilhetes mais baratos e o preço médio), enquanto os dados com as informações, classificados pelos três indicadores mencionados, são salvos em um arquivo Excel.
- Todas as ações descritas acima são executadas em um ciclo com um intervalo de tempo definido.
É importante notar que cada projeto Selenium começa com o WebDriver. Eu uso , trabalho com o Google Chrome, mas existem outras opções. PhantomJS e Firefox também são bastante populares. Após o download do driver, ele precisa ser colocado na pasta correspondente, e assim a preparação para o seu uso é concluída. Nas primeiras linhas do nosso script, uma nova aba do Chrome é aberta.
Considere que, em meu relato, não estou tentando abrir novos horizontes na busca de ofertas vantajosas para passagens aéreas. Há técnicas muito mais avançadas para encontrar tais ofertas. Eu apenas gostaria de sugerir aos leitores deste material um método simples, mas aplicável na prática, para resolver essa tarefa.
Aqui está o código do qual falamos anteriormente.
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
# Use aqui seu caminho para o chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'
driver = webdriver.Chrome(executable_path=chromedriver_path) # Este comando abre uma janela do Chrome
sleep(2) No início do código, podem ser vistos os comandos de importação dos pacotes que são usados em todo o nosso projeto. Assim, randint é usado para que o bot "durma" por um número aleatório de segundos antes de iniciar uma nova operação de busca. Normalmente, ninguém consegue operar um bot sem isso. Se o código acima for executado, uma janela do Chrome será aberta, que o bot usará para interagir com os sites.
Vamos realizar um pequeno experimento e abrir o site kayak.com em uma janela separada. Vamos escolher a cidade de onde estamos planejando voar e a cidade para onde queremos ir, além das datas dos voos. Ao escolher as datas, vamos assegurar que seja utilizado um intervalo de +-3 dias. Eu escrevi um código considerando o que o site retorna em resposta a esse tipo de solicitação. Se, por exemplo, você precisar buscar passagens apenas em datas específicas, é muito provável que você tenha que modificar o código do bot. Ao falar sobre o código, eu faço as devidas explicações, mas se você sentir que está confuso, me avise.
Agora, clique no botão de iniciar a busca e olhe para o link na barra de endereço. Ele deve ser parecido com o link que eu uso no exemplo abaixo, onde a variável é declarada kayak, que armazena a URL, e utiliza o método get do driver web. Após clicar no botão de busca, os resultados devem aparecer na página.
![]()
Quando utilizei o comando get mais de duas ou três vezes em alguns minutos, fui solicitado a passar pela verificação com reCaptcha. Essa verificação pode ser feita manualmente e continuar os experimentos até que o sistema decida aplicar uma nova verificação. Quando testei o script, tive a impressão de que a primeira sessão de busca sempre ocorria sem problemas, então, se você quiser experimentar com o código, terá apenas que passar manualmente pela verificação de tempos em tempos e deixar o código rodando, usando intervalos longos entre as sessões de busca. E, se pararmos para pensar, uma pessoa provavelmente não precisará de informações sobre os preços das passagens obtidas com intervalos de 10 minutos entre as buscas.
Trabalhando com a página usando XPath
Então, abrimos a janela e carregamos o site. Para obter informações sobre preços e outros dados, precisamos usar a tecnologia XPath ou seletores CSS. Eu decidi ficar com XPath e não senti a necessidade de aplicar seletores CSS, mas é possível trabalhar assim também. Navegar pela página usando XPath pode ser uma tarefa difícil, e mesmo que você utilize os métodos que descrevi em no artigo, onde utilizei a cópia dos identificadores correspondentes do código da página, percebi que esta realmente não é a maneira ideal de acessar os elementos necessários. Aliás, em o livro é possível encontrar uma excelente descrição dos fundamentos do trabalho com páginas usando XPath e seletores CSS. Veja como é o método correspondente do webdriver.
![]()
Assim, continuamos o trabalho no bot. Vamos aproveitar as capacidades do programa para escolher os ingressos mais baratos. Na próxima imagem, o código do seletor XPath está destacado em vermelho. Para visualizar o código, clique com o botão direito do mouse no elemento da página que lhe interessa e selecione a opção Inspecionar (Inspect) no menu que aparece. Este comando pode ser chamado para diferentes elementos da página, e o código correspondente será exibido e destacado na janela de visualização do código.

Visualizar código da página
Para encontrar confirmação dos meus argumentos sobre as desvantagens da cópia de seletores do código, observe as seguintes características.
Veja o que acontece ao copiar o código:
//*[@id="wtKI-price_aTab"]/div[1]/div/div/div[1]/div/span/spanPara copiar algo semelhante, você precisa clicar com o botão direito do mouse na parte do código que lhe interessa e escolher no menu que aparece a opção Copiar > Copiar XPath.
Veja o que usei para definir o botão Cheapest:
cheap_results = ‘//a[@data-code = "price"]’ 
Comando Copiar > Copiar XPath
É absolutamente claro que a segunda opção parece muito mais simples. Ao usá-la, estamos buscando um elemento a que tem o atributo data-code, igual a price.Ao usar a primeira opção, estamos buscando um elemento id cujo valor é wtKI-price_aTab,sendo que o caminho XPath para o elemento é como /div[1]/div/div/div[1]/div/span/span. Esta consulta XPath à página fará seu trabalho, mas — apenas uma vez. Posso afirmar agora que id mudará na próxima carga da página. A sequência de caracteres wtKI muda dinamicamente a cada carregamento da página, resultando que o código no qual é utilizado se tornará inútil após o próximo recarregamento da página. Portanto, dedique um tempo para entender XPath. Este conhecimento será útil para você.
No entanto, é importante notar que a cópia de seletores XPath pode ser útil ao trabalhar com sites bastante simples, e se isso for aceitável para você, não há nada de errado nisso.
Agora vamos pensar sobre como proceder se você precisar obter todos os resultados de pesquisa em várias linhas, dentro de uma lista. É bem simples. Cada resultado está dentro de um objeto com a classe resultWrapper. O carregamento de todos os resultados pode ser feito em um loop, semelhante ao que será mostrado abaixo.
É importante notar que se você entendeu o que foi dito acima, você deve compreender a maior parte do código que estaremos analisando. Durante a execução desse código, para o que precisamos (na verdade — é o elemento que contém o resultado), nos referimos a ele usando um mecanismo para indicar o caminho (XPath). Isso é feito para obter o texto do elemento e colocá-lo em um objeto do qual podemos ler os dados (primeiro é usado flight_containers, depois — flights_list).

Os três primeiros resultados são exibidos e podemos ver claramente tudo o que precisamos. No entanto, temos maneiras mais interessantes de obter informações. Precisamos coletar dados de cada elemento individualmente.
Vamos ao trabalho!
É mais fácil escrever uma função para carregar resultados adicionais, então vamos começar por ela. Eu gostaria de maximizar o número de voos cujas informações o programa coleta, e ao mesmo tempo, não levantar suspeitas no serviço que poderiam levar a uma verificação, por isso eu clico uma vez no botão Carregar mais resultados toda vez que a página é exibida. Neste código, vale a pena prestar atenção no bloco try, que adicionei porque às vezes o botão não carrega corretamente. Se você também se deparar com isso — comente as chamadas dessa função no código da função start_kayak, que vamos analisar a seguir.
# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
def load_more():
try:
more_results = '//a[@class = "moreButton"]'
driver.find_element_by_xpath(more_results).click()
# Вывод этих заметок в ходе работы программы помогает мне быстро выяснить то, чем она занята
print('sleeping.....')
sleep(randint(45,60))
except:
passAgora, após uma longa análise dessa função (às vezes posso me empolgar), estamos prontos para declarar a função que irá lidar com o scraping da página.
Já reuni a maior parte do que preciso na próxima função, chamada page_scrape. Às vezes, os dados retornados sobre as etapas do caminho acabam se unindo, para separá-los, utilizo um método simples. Por exemplo, quando uso as variáveis section_a_list e section_b_list. Nossa função retorna um dataframe flights_df, que nos permite separar os resultados obtidos através de diferentes métodos de ordenação de dados, e depois — uni-los.
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] # так мы разделяем информацию о двух полётах
section_b_list = sections_list[1::2]
# Если вы наткнулись на reCaptcha, вам может понадобиться что-то предпринять.
# О том, что что-то пошло не так, вы узнаете исходя из того, что вышеприведённые списки пусты
# это выражение if позволяет завершить работу программы или сделать ещё что-нибудь
# тут можно приостановить работу, что позволит вам пройти проверку и продолжить скрапинг
# я использую тут SystemExit так как хочу протестировать всё с самого начала
if section_a_list == []:
raise SystemExit
# Я буду использовать букву A для уходящих рейсов и B для прибывающих
a_duration = []
a_section_names = []
for n in section_a_list:
# Получаем время
a_section_names.append(''.join(n.split()[2:5]))
a_duration.append(''.join(n.split()[0:2]))
b_duration = []
b_section_names = []
for n in section_b_list:
# Получаем время
b_section_names.append(''.join(n.split()[2:5]))
b_duration.append(''.join(n.split()[0:2]))
xp_dates = '//div[@class="section date"]'
dates = driver.find_elements_by_xpath(xp_dates)
dates_list = [value.text for value in dates]
a_date_list = dates_list[::2]
b_date_list = dates_list[1::2]
# Получаем день недели
a_day = [value.split()[0] for value in a_date_list]
a_weekday = [value.split()[1] for value in a_date_list]
b_day = [value.split()[0] for value in b_date_list]
b_weekday = [value.split()[1] for value in b_date_list]
# Получаем цены
xp_prices = '//a[@class="booking-link"]/span[@class="price option-text"]'
prices = driver.find_elements_by_xpath(xp_prices)
prices_list = [price.text.replace('$','') for price in prices if price.text != '']
prices_list = list(map(int, prices_list))
# stops - это большой список, в котором первый фрагмент пути находится по чётному индексу, а второй - по нечётному
xp_stops = '//div[@class="section stops"]/div[1]'
stops = driver.find_elements_by_xpath(xp_stops)
stops_list = [stop.text[0].replace('n','0') for stop in stops]
a_stop_list = stops_list[::2]
b_stop_list = stops_list[1::2]
xp_stops_cities = '//div[@class="section stops"]/div[2]'
stops_cities = driver.find_elements_by_xpath(xp_stops_cities)
stops_cities_list = [stop.text for stop in stops_cities]
a_stop_name_list = stops_cities_list[::2]
b_stop_name_list = stops_cities_list[1::2]
# сведения о компании-перевозчике, время отправления и прибытия для обоих рейсов
xp_schedule = '//div[@class="section times"]'
schedules = driver.find_elements_by_xpath(xp_schedule)
hours_list = []
carrier_list = []
for schedule in schedules:
hours_list.append(schedule.text.split('n')[0])
carrier_list.append(schedule.text.split('n')[1])
# разделяем сведения о времени и о перевозчиках между рейсами a и b
a_hours = hours_list[::2]
a_carrier = carrier_list[1::2]
b_hours = hours_list[::2]
b_carrier = carrier_list[1::2]
cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',
'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',
'Price'])
flights_df = pd.DataFrame({'Out Day': a_day,
'Out Weekday': a_weekday,
'Out Duration': a_duration,
'Out Cities': a_section_names,
'Return Day': b_day,
'Return Weekday': b_weekday,
'Return Duration': b_duration,
'Return Cities': b_section_names,
'Out Stops': a_stop_list,
'Out Stop Cities': a_stop_name_list,
'Return Stops': b_stop_list,
'Return Stop Cities': b_stop_name_list,
'Out Time': a_hours,
'Out Airline': a_carrier,
'Return Time': b_hours,
'Return Airline': b_carrier,
'Price': prices_list})[cols]
flights_df['timestamp'] = strftime("%Y%m%d-%H%M") # время сбора данных
return flights_df Eu procurei nomear as variáveis de forma que o código fosse entendido. Lembre-se de que as variáveis que começam com a pertencem à primeira etapa do caminho, e b — à segunda. Vamos passar para a próxima função.
Mecanismos auxiliares
Agora temos uma função que permite carregar resultados de busca adicionais e outra para processar esses resultados. Este artigo poderia terminar aqui, pois essas duas funções oferecem tudo o que é necessário para o scraping de páginas que podem ser abertas por conta própria. Mas ainda não abordamos alguns mecanismos auxiliares que mencionamos anteriormente. Por exemplo, o código para enviar e-mails e mais algumas coisas. Tudo isso pode ser encontrado na função start_kayak, que agora vamos analisar.
Para que essa função funcione, são necessárias informações sobre cidades e datas. Ela, utilizando essas informações, forma um link na variável kayak, que é usada para ir à página onde estarão os resultados da busca, ordenados por sua melhor correspondência com a consulta. Após a primeira sessão de scraping, trabalharemos com os preços na tabela na parte superior da página. Ou seja, encontraremos o menor preço do bilhete e o preço médio. Tudo isso, juntamente com a previsão fornecida pelo site, será enviado por e-mail. Na página, a tabela correspondente deve estar no canto superior esquerdo. Trabalhar com essa tabela, aliás, pode causar um erro na busca utilizando datas exatas, já que nesses casos a tabela na página não é exibida.
def start_kayak(cidade_de, cidade_para, data_inicio, data_fim):
"""Códigos das cidades - são os códigos IATA!
Formato da data - AAAA-MM-DD"""
kayak = ('https://www.kayak.com/flights/' + cidade_de + '-' + cidade_para +
'/' + data_inicio + '-flexible/' + data_fim + '-flexible?sort=bestflight_a')
driver.get(kayak)
sleep(randint(8,10))
# Às vezes aparece um popup, para verificar isso e fechá-lo podemos usar o bloco try
try:
xp_popup_close = '//button[contains(@id,"dialog-close") and contains(@class,"Button-No-Standard-Style close ")]'
driver.find_elements_by_xpath(xp_popup_close)[5].click()
except Exception as e:
pass
sleep(randint(60,95))
print('carregando mais.....')
# load_more()
print('iniciando o primeiro raspado.....')
df_voos_melhores = page_scrape()
df_voos_melhores['sort'] = 'melhor'
sleep(randint(60,80))
# Vamos pegar o preço mais baixo da tabela, localizada na parte superior da página
matrix = driver.find_elements_by_xpath('/*[contains(@id,"FlexMatrixCell")]')
matrix_precos = [preco.text.replace('$','') for preco in matrix]
matrix_precos = list(map(int, matrix_precos))
matrix_min = min(matrix_precos)
matrix_avg = sum(matrix_precos)/len(matrix_precos)
print('trocando para os resultados mais baratos.....')
resultados_baratos = '//a[@data-code = "price"]'
driver.find_element_by_xpath(resultados_baratos).click()
sleep(randint(60,90))
print('carregando mais.....')
# load_more()
print('iniciando o segundo raspado.....')
df_voos_baratos = page_scrape()
df_voos_baratos['sort'] = 'barato'
sleep(randint(60,80))
print('trocando para os resultados mais rápidos.....')
resultados_rapidos = '//a[@data-code = "duration"]'
driver.find_element_by_xpath(resultados_rapidos).click()
sleep(randint(60,90))
print('carregando mais.....')
# load_more()
print('iniciando o terceiro raspado.....')
df_voos_rapidos = page_scrape()
df_voos_rapidos['sort'] = 'rapido'
sleep(randint(60,80))
# Salvando o novo quadro em arquivo Excel, cujo nome reflete as cidades e datas
final_df = df_voos_baratos.append(df_voos_melhores).append(df_voos_rapidos)
final_df.to_excel('search_backups//{}_voos_{}-{}_de_{}_para_{}.xlsx'.format(strftime("%Y%m%d-%H%M"),
cidade_de, cidade_para,
data_inicio, data_fim), index=False)
print('dados salvos.....')
# Podemos acompanhar como a previsão fornecida pelo site se relaciona com a realidade
xp_loading = '//div[contains(@id,"advice")]'
loading = driver.find_element_by_xpath(xp_loading).text
xp_prediction = '//span[@class="info-text"]'
prediction = driver.find_element_by_xpath(xp_prediction).text
print(loading+'n'+prediction)
# Às vezes na variável loading aparece esta linha, que, mais tarde, causa problemas ao enviar o e-mail
# se isso acontecer - mudamos para "Not Sure"
estranho = '¯_(ツ)_/¯'
if loading == estranho:
loading = 'Não tenho certeza'
username = 'SEUEMAIL@hotmail.com'
password = 'SUA SENHA'
server = smtplib.SMTP('smtp.outlook.com', 587)
server.ehlo()
server.starttls()
server.login(username, password)
msg = ('Subject: Flight Scrapernn
Voo mais barato: {}nPreço médio: {}nnRecomendação: {}nnFim da mensagem'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
message = MIMEMultipart()
message['From'] = 'SEUEMAIL@hotmail.com'
message['to'] = 'SEUOUTROEMAIL@dominio.com'
server.sendmail('SEUEMAIL@hotmail.com', 'SEUOUTROEMAIL@dominio.com', msg)
print('email enviado.....')Eu testei este script usando uma conta Outlook (hotmail.com). Não verifiquei sua eficácia com uma conta Gmail, que é bastante popular, mas há muitas alternativas possíveis. Se você estiver usando uma conta Hotmail, para que tudo funcione, basta inserir seus dados no código.
Se você quiser entender o que exatamente é realizado em partes específicas do código dessa função, pode copiá-las e experimentar. Experimentar com o código é a única maneira de realmente compreendê-lo.
Sistema pronto
Agora que tudo o que discutimos foi feito, podemos criar um loop simples onde nossas funções são chamadas. O script solicita ao usuário informações sobre cidades e datas. Quando você testa o script reinicializando-o constantemente, provavelmente não quer inserir esses dados manualmente toda vez, então as respectivas linhas podem ser comentadas temporariamente, descomentando aquelas que vêm abaixo delas, nas quais os dados necessários para o script estão definidos de maneira fixa.
city_from = input('De qual cidade? ')
city_to = input('Para onde? ')
date_start = input('Procurar em torno de qual data de partida? Por favor, use apenas o formato AAAA-MM-DD ')
date_end = input('Retornar quando? Por favor, use apenas o formato AAAA-MM-DD ')
# city_from = 'LIS'
# city_to = 'SIN'
# date_start = '2019-08-21'
# date_end = '2019-09-07'
for n in range(0,5):
start_kayak(city_from, city_to, date_start, date_end)
print('Iteração {} completa em {}'.format(n, strftime("%Y%m%d-%H%M")))
# Esperamos 4 horas
sleep(60*60*4)
print('espera finalizada.....') Assim é como o teste do script parece.

Teste do script
Resultados
Se você chegou a este ponto — parabéns! Agora você tem um web scraper funcional, embora eu já veja várias maneiras de melhorá-lo. Por exemplo, ele pode ser integrado ao Twilio, para que, em vez de e-mails, envie mensagens de texto. Você pode usar uma VPN ou algo semelhante para obter resultados de vários servidores ao mesmo tempo. Além disso, há o problema ocasional de verificar se o usuário do site é humano, mas isso também pode ser resolvido. De qualquer forma, você agora tem uma base que pode, se desejar, expandir. Por exemplo, fazer com que um arquivo Excel seja enviado ao usuário como um anexo em um e-mail.
Apenas usuários registrados podem participar da pesquisa. , por favor.
Você utiliza tecnologias de web scraping?
Sim
Não
8 usuários votaram. 1 usuário se absteve.
Fonte: habr.com
