Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager

L'auteur de l'article, dont nous publions la traduction aujourd'hui, affirme que son objectif est de parler du développement d'un web scraper en Python utilisant Selenium, qui recherche les prix des billets d'avion. Lors de la recherche de billets, des dates flexibles sont utilisées (+- 3 jours par rapport aux dates indiquées). Le scraper enregistre les résultats de la recherche dans un fichier Excel et envoie à la personne qui a effectué la recherche un e-mail avec un résumé de ce qu'elle a trouvé. L'objectif de ce projet est d'aider les voyageurs à trouver les meilleures offres.

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager

Si, en comprenant le matériel, vous vous sentez perdu, jetez un œil à cette article.

Que cherchons-nous?

Vous êtes libre d’utiliser le système décrit ici comme vous le souhaitez. Par exemple, je l'ai utilisé pour rechercher des visites du week-end et des billets pour ma ville natale. Si vous voulez vraiment trouver des billets rentables, vous pouvez exécuter le script sur le serveur (simple serveur, pour 130 roubles par mois, convient tout à fait) et assurez-vous qu'il fonctionne une à deux fois par jour. Les résultats de la recherche vous seront envoyés par email. De plus, je recommande de tout configurer pour que le script enregistre un fichier Excel avec les résultats de la recherche dans un dossier Dropbox, ce qui vous permettra de visualiser ces fichiers de n'importe où et à tout moment.

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Je n'ai pas encore trouvé de tarifs erronés, mais je pense que c'est possible

Lors de la recherche, comme déjà mentionné, une « date flexible » est utilisée ; le script trouve les offres qui se situent dans les trois jours suivant les dates indiquées. Bien que lors de l'exécution du script, il recherche les offres dans une seule direction, il est facile de le modifier pour qu'il puisse collecter des données sur plusieurs directions de vol. Avec son aide, vous pouvez même rechercher des tarifs erronés, de telles découvertes peuvent être très intéressantes.

Pourquoi avez-vous besoin d’un autre web scraper ?

Quand j’ai commencé le web scraping, honnêtement, cela ne m’intéressait pas particulièrement. Je souhaitais réaliser davantage de projets dans le domaine de la modélisation prédictive, de l'analyse financière et, éventuellement, dans le domaine de l'analyse de la coloration émotionnelle des textes. Mais il s’est avéré très intéressant de comprendre comment créer un programme qui collecte des données à partir de sites Web. En approfondissant ce sujet, j'ai réalisé que le web scraping est le « moteur » d'Internet.

Vous pensez peut-être que c’est une déclaration trop audacieuse. Mais considérez que Google a commencé avec un grattoir Web créé par Larry Page en utilisant Java et Python. Les robots de Google explorent Internet pour tenter de fournir à ses utilisateurs les meilleures réponses à leurs questions. Le Web scraping a des utilisations infinies, et même si vous êtes intéressé par autre chose en science des données, vous aurez besoin de compétences en scraping pour obtenir les données que vous devez analyser.

J'ai trouvé certaines des techniques utilisées ici d'une manière merveilleuse un livre sur le web scraping, que j'ai récemment acquis. Il contient de nombreux exemples simples et des idées pour une application pratique de ce que vous avez appris. De plus, il existe un chapitre très intéressant sur le contournement des contrôles reCaptcha. C’était une nouvelle pour moi, car je ne savais même pas qu’il existait des outils spéciaux, voire des services complets, pour résoudre de tels problèmes.

Aimes-tu voyager?!

A la question simple et plutôt inoffensive posée dans le titre de cette section, on entend souvent une réponse positive, accompagnée de quelques récits de voyages de la personne à qui elle a été posée. La plupart d’entre nous conviendraient que voyager est un excellent moyen de s’immerger 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 ne sera pas aussi positive. En fait, Python vient ici en aide.

La première tâche que nous devons résoudre en vue de créer un système de recherche d'informations sur les billets d'avion sera de sélectionner une plate-forme appropriée à partir de laquelle nous récupérerons les informations. Résoudre ce problème n'a pas été facile pour moi, mais j'ai finalement choisi le service Kayak. J'ai essayé les services de Momondo, Skyscanner, Expedia et quelques autres, mais les mécanismes de protection des robots sur ces ressources étaient impénétrables. Après plusieurs tentatives, au cours desquelles j'ai dû composer avec des feux de circulation, des passages pour piétons et des vélos, en essayant de convaincre les systèmes que j'étais humain, j'ai décidé que Kayak me convenait le mieux, malgré le fait que même si trop de pages sont chargées dans peu de temps, et les contrôles commencent également. J'ai réussi à faire en sorte que le bot envoie des requêtes au site à des intervalles de 4 à 6 heures, et tout a bien fonctionné. De temps en temps, des difficultés surviennent lorsque vous travaillez avec Kayak, mais s'ils commencent à vous harceler avec des contrôles, vous devez alors soit les traiter manuellement puis lancer le bot, soit attendre quelques heures et les contrôles devraient s'arrêter. Si nécessaire, vous pouvez facilement adapter le code pour une autre plateforme, et si vous le faites, vous pouvez le signaler dans les commentaires.

Si vous débutez avec le web scraping et que vous ne savez pas pourquoi certains sites Web ont du mal à y parvenir, alors avant de démarrer votre premier projet dans ce domaine, rendez-vous service et effectuez une recherche Google sur les mots « étiquette du web scraping ». . Vos expériences peuvent se terminer plus tôt que vous ne le pensez si vous effectuez du web scraping de manière imprudente.

Mise en route

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

  • Importez les bibliothèques requises.
  • Ouverture d'un onglet Google Chrome.
  • Appelez une fonction qui démarre le bot en lui transmettant les villes et les dates qui seront utilisées lors de la recherche de billets.
  • Cette fonction prend les premiers résultats de recherche, triés par les meilleurs, et clique sur un bouton pour charger plus de résultats.
  • Une autre fonction collecte les données de la page entière et renvoie un bloc de données.
  • Les deux étapes précédentes sont réalisées en utilisant des types de tri par prix du billet (bon marché) et par vitesse de vol (le plus rapide).
  • L'utilisateur du script reçoit un e-mail contenant un résumé des prix des billets (billets les moins chers et prix moyen), et une trame de données contenant des informations triées selon les trois indicateurs mentionnés ci-dessus est enregistrée sous forme de fichier Excel.
  • Toutes les actions ci-dessus sont effectuées dans un cycle après une période de temps spécifiée.

Il convient de noter que chaque projet Selenium démarre avec un pilote Web. j'utilise Pilote Chrome, je travaille avec Google Chrome, mais il existe d'autres options. PhantomJS et Firefox sont également populaires. Après avoir téléchargé le pilote, vous devez le placer dans le dossier approprié, ce qui termine la préparation à son utilisation. Les premières lignes de notre script ouvrent un nouvel onglet Chrome.

Gardez à l’esprit que dans mon histoire, je n’essaie pas d’ouvrir de nouveaux horizons pour trouver de bonnes affaires sur les billets d’avion. Il existe des méthodes beaucoup plus avancées pour rechercher de telles offres. Je veux juste offrir aux lecteurs de ce document un moyen simple mais pratique de résoudre ce problème.

Voici le code dont nous avons parlé ci-dessus.

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

# Используйте тут ваш путь к chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # Этой командой открывается окно Chrome
sleep(2)

Au début du code, vous pouvez voir les commandes d'importation de packages utilisées tout au long de notre projet. Donc, randint utilisé pour endormir le bot pendant un nombre aléatoire de secondes avant de commencer une nouvelle opération de recherche. Habituellement, aucun robot ne peut s'en passer. Si vous exécutez le code ci-dessus, une fenêtre Chrome s'ouvrira, que le bot utilisera pour travailler avec les sites.

Faisons une petite expérience et ouvrons le site Web kayak.com dans une fenêtre séparée. Nous sélectionnerons la ville à partir de laquelle nous allons voler, et la ville vers laquelle nous souhaitons nous rendre, ainsi que les dates de vol. Lors du choix des dates, assurez-vous que la plage de +-3 jours est utilisée. J'ai écrit le code en tenant compte de ce que le site produit en réponse à de telles demandes. Si, par exemple, vous devez rechercher des billets uniquement pour des dates spécifiées, il est fort probable que vous deviez modifier le code du bot. Lorsque je parle du code, je donne des explications appropriées, mais si vous vous sentez confus, faites-le-moi savoir.

Cliquez maintenant sur le bouton de recherche et regardez le lien dans la barre d'adresse. Cela devrait être similaire au lien que j'utilise dans l'exemple ci-dessous où la variable est déclarée kayak, qui stocke l'URL, et la méthode est utilisée get pilote Web. Après avoir cliqué sur le bouton de recherche, les résultats devraient apparaître sur la page.

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Quand j'ai utilisé la commande get plus de deux ou trois fois en quelques minutes, on m'a demandé de terminer la vérification à l'aide de reCaptcha. Vous pouvez réussir cette vérification manuellement et continuer à expérimenter jusqu'à ce que le système décide d'exécuter une nouvelle vérification. Lorsque j'ai testé le script, il semblait que la première session de recherche se déroulait toujours sans problème, donc si vous souhaitiez expérimenter le code, il vous suffirait de vérifier manuellement périodiquement et de laisser le code s'exécuter, en utilisant de longs intervalles entre les sessions de recherche. Et, si l'on y réfléchit, il est peu probable qu'une personne ait besoin d'informations sur les prix des billets reçues à intervalles de 10 minutes entre les opérations de recherche.

Travailler avec une page en utilisant XPath

Nous avons donc ouvert une fenêtre et chargé le site. Pour obtenir les prix et d'autres informations, nous devons utiliser la technologie XPath ou des sélecteurs CSS. J'ai décidé de m'en tenir à XPath et je n'ai pas ressenti le besoin d'utiliser des sélecteurs CSS, mais il est tout à fait possible de travailler de cette façon. Naviguer dans une page à l'aide de XPath peut être délicat, et même si vous utilisez les techniques que j'ai décrites dans cette article, qui impliquait de copier les identifiants correspondants du code de la page, je me suis rendu compte que ce n'était en fait pas la manière optimale d'accéder aux éléments nécessaires. D'ailleurs, dans cette Le livre fournit une excellente description des bases du travail avec des pages à l'aide des sélecteurs XPath et CSS. Voici à quoi ressemble la méthode du pilote Web correspondante.

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Alors continuons à travailler sur le bot. Utilisons les capacités du programme pour sélectionner les billets les moins chers. Dans l'image suivante, le code du sélecteur XPath est surligné en rouge. Pour afficher le code, vous devez cliquer avec le bouton droit sur l'élément de page qui vous intéresse et sélectionner la commande Inspecter dans le menu qui apparaît. Cette commande peut être appelée pour différents éléments de page dont le code sera affiché et mis en évidence dans le visualiseur de code.

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Afficher le code de la page

Afin de trouver la confirmation de mon raisonnement sur les inconvénients de la copie des sélecteurs à partir du code, faites attention aux fonctionnalités suivantes.

Voici ce que vous obtenez lorsque vous copiez le code :

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

Pour copier quelque chose comme ceci, vous devez cliquer avec le bouton droit sur la section de code qui vous intéresse et sélectionner la commande Copier > Copier XPath dans le menu qui apparaît.

Voici ce que j'ai utilisé pour définir le bouton le moins cher :

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

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Copier la commande > Copier XPath

Il est évident que la deuxième option semble beaucoup plus simple. Lorsqu'il est utilisé, il recherche un élément a qui possède l'attribut data-codeégal à price. Lors de l'utilisation de la première option, l'élément est recherché id qui est égal à wtKI-price_aTab, et le chemin XPath vers l'élément ressemble à /div[1]/div/div/div[1]/div/span/span. Une requête XPath comme celle-ci sur une page fera l'affaire, mais une seule fois. Je peux dire maintenant que id changera au prochain chargement de la page. Séquence de caractères wtKI change dynamiquement à chaque fois que la page est chargée, donc le code qui l'utilise sera inutile après le prochain rechargement de la page. Prenez donc le temps de comprendre XPath. Cette connaissance vous sera très utile.

Cependant, il convient de noter que la copie des sélecteurs XPath peut être utile lorsque vous travaillez avec des sites assez simples, et si vous êtes à l'aise avec cela, il n'y a rien de mal à cela.

Réfléchissons maintenant à ce qu'il faut faire si vous avez besoin d'obtenir tous les résultats de la recherche sur plusieurs lignes, dans une liste. Très simple. Chaque résultat se trouve à l'intérieur d'un objet avec une classe resultWrapper. Le chargement de tous les résultats peut être effectué dans une boucle similaire à celle présentée ci-dessous.

Il convient de noter que si vous comprenez ce qui précède, vous devriez alors facilement comprendre la plupart du code que nous analyserons. Pendant l'exécution de ce code, nous accédons à ce dont nous avons besoin (en fait, l'élément dans lequel le résultat est encapsulé) en utilisant une sorte de mécanisme de spécification de chemin (XPath). Ceci est fait afin d'obtenir le texte de l'élément et de le placer dans un objet à partir duquel les données peuvent être lues (d'abord utilisé flight_containers, puis - flights_list).

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Les trois premières lignes sont affichées et nous pouvons voir clairement tout ce dont nous avons besoin. Cependant, nous disposons de moyens plus intéressants pour obtenir des informations. Nous devons prendre les données de chaque élément séparément.

Mettez-vous au travail !

Le moyen le plus simple d’écrire une fonction est de charger des résultats supplémentaires, c’est donc par là que nous commencerons. Je souhaite maximiser le nombre de vols sur lesquels le programme reçoit des informations, sans éveiller de soupçons dans le service qui mène à une inspection, c'est pourquoi je clique sur le bouton Charger plus de résultats une fois à chaque fois que la page est affichée. Dans ce code, vous devez faire attention au bloc try, que j'ai ajouté car parfois le bouton ne se charge pas correctement. Si vous rencontrez également 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

Maintenant, après une longue analyse de cette fonction (parfois je peux m'emballer), nous sommes prêts à déclarer une fonction qui grattera la page.

J'ai déjà rassemblé la plupart de ce qui est nécessaire dans la fonction suivante appelée page_scrape. Parfois, les données de chemin renvoyées sont combinées, j'utilise donc une méthode simple pour les séparer. Par exemple, lorsque j'utilise des variables pour la première fois section_a_list и section_b_list. Notre fonction renvoie une trame de données flights_df, cela nous permet de séparer les résultats obtenus à partir de différentes méthodes de tri de données et de les combiner ultérieurement.

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 pour que le code soit compréhensible. N'oubliez pas que les variables commençant par a appartiennent à la première étape du chemin, et b - à la seconde. Passons à la fonction suivante.

Mécanismes de soutien

Nous disposons désormais d'une fonction qui nous permet de charger des résultats de recherche supplémentaires et d'une fonction pour traiter ces résultats. Cet article aurait pu se terminer ici, puisque ces deux fonctions fournissent tout ce dont vous avez besoin pour gratter des pages que vous pouvez ouvrir vous-même. Mais nous n’avons pas encore examiné certains des mécanismes auxiliaires évoqués ci-dessus. Par exemple, c'est le code pour envoyer des e-mails et d'autres choses. Tout cela se trouve dans la fonction start_kayak, que nous allons maintenant considérer.

Cette fonction nécessite des informations sur les villes et les dates. A partir de ces informations, il forme un lien dans une variable kayak, qui est utilisé pour vous diriger vers une page qui contiendra les résultats de recherche triés selon leur meilleure correspondance avec la requête. Après la première séance de scraping, nous travaillerons avec les prix indiqués dans le tableau en haut de la page. A savoir, nous trouverons le prix minimum du billet et le prix moyen. Tout cela, accompagné de la prédiction émise par le site, sera envoyé par email. Sur la page, le tableau correspondant doit se trouver dans le coin supérieur gauche. Soit dit en passant, travailler avec ce tableau peut provoquer une erreur lors de la recherche à l'aide de dates exactes, car dans ce cas, le tableau n'est pas affiché sur la page.

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))
    
    # иногда появляется всплывающее окно, для проверки на это и его закрытия можно воспользоваться блоком 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('loading more.....')
    
#     load_more()
    
    print('starting first scrape.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Возьмём самую низкую цену из таблицы, расположенной в верхней части страницы
    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('switching to cheapest results.....')
    cheap_results = '//a[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting second scrape.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('switching to quickest results.....')
    quick_results = '//a[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting third scrape.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Сохранение нового фрейма в Excel-файл, имя которого отражает города и даты
    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('saved df.....')
    
    # Можно следить за тем, как прогноз, выдаваемый сайтом, соотносится с реальностью
    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)
    
    # иногда в переменной loading оказывается эта строка, которая, позже, вызывает проблемы с отправкой письма
    # если это прозошло - меняем её на "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Not sure'
    
    username = '[email protected]'
    password = 'YOUR PASSWORD'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Subject: Flight Scrapernn
Cheapest Flight: {}nAverage Price: {}nnRecommendation: {}nnEnd of message'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
    message = MIMEMultipart()
    message['From'] = '[email protected]'
    message['to'] = '[email protected]'
    server.sendmail('[email protected]', '[email protected]', msg)
    print('sent email.....')

J'ai testé ce script en utilisant un compte Outlook (hotmail.com). Je ne l'ai pas testé pour fonctionner correctement avec un compte Gmail, ce système de messagerie est assez populaire, mais il existe de nombreuses options possibles. Si vous utilisez un compte Hotmail, pour que tout fonctionne, il vous suffit de saisir vos données dans le code.

Si vous souhaitez comprendre ce qui est fait exactement dans des sections spécifiques du code de cette fonction, vous pouvez les copier et les expérimenter. Expérimenter le code est le seul moyen de vraiment le comprendre.

Système prêt

Maintenant que nous avons fait tout ce dont nous avons parlé, nous pouvons créer une boucle simple qui appelle nos fonctions. Le script demande des données à l'utilisateur sur les villes et les dates. Lors d'un test avec redémarrage constant du script, il est peu probable que vous souhaitiez saisir ces données manuellement à chaque fois, donc les lignes correspondantes, pour le moment du test, peuvent être commentées en décommentant celles en dessous, dans lesquelles les données nécessaires au le script est codé en dur.

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 ')

# 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('iteration {} was complete @ {}'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Ждём 4 часа
    sleep(60*60*4)
    print('sleep finished.....')

Voici à quoi ressemble un test du script.
Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager
Test du script

Les résultats de

Si vous êtes arrivé jusqu'ici, félicitations ! Vous disposez désormais d'un grattoir Web fonctionnel, même si je vois déjà de nombreuses façons de l'améliorer. Par exemple, il peut être intégré à Twilio pour envoyer des messages texte au lieu d'e-mails. Vous pouvez utiliser un VPN ou autre chose pour recevoir simultanément les résultats de plusieurs serveurs. Il existe également un problème périodique lié à la vérification de l'utilisateur du site pour voir s'il est une personne, mais ce problème peut également être résolu. Dans tous les cas, vous disposez désormais d’une base que vous pouvez agrandir si vous le souhaitez. Par exemple, assurez-vous qu'un fichier Excel est envoyé à l'utilisateur en pièce jointe à un e-mail.

Python - un assistant pour trouver des billets d'avion bon marché pour ceux qui aiment voyager

Seuls les utilisateurs enregistrés peuvent participer à l'enquête. se connecters'il te plait.

Utilisez-vous des technologies de web scraping ?

  • Oui

  • Aucun

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

Source: habr.com

Ajouter un commentaire