Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages

L'auteur de l'article, dont la traduction est publiée aujourd'hui, explique que son objectif est de décrire le développement d'un web scraper en Python utilisant Selenium, qui recherche les prix des billets d'avion. Lors de la recherche de billets, il utilise des dates flexibles (+- 3 jours autour des dates spécifiées). Le scraper enregistre les résultats de recherche dans un fichier Excel et envoie un e-mail à la personne qui l'a exécuté avec un aperçu des trouvailles. L'objectif de ce projet est d'aider les voyageurs à trouver les meilleures offres.

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages

Si, en explorant le contenu, vous vous sentez perdu, jetez un œil à cet article.

Que allons-nous rechercher?

Vous êtes libre d'utiliser le système décrit ici comme bon vous semble. Personnellement, je l'ai utilisé pour trouver des escapades de week-end et des billets pour ma ville natale. Si vous êtes sérieusement déterminé à rechercher des billets avantageux, vous pouvez exécuter le script sur un serveur (un simple serveur, pour 130 roubles par mois, convient parfaitement) et le faire s'exécuter une ou deux fois par jour. Les résultats de recherche vous seront envoyés par e-mail. De plus, je recommande de configurer le tout de manière à ce que le script enregistre un fichier Excel des résultats de recherche dans un dossier Dropbox, ce qui vous permettra de consulter ces fichiers partout et à tout moment.

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Je n'ai pas encore trouvé de tarifs erronés, mais je suppose que c'est possible

Lors de la recherche, comme déjà mentionné, date flexible est utilisée, le script trouve les offres situées dans un rayon de trois jours autour des dates spécifiées. Bien que le script ne cherche des offres que pour une seule direction à la fois, il n'est pas difficile de le modifier pour qu'il puisse collecter des données pour plusieurs directions de vol. Avec lui, il est même possible de rechercher des tarifs erronés, de telles trouvailles peuvent s'avérer très intéressantes.

Pourquoi a-t-on besoin d'un autre web scraper?

Lorsque j'ai commencé à faire du web scraping, pour être honnête, cela ne m'intéressait pas vraiment. Je voulais réaliser davantage de projets dans le domaine de la modélisation prédictive, de l'analyse financière et peut-être dans le domaine de l'analyse des émotions dans les textes. Mais je me suis rendu compte que c'était très intéressant — comprendre comment créer un programme qui collecte des données à partir de sites web. À mesure que je plongeais dans ce sujet, j'ai compris que le web scraping est le « moteur » d'internet.

Vous pourriez penser que c'est une affirmation audacieuse. Mais pensez à ce que l'entreprise Google a commencé avec un web scraper que Larry Page a créé en utilisant Java et Python. Les robots de Google explorent et continuent d'explorer internet, essayant de fournir les meilleures réponses possibles à leurs utilisateurs. Le web scraping a un nombre infini d'applications, et même si, dans le domaine de la Data Science, vous vous intéressez à autre chose, vous aurez besoin de certaines compétences en scraping pour obtenir des données à analyser.

Certaines des techniques utilisées ici, je les ai trouvées dans un excellent au livre livre sur le web scraping, que je viens d'acquérir. Il regorge de nombreux exemples simples et d'idées sur la façon d'appliquer ce que vous avez appris. De plus, il y a un chapitre très intéressant sur le contournement des vérifications reCaptcha. Pour moi, cela a été une révélation, car je ne savais pas qu'il existait des outils spéciaux et même des services entiers pour résoudre de tels problèmes.

Aimez-vous voyager ?

À la question simple et plutôt inoffensive posée dans le titre de cette section, on peut souvent entendre une réponse positive, accompagnée de quelques histoires de voyage de la part de la personne interrogée. La plupart d'entre nous s'accorde à dire que voyager est un excellent moyen de plonger dans de nouveaux environnements culturels et d'élargir ses horizons. Cependant, si vous demandez à quelqu'un s'il aime chercher des billets d'avion, je suis sûr que la réponse sera loin d'être aussi positive. En fait, c'est là que Python nous vient en aide.

La première tâche à accomplir pour créer un système de recherche d'informations sur les billets d'avion est de choisir la bonne plateforme à partir de laquelle nous allons recueillir des informations. Ce choix n'a pas été facile, mais j'ai finalement opté pour le service Kayak. J'ai testé des services comme Momondo, Skyscanner, Expedia, ainsi que quelques autres, mais les mécanismes de protection contre les robots sur ces plateformes étaient imprenables. Après plusieurs essais, au cours desquels j'ai dû convaincre les systèmes que j'étais un humain, en ayant à gérer des feux de circulation, des passages piétons et des vélos, j'ai décidé que Kayak était la plateforme qui me convenait le mieux, même si ici aussi, si vous chargez trop de pages en peu de temps, des vérifications commencent également. J'ai réussi à configurer mon bot pour qu'il envoie des requêtes au site à des intervalles de 4 à 6 heures, et tout fonctionne bien. Il y a parfois des difficultés avec Kayak, mais si vous êtes embêté par des vérifications, il faut soit les gérer manuellement avant de relancer le bot, soit attendre quelques heures, et les vérifications devraient cesser. Si nécessaire, vous pouvez parfaitement adapter le code pour une autre plateforme, et si vous le faites, n'hésitez pas à en faire part dans les commentaires.

Si vous débutez dans le web scraping et que vous ne savez pas pourquoi certains sites web s'y opposent avec tant de force, avant de commencer votre premier projet dans ce domaine, donnez-vous un conseil et recherchez sur Google des ressources avec les mots « web scraping etiquette ». Vos expériences peuvent se terminer plus rapidement que prévu si vous ne faites pas preuve de bon sens dans votre approche du web scraping.

Commencer

Voici un aperçu général de ce qui va se passer dans le code de notre web scraper :

  • Importation des bibliothèques nécessaires.
  • Ouverture d'un onglet Google Chrome.
  • Appel d'une fonction qui démarre le bot en lui passant les villes et les dates qui seront utilisées pour rechercher des billets.
  • Cette fonction obtient les premiers résultats de recherche, triés par ordre d'attractivité (best), et clique sur le bouton pour charger des résultats supplémentaires.
  • Une autre fonction collecte les données de toute la page et retourne un DataFrame.
  • Les deux étapes précédentes s'effectuent en utilisant des types de tri par prix des billets (cheap) et par rapidité de vol (fastest).
  • L'utilisateur du script reçoit un courriel contenant un résumé des prix des billets (les billets les moins chers et le prix moyen), et la trame de données avec les informations, triées selon les trois indicateurs mentionnés ci-dessus, est sauvegardée sous forme de fichier Excel.
  • Toutes les actions décrites ci-dessus sont effectuées en boucle à intervalles réguliers.

Il convient de noter que chaque projet Selenium commence avec un web driver. J'utilise Chromedriver, en travaillant avec Google Chrome, mais il existe également d'autres options. PhantomJS et Firefox sont également populaires. Après avoir téléchargé le driver, il doit être placé dans le dossier approprié, et la préparation pour son utilisation est alors terminée. Dans les premières lignes de notre script, un nouvel onglet Chrome est ouvert.

Gardez à l'esprit que, dans mon récit, je n'essaie pas d'ouvrir de nouveaux horizons pour trouver des offres avantageuses sur les billets d'avion. Il existe des méthodes beaucoup plus avancées pour rechercher de telles offres. Je souhaite simplement proposer aux lecteurs de ce matériel une méthode simple mais applicable en pratique pour résoudre cette tâche.

Voici le code dont nous avons parlé plus haut.

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

# Utilisez ici votre chemin vers chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # Cette commande ouvre la fenêtre Chrome
sleep(2)

Au début du code, vous pouvez voir les commandes d'importation des packages qui sont utilisés dans l'ensemble de notre projet. Ainsi, randint est utilisé pour que le bot "dorme" pendant un nombre aléatoire de secondes avant de commencer une nouvelle opération de recherche. En général, aucun bot ne se passe de cela. Si vous exécutez le code ci-dessus, une fenêtre Chrome s'ouvrira, que le bot utilisera pour interagir avec les sites.

Faisons une petite expérience et ouvrons le site kayak.com dans une fenêtre séparée. Sélectionnons la ville de départ et la ville d'arrivée, ainsi que les dates de vol. Lors de la sélection des dates, vérifions que la plage de dates soit de +-3 jours. J'ai écrit du code en tenant compte des réponses que le site donne à de telles requêtes. Si, par exemple, vous devez rechercher des billets uniquement pour des dates spécifiques, il est probable que vous deviez modifier le code du bot. En parlant du code, je fournis des explications appropriées, mais si vous vous sentez perdu, faites-le moi savoir.

Maintenant, cliquons sur le bouton de lancement de recherche et regardons le lien dans la barre d'adresse. Il devrait ressembler au lien que j'utilise dans l'exemple ci-dessous, où la variable est déclarée kayak, qui stocke l'URL, et utilise la méthode obtenir du web driver. Après avoir cliqué sur le bouton de recherche, les résultats devraient apparaître sur la page.

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Quand j'ai utilisé la commande obtenir plus de deux ou trois fois en quelques minutes, on me proposait de passer la vérification avec reCaptcha. Cette vérification peut être faite manuellement et vous pouvez continuer vos expériences jusqu'à ce que le système décide de déclencher une nouvelle vérification. Lorsque je testais le script, j'avais l'impression que la première session de recherche se déroulait toujours sans problème, donc si vous voulez expérimenter avec le code, vous devrez simplement passer manuellement la vérification de temps à autre et laisser le code s'exécuter en utilisant de longs intervalles entre les sessions de recherche. Après tout, si l'on y pense, une personne n'a probablement pas besoin d'informations sur les prix des billets obtenues avec des intervalles de 10 minutes entre les opérations de recherche.

Travailler avec la page en utilisant XPath

Donc, nous avons ouvert une fenêtre et chargé le site. Pour obtenir des informations sur les prix et d'autres détails, nous devons utiliser la technologie XPath ou les sélecteurs CSS. J'ai décidé de me concentrer sur XPath et je n'ai pas ressenti le besoin d'utiliser des sélecteurs CSS, mais on peut très bien travailler de cette façon. Naviguer sur la page à l'aide de XPath peut s'avérer compliqué, et même si vous utilisez les méthodes que j'ai décrites dans celui-ci Dans l'article où j'ai utilisé la copie des identifiants correspondants à partir du code de la page, j'ai compris que ce n'est en réalité pas une méthode optimale pour accéder aux éléments nécessaires. D'ailleurs, dans celui-ci le livre, vous trouverez une excellente description des bases du travail avec les pages en utilisant XPath et des sélecteurs CSS. Voici à quoi ressemble la méthode correspondante du pilote web.

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Donc, continuons à travailler sur le bot. Nous allons utiliser les fonctionnalités du programme pour rechercher les billets les moins chers. Dans l'image suivante, le code du sélecteur XPath est mis en évidence en rouge. Pour voir le code, vous devez faire un clic droit sur l'élément de la page qui vous intéresse et sélectionner l'option Inspecter (Inspect). Cette commande peut être utilisée pour différents éléments de la page, dont le code sera affiché et mis en évidence dans la fenêtre de visualisation du code.

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Visualisation du code de la page

Pour trouver confirmation de mes réflexions sur les inconvénients de la copie des sélecteurs à partir du code, faites attention aux caractéristiques suivantes.

Voici ce qui se passe lors de la copie du code :

//*[@id="wtKI-price_aTab"]/div[1]/div/div/div[1]/div/span/span

Pour copier quelque chose de similaire, il faut faire un clic droit sur la section de code qui vous intéresse et choisir dans le menu qui apparaît l'option Copie > Copier XPath.

Voici ce que j'ai utilisé pour identifier le bouton Cheapest :

cheap_results = '//'a[@data-code = "price"]'

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Commande Copie > Copier XPath

Il est tout à fait évident que la deuxième option semble beaucoup plus simple. Avec son utilisation, on recherche l'élément a qui a pour attribut data-code, égal à price. Avec la première option, on recherche un élément id dont l'identifiant est égal à wtKI-price_aTab, tandis que le chemin XPath vers l'élément ressemble à /div[1]/div/div/div[1]/div/span/span. Une telle requête XPath à la page fera son effet, mais - seulement une fois. Je peux dire tout de suite que id cela changera lors du prochain chargement de la page. La séquence de caractères wtKI change de manière dynamique à chaque chargement de page, ce qui signifie que le code dans lequel il est utilisé deviendra inutile après un nouveau chargement de la page. C'est pourquoi prenez un peu de temps pour vous familiariser avec XPath. Ces connaissances vous seront très utiles.

Il convient cependant de noter que la copie des sélecteurs XPath peut être utile lors de la gestion de sites relativement simples, et si cela vous convient, il n'y a rien de mal à cela.

Considérons maintenant comment obtenir tous les résultats de recherche sur plusieurs lignes, à l'intérieur d'une liste. C'est très simple. Chaque résultat se trouve dans un objet avec la classe resultWrapper. Le chargement de tous les résultats peut être effectué dans une boucle semblable à celle qui sera montrée ci-dessous.

Il convient de noter que si vous comprenez ce qui précède, vous ne devriez avoir aucun problème à comprendre la majorité du code que nous allons analyser. Dans le cadre de ce code, pour accéder à ce que nous voulons (en fait, c'est l'élément dans lequel le résultat est enveloppé), nous utilisons un certain mécanisme pour spécifier le chemin (XPath). Cela se fait pour obtenir le texte de l'élément et le placer dans un objet à partir duquel les données peuvent être lues (d'abord utilisé flight_containers, puis — flights_list).

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Nous affichons les trois premières lignes et nous pouvons clairement voir tout ce dont nous avons besoin. Cependant, nous avons des moyens plus intéressants d’obtenir des informations. Nous devons récupérer les données de chaque élément individuellement.

Au travail!

Il est plus facile d'écrire une fonction pour charger des résultats supplémentaires, donc commençons par cela. Je voudrais maximiser le nombre de vols dont le programme obtient des informations, tout en évitant d'éveiller les soupçons du service, ce qui pourrait conduire à une vérification. C'est pourquoi je clique une fois sur le bouton 'Charger plus de résultats' chaque fois que la page s'affiche. Dans ce code, il faut faire attention au bloc try, que j'ai ajouté parce que parfois le bouton ne se charge pas correctement. Si vous êtes également confronté à ce problème, commentez les appels à cette fonction dans le code de la fonction start_kayak, que nous examinerons ci-dessous.

# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
def load_more():
    try:
        more_results = '//a[@class = "moreButton"]'
        driver.find_element_by_xpath(more_results).click()
        # Вывод этих заметок в ходе работы программы помогает мне быстро выяснить то, чем она занята
        print('sleeping.....')
        sleep(randint(45,60))
    except:
        pass

Après une longue analyse de cette fonction (parfois je peux m'enthousiasmer), nous sommes prêts à déclarer la fonction qui s'occupera du scraping de la page.

J'ai déjà rassemblé la plupart des éléments nécessaires dans la fonction suivante, appelée page_scrape. Parfois, les données retournées concernant les étapes du chemin sont combinées, pour les séparer, j'utilise une méthode simple. Par exemple, lorsque j'utilise pour la première fois les variables section_a_list et section_b_list. Notre fonction retourne un cadre de données flights_df, ce qui nous permet de séparer les résultats obtenus en utilisant différentes méthodes de tri des données, puis de les fusionner.

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

J'ai essayé de nommer les variables de manière à ce que le code soit compréhensible. N'oubliez pas que les variables qui commencent par a se rapportent à la première étape du parcours, et b à la deuxième. Passons à la fonction suivante.

Mécanismes d'assistance

Nous avons maintenant une fonction qui permet de charger des résultats de recherche supplémentaires et une fonction pour traiter ces résultats. Cet article pourrait se terminer ici, car ces deux fonctions fournissent tout ce qui est nécessaire pour le scraping de pages, que l'on peut ouvrir soi-même. Mais nous n'avons pas encore abordé certains mécanismes d'assistance, que nous avons mentionnés précédemment. Par exemple, il y a le code pour envoyer des emails et d'autres choses. Tout cela se trouve dans la fonction start_kayak, que nous allons maintenant examiner.

Pour faire fonctionner cette fonction, des informations sur les villes et les dates sont nécessaires. Elle, en utilisant ces informations, crée un lien dans la variable kayak, qui est utilisée pour naviguer vers la page où se trouveront les résultats de recherche, triés par leur meilleure correspondance avec la requête. Après la première session de scraping, nous nous occuperons des prix présents dans le tableau, en haut de la page. Plus précisément, nous trouverons le prix minimum du billet et le prix moyen. Tout cela, avec la prévision donnée par le site, sera envoyé par email. Sur la page, le tableau correspondant devrait être en haut à gauche. D'ailleurs, travailler avec ce tableau peut provoquer une erreur lors de recherches utilisant des dates exactes, car dans ce cas, le tableau ne s'affiche pas sur la page.

def start_kayak(city_from, city_to, date_start, date_end):
    """Codes des villes - ce sont les codes IATA !
    Format de date - 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))
    
    # Il arrive parfois qu'une fenêtre contextuelle apparaisse, pour tester cela et la fermer, on peut utiliser le bloc try
    try:
        xp_popup_close = '//*[@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('chargement en cours.....')
    
#     load_more()
    
    print('début du premier scrap.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Prenons le prix le plus bas dans le tableau situé en haut de la page
    matrix = driver.find_elements_by_xpath('/*[contains(@id,"FlexMatrixCell")]')
    matrix_prices = [price.text.replace('$','') for price in matrix]
    matrix_prices = list(map(int, matrix_prices))
    matrix_min = min(matrix_prices)
    matrix_avg = sum(matrix_prices)/len(matrix_prices)
    
    print('passage aux résultats les moins chers.....')
    cheap_results = '//*[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('chargement en cours.....')
    
#     load_more()
    
    print('début du second scrap.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('passage aux résultats les plus rapides.....')
    quick_results = '//*[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('chargement en cours.....')
    
#     load_more()
    
    print('début du troisième scrap.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Sauvegarde du nouveau cadre dans un fichier Excel, dont le nom reflète les villes et les dates
    final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)
    final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime("%Y%m%d-%H%M"),
                                                                                   city_from, city_to, 
                                                                                   date_start, date_end), index=False)
    print('df sauvegardé.....')
    
    # On peut suivre comment la prévision donnée par le site se rapporte à la réalité
    xp_loading = '//*[@contains(@id,"advice")]'
    loading = driver.find_element_by_xpath(xp_loading).text
    xp_prediction = '//*[@class="info-text"]'
    prediction = driver.find_element_by_xpath(xp_prediction).text
    print(loading+'n'+prediction)
    
    # Il arrive parfois que la variable loading contienne cette chaîne, ce qui crée par la suite des problèmes lors de l'envoi de l'email
    # Si cela arrive, on la remplace par "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Pas sûr'
    
    username = 'YOUREMAIL@hotmail.com'
    password = 'VOTRE MOT DE PASSE'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Sujet : Flight Scraper
Vol le moins cher : {}nPrix moyen : {}nnRecommandation : {}nnFin du message'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
    message = MIMEMultipart()
    message['From'] = 'YOUREMAIL@hotmail.com'
    message['to'] = 'YOUROTHEREMAIL@domain.com'
    server.sendmail('YOUREMAIL@hotmail.com', 'YOUROTHEREMAIL@domain.com', msg)
    print('email envoyé.....')

J'ai testé ce script en utilisant un compte Outlook (hotmail.com). Je ne l'ai pas vérifié sur la façon dont il fonctionne avec un compte Gmail, ce service de messagerie étant très populaire, mais il existe de nombreuses options possibles. Si vous utilisez un compte Hotmail, il vous suffit d'entrer vos données dans le code pour que tout fonctionne.

Si vous souhaitez comprendre ce qui se passe dans les différentes parties du code de cette fonction, vous pouvez les copier et expérimenter avec. Les expérimentations avec le code sont le seul moyen de bien le comprendre.

Système prêt

Maintenant que tout ce dont nous avons parlé est fait, nous pouvons créer une simple boucle dans laquelle nous appelons nos fonctions. Le script demande à l'utilisateur des informations sur les villes et les dates. Lors des tests avec une redémarrage constant du script, il est peu probable que vous souhaitiez entrer ces données manuellement chaque fois, c'est pourquoi vous pouvez commenter les lignes correspondantes pendant les tests et décommenter celles qui suivent, dans lesquelles les données nécessaires au script sont définies en dur.

city_from = input('De quelle ville ? ')
city_to = input('Où aller ? ')
date_start = input('Recherchez autour de quelle date de départ ? Veuillez utiliser uniquement le format AAAA-MM-JJ ')
date_end = input('Retourner quand ? Veuillez utiliser uniquement le format AAAA-MM-JJ ')

# 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('l'itération {} a été complétée @ {}'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Attendre 4 heures
    sleep(60*60*4)
    print('fin de l'attente.....')

Voici à quoi ressemble le lancement de test du script.
Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages
Lancement de test du script

Résultats

Si vous êtes arrivé à ce stade, félicitations ! Vous avez maintenant un web scraper fonctionnel, bien que je voie déjà de nombreux moyens de l'améliorer. Par exemple, vous pourriez l'intégrer avec Twilio pour qu'il envoie des SMS au lieu d'e-mails. Vous pourriez utiliser un VPN ou autre chose pour obtenir simultanément des résultats de plusieurs serveurs. Il y a aussi le problème de la vérification périodique par le site pour déterminer si l'utilisateur est un humain, mais ce problème peut également être résolu. Dans tous les cas, vous avez maintenant une base que vous pouvez étendre si vous le souhaitez. Par exemple, rendre un fichier Excel envoyé à l'utilisateur en tant que pièce jointe à un e-mail.

Python — l'assistant pour la recherche de billets d'avion à bas prix pour les amateurs de voyages

Seuls les utilisateurs enregistrés peuvent participer au sondage. Connectez-vous, s'il vous plaît.

Utilisez-vous des technologies de web scraping ?

  • Oui

  • Non

8 utilisateurs ont voté. 1 utilisateur s'est abstenu.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster