Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber

Der Autor des Artikels, dessen Übersetzung wir heute veröffentlichen, sagt, dass sein Ziel darin besteht, über die Entwicklung eines Web-Scraper in Python mit Selenium zu sprechen, der nach Flugticketpreisen sucht. Bei der Suche nach Tickets werden flexible Termine verwendet (+- 3 Tage relativ zu den angegebenen Terminen). Der Scraper speichert die Suchergebnisse in einer Excel-Datei und sendet der Person, die die Suche durchgeführt hat, eine E-Mail mit einer Zusammenfassung der gefundenen Ergebnisse. Ziel dieses Projekts ist es, Reisenden dabei zu helfen, die besten Angebote zu finden.

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber

Wenn Sie sich beim Verstehen des Materials verloren fühlen, werfen Sie einen Blick darauf diese Artikel.

Was suchen wir?

Es steht Ihnen frei, das hier beschriebene System nach Ihren Wünschen zu nutzen. Ich habe damit beispielsweise nach Wochenendtouren und Tickets für meine Heimatstadt gesucht. Wenn Sie es ernst meinen mit der Suche nach profitablen Tickets, können Sie das Skript auf dem Server ausführen (einfach). Server, für 130 Rubel im Monat, ist hierfür durchaus geeignet) und achten Sie darauf, dass es ein- bis zweimal am Tag läuft. Die Suchergebnisse werden Ihnen per E-Mail zugesandt. Darüber hinaus empfehle ich, alles so einzurichten, dass das Skript eine Excel-Datei mit Suchergebnissen in einem Dropbox-Ordner speichert, sodass Sie solche Dateien jederzeit und überall anzeigen können.

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Tarife mit Fehlern habe ich noch nicht gefunden, halte es aber für möglich

Bei der Suche wird, wie bereits erwähnt, ein „flexibles Datum“ verwendet; das Skript findet Angebote, die innerhalb von drei Tagen zu den angegebenen Daten liegen. Obwohl das Skript beim Ausführen nur in einer Richtung nach Angeboten sucht, lässt es sich leicht ändern, sodass Daten für mehrere Flugrichtungen erfasst werden können. Mit seiner Hilfe können Sie sogar nach fehlerhaften Tarifen suchen; solche Funde können sehr interessant sein.

Warum brauchen Sie einen weiteren Web-Scraper?

Als ich zum ersten Mal mit Web Scraping begann, war ich ehrlich gesagt nicht besonders daran interessiert. Ich wollte mehr Projekte im Bereich Vorhersagemodellierung, Finanzanalyse und möglicherweise im Bereich der Analyse der emotionalen Färbung von Texten durchführen. Es stellte sich jedoch heraus, dass es sehr interessant war, herauszufinden, wie man ein Programm erstellt, das Daten von Websites sammelt. Als ich mich mit diesem Thema befasste, wurde mir klar, dass Web Scraping der „Motor“ des Internets ist.

Sie denken vielleicht, dass dies eine zu kühne Aussage ist. Bedenken Sie jedoch, dass Google mit einem Web-Scraper begann, den Larry Page mit Java und Python erstellt hat. Google-Roboter erforschen das Internet und versuchen, seinen Nutzern die besten Antworten auf ihre Fragen zu geben. Web Scraping hat endlose Einsatzmöglichkeiten, und selbst wenn Sie sich für etwas anderes im Bereich Data Science interessieren, benötigen Sie einige Scraping-Kenntnisse, um an die Daten zu gelangen, die Sie analysieren müssen.

Ich fand einige der hier verwendeten Techniken wunderbar ein Buch über Web Scraping, das ich kürzlich erworben habe. Es enthält viele einfache Beispiele und Ideen zur praktischen Anwendung des Gelernten. Darüber hinaus gibt es ein sehr interessantes Kapitel zur Umgehung von reCaptcha-Prüfungen. Für mich war das eine Neuigkeit, da ich nicht einmal wusste, dass es spezielle Tools und sogar ganze Dienstleistungen zur Lösung solcher Probleme gibt.

Reisen Sie gerne?!

Auf die einfache und eher harmlose Frage im Titel dieses Abschnitts hört man oft eine positive Antwort, begleitet von ein paar Geschichten aus den Reisen der Person, der sie gestellt wurde. Die meisten von uns würden zustimmen, dass Reisen eine großartige Möglichkeit ist, in neue kulturelle Umgebungen einzutauchen und seinen Horizont zu erweitern. Wenn Sie jedoch jemanden fragen, ob ihm die Suche nach Flugtickets gefällt, wird die Antwort sicher nicht so positiv ausfallen. Tatsächlich kommt uns hier Python zu Hilfe.

Die erste Aufgabe, die wir auf dem Weg zur Schaffung eines Systems zur Suche nach Informationen zu Flugtickets lösen müssen, ist die Auswahl einer geeigneten Plattform, von der wir Informationen beziehen. Die Lösung dieses Problems fiel mir nicht leicht, aber am Ende habe ich mich für den Kayak-Service entschieden. Ich habe die Dienste von Momondo, Skyscanner, Expedia und einigen anderen ausprobiert, aber die Roboterschutzmechanismen dieser Ressourcen waren undurchdringlich. Nach mehreren Versuchen, bei denen ich mich mit Ampeln, Fußgängerüberwegen und Fahrrädern auseinandersetzen musste, um die Systeme davon zu überzeugen, dass ich ein Mensch bin, entschied ich, dass Kayak am besten für mich geeignet war, obwohl zu viele Seiten geladen wurden in kurzer Zeit, und auch die Kontrollen beginnen. Ich habe es geschafft, dass der Bot in Abständen von 4 bis 6 Stunden Anfragen an die Website sendet, und alles hat gut funktioniert. Von Zeit zu Zeit treten bei der Arbeit mit Kayak Schwierigkeiten auf. Wenn Sie jedoch mit Überprüfungen belästigt werden, müssen Sie diese entweder manuell beheben und dann den Bot starten oder ein paar Stunden warten, bis die Überprüfungen aufhören. Bei Bedarf können Sie den Code problemlos für eine andere Plattform anpassen und wenn Sie dies tun, können Sie dies in den Kommentaren melden.

Wenn Sie gerade erst mit dem Web-Scraping beginnen und nicht wissen, warum einige Websites damit Probleme haben, dann tun Sie sich selbst einen Gefallen und führen Sie eine Google-Suche nach den Wörtern „Web-Scraping-Etikette“ durch, bevor Sie Ihr erstes Projekt in diesem Bereich starten. . Ihre Experimente enden möglicherweise früher als Sie denken, wenn Sie unklugerweise Web Scraping betreiben.

Erste Schritte

Hier ist ein allgemeiner Überblick darüber, was in unserem Web-Scraper-Code passieren wird:

  • Importieren Sie die erforderlichen Bibliotheken.
  • Öffnen eines Google Chrome-Tabs.
  • Rufen Sie eine Funktion auf, die den Bot startet und ihm die Städte und Daten übergibt, die bei der Suche nach Tickets verwendet werden.
  • Diese Funktion nimmt die ersten Suchergebnisse, sortiert nach den besten, und klickt auf eine Schaltfläche, um weitere Ergebnisse zu laden.
  • Eine andere Funktion sammelt Daten von der gesamten Seite und gibt einen Datenrahmen zurück.
  • Die beiden vorherigen Schritte werden mithilfe der Sortierarten nach Ticketpreis (günstig) und Fluggeschwindigkeit (am schnellsten) durchgeführt.
  • Dem Benutzer des Skripts wird eine E-Mail mit einer Zusammenfassung der Ticketpreise (günstigste Tickets und Durchschnittspreis) gesendet und ein Datenrahmen mit Informationen, sortiert nach den drei oben genannten Indikatoren, wird als Excel-Datei gespeichert.
  • Alle oben genannten Aktionen werden in einem Zyklus nach einer bestimmten Zeitspanne ausgeführt.

Es ist zu beachten, dass jedes Selenium-Projekt mit einem Webtreiber beginnt. Ich verwende Chrome-TreiberIch arbeite mit Google Chrome, es gibt aber auch andere Optionen. Beliebt sind auch PhantomJS und Firefox. Nachdem Sie den Treiber heruntergeladen haben, müssen Sie ihn im entsprechenden Ordner ablegen. Damit ist die Vorbereitung für die Verwendung abgeschlossen. Die ersten Zeilen unseres Skripts öffnen einen neuen Chrome-Tab.

Denken Sie daran, dass ich in meiner Geschichte nicht versuche, neue Horizonte für die Suche nach tollen Angeboten für Flugtickets zu eröffnen. Es gibt viel fortgeschrittenere Methoden zur Suche nach solchen Angeboten. Ich möchte den Lesern dieses Materials lediglich eine einfache, aber praktische Möglichkeit bieten, dieses Problem zu lösen.

Hier ist der Code, über den wir oben gesprochen haben.

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)

Am Anfang des Codes sehen Sie die Paketimportbefehle, die in unserem gesamten Projekt verwendet werden. Also, randint Wird verwendet, um den Bot für eine zufällige Anzahl von Sekunden „einschlafen“ zu lassen, bevor er einen neuen Suchvorgang startet. Darauf kann in der Regel kein einziger Bot verzichten. Wenn Sie den obigen Code ausführen, wird ein Chrome-Fenster geöffnet, das der Bot für die Arbeit mit Websites verwendet.

Machen wir ein kleines Experiment und öffnen wir die Website Kayak.com in einem separaten Fenster. Wir wählen die Stadt aus, von der aus wir fliegen, und die Stadt, in die wir gelangen möchten, sowie die Flugdaten. Achten Sie bei der Datumsauswahl darauf, dass der Bereich von +-3 Tagen genutzt wird. Ich habe den Code unter Berücksichtigung dessen geschrieben, was die Site als Reaktion auf solche Anfragen produziert. Wenn Sie beispielsweise nur für bestimmte Termine nach Tickets suchen müssen, ist die Wahrscheinlichkeit hoch, dass Sie den Bot-Code ändern müssen. Wenn ich über den Code spreche, gebe ich entsprechende Erklärungen, aber wenn Sie verwirrt sind, lassen Sie es mich wissen.

Klicken Sie nun auf die Suchschaltfläche und sehen Sie sich den Link in der Adressleiste an. Es sollte dem Link ähneln, den ich im folgenden Beispiel verwende, wo die Variable deklariert wird kayak, in dem die URL und die verwendete Methode gespeichert sind get Web-Treiber. Nachdem Sie auf die Suchschaltfläche geklickt haben, sollten Ergebnisse auf der Seite angezeigt werden.

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Als ich den Befehl verwendet habe get Mehr als zwei- oder dreimal innerhalb weniger Minuten wurde ich aufgefordert, die Verifizierung mithilfe von reCaptcha abzuschließen. Sie können diese Prüfung manuell bestehen und weiter experimentieren, bis das System beschließt, eine neue Prüfung durchzuführen. Als ich das Skript getestet habe, schien es, als ob die erste Suchsitzung immer reibungslos verlief. Wenn Sie also mit dem Code experimentieren wollten, müssten Sie den Code nur regelmäßig manuell überprüfen und laufen lassen, wobei Sie lange Intervalle zwischen den Suchsitzungen einhalten müssten. Und wenn Sie darüber nachdenken, ist es unwahrscheinlich, dass eine Person Informationen über Ticketpreise benötigt, die sie alle 10 Minuten zwischen den Suchvorgängen erhält.

Arbeiten mit einer Seite mit XPath

Also haben wir ein Fenster geöffnet und die Seite geladen. Um Preise und andere Informationen zu erhalten, müssen wir XPath-Technologie oder CSS-Selektoren verwenden. Ich habe mich entschieden, bei XPath zu bleiben und hatte nicht das Bedürfnis, CSS-Selektoren zu verwenden, aber es ist durchaus möglich, so zu arbeiten. Das Navigieren auf einer Seite mit XPath kann schwierig sein, selbst wenn Sie die Techniken verwenden, die ich in beschrieben habe diese Beim Lesen des Artikels, bei dem es darum ging, die entsprechenden Bezeichner aus dem Seitencode zu kopieren, wurde mir klar, dass dies tatsächlich nicht die optimale Möglichkeit ist, auf die erforderlichen Elemente zuzugreifen. Übrigens, in diese Das Buch bietet eine hervorragende Beschreibung der Grundlagen der Arbeit mit Seiten mithilfe von XPath- und CSS-Selektoren. So sieht die entsprechende Web-Treibermethode aus.

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Also, lasst uns weiter am Bot arbeiten. Nutzen wir die Möglichkeiten des Programms, um die günstigsten Tickets auszuwählen. Im folgenden Bild ist der XPath-Selektorcode rot hervorgehoben. Um den Code anzuzeigen, müssen Sie mit der rechten Maustaste auf das Seitenelement klicken, an dem Sie interessiert sind, und im angezeigten Menü den Befehl „Inspizieren“ auswählen. Dieser Befehl kann für verschiedene Seitenelemente aufgerufen werden, deren Code im Code Viewer angezeigt und hervorgehoben wird.

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Seitencode anzeigen

Um meine Argumentation zu den Nachteilen des Kopierens von Selektoren aus dem Code zu bestätigen, achten Sie auf die folgenden Merkmale.

Das erhalten Sie, wenn Sie den Code kopieren:

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

Um so etwas zu kopieren, müssen Sie mit der rechten Maustaste auf den Codeabschnitt klicken, der Sie interessiert, und im angezeigten Menü den Befehl „Kopieren > XPath kopieren“ auswählen.

Hier ist, was ich verwendet habe, um die Schaltfläche „Günstigstes“ zu definieren:

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

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Befehl kopieren > XPath kopieren

Es ist ziemlich offensichtlich, dass die zweite Option viel einfacher aussieht. Bei Verwendung wird nach einem Element a gesucht, das das Attribut hat data-codegleich price. Bei Verwendung der ersten Option wird das Element durchsucht id was gleich ist wtKI-price_aTab, und der XPath-Pfad zum Element sieht so aus /div[1]/div/div/div[1]/div/span/span. Eine XPath-Abfrage wie diese an eine Seite reicht aus, aber nur einmal. Das kann ich jetzt sagen id ändert sich beim nächsten Laden der Seite. Zeichenfolge wtKI ändert sich jedes Mal dynamisch, wenn die Seite geladen wird, sodass der Code, der sie verwendet, nach dem nächsten Neuladen der Seite unbrauchbar wird. Nehmen Sie sich also etwas Zeit, um XPath zu verstehen. Dieses Wissen wird Ihnen gute Dienste leisten.

Es sollte jedoch beachtet werden, dass das Kopieren von XPath-Selektoren bei der Arbeit mit relativ einfachen Websites nützlich sein kann, und wenn Sie damit vertraut sind, ist daran nichts auszusetzen.

Lassen Sie uns nun darüber nachdenken, was zu tun ist, wenn Sie alle Suchergebnisse in mehreren Zeilen innerhalb einer Liste erhalten möchten. Sehr einfach. Jedes Ergebnis befindet sich in einem Objekt mit einer Klasse resultWrapper. Das Laden aller Ergebnisse kann in einer Schleife ähnlich der unten gezeigten erfolgen.

Es ist zu beachten, dass Sie den Großteil des Codes, den wir analysieren werden, problemlos verstehen können, wenn Sie das oben Gesagte verstanden haben. Während dieser Code ausgeführt wird, greifen wir über eine Art Pfadangabemechanismus (XPath) auf das zu, was wir benötigen (eigentlich das Element, in das das Ergebnis eingeschlossen ist). Dies geschieht, um den Text des Elements abzurufen und ihn in einem Objekt zu platzieren, aus dem Daten gelesen (zuerst verwendet) werden können flight_containers, Dann - flights_list).

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Die ersten drei Zeilen werden angezeigt und wir können alles, was wir brauchen, deutlich sehen. Wir haben jedoch noch interessantere Möglichkeiten der Informationsbeschaffung. Wir müssen Daten von jedem Element separat erfassen.

Machen Sie sich an die Arbeit!

Der einfachste Weg, eine Funktion zu schreiben, besteht darin, zusätzliche Ergebnisse zu laden. Hier beginnen wir also. Ich möchte die Anzahl der Flüge, über die das Programm Informationen erhält, maximieren, ohne Verdacht im Dienst zu erregen, der zu einer Inspektion führt. Deshalb klicke ich bei jeder Anzeige der Seite einmal auf die Schaltfläche „Weitere Ergebnisse laden“. In diesem Code sollten Sie auf den Block achten try, was ich hinzugefügt habe, weil die Schaltfläche manchmal nicht richtig geladen wird. Wenn dies auch bei Ihnen auftritt, kommentieren Sie Aufrufe dieser Funktion im Funktionscode aus start_kayak, die wir uns weiter unten ansehen werden.

# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
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

Nach einer langen Analyse dieser Funktion (manchmal übertreibe ich es) sind wir nun bereit, eine Funktion zu deklarieren, die die Seite durchsucht.

Das meiste, was benötigt wird, habe ich bereits in der folgenden aufgerufenen Funktion gesammelt page_scrape. Manchmal werden die zurückgegebenen Pfaddaten kombiniert, daher verwende ich eine einfache Methode, um sie zu trennen. Zum Beispiel, wenn ich zum ersten Mal Variablen verwende section_a_list и section_b_list. Unsere Funktion gibt einen Datenrahmen zurück flights_dfDadurch können wir die Ergebnisse verschiedener Datensortiermethoden trennen und später kombinieren.

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

Ich habe versucht, die Variablen so zu benennen, dass der Code verständlich ist. Denken Sie daran, dass Variablen beginnen mit a gehören zur ersten Etappe des Weges und b - auf die Sekunde genau. Kommen wir zur nächsten Funktion.

Unterstützungsmechanismen

Wir haben jetzt eine Funktion, die es uns ermöglicht, zusätzliche Suchergebnisse zu laden, und eine Funktion, um diese Ergebnisse zu verarbeiten. Dieser Artikel hätte hier enden können, da diese beiden Funktionen alles bieten, was Sie zum Scrapen von Seiten benötigen, die Sie selbst öffnen können. Einige der oben diskutierten Hilfsmechanismen haben wir jedoch noch nicht berücksichtigt. Dies ist beispielsweise der Code zum Versenden von E-Mails und einigen anderen Dingen. All dies finden Sie in der Funktion start_kayak, was wir nun betrachten werden.

Für diese Funktion sind Informationen zu Städten und Daten erforderlich. Anhand dieser Informationen bildet es eine Verknüpfung in einer Variablen kayak, mit dem Sie zu einer Seite weitergeleitet werden, die Suchergebnisse enthält, die nach der besten Übereinstimmung mit der Suchanfrage sortiert sind. Nach der ersten Scraping-Sitzung arbeiten wir mit den Preisen in der Tabelle oben auf der Seite. Wir ermitteln nämlich den Mindestticketpreis und den Durchschnittspreis. All dies wird zusammen mit der von der Website ausgegebenen Vorhersage per E-Mail gesendet. Auf der Seite sollte sich die entsprechende Tabelle in der oberen linken Ecke befinden. Bei der Arbeit mit dieser Tabelle kann es übrigens bei der Suche nach genauen Daten zu einem Fehler kommen, da die Tabelle in diesem Fall nicht auf der Seite angezeigt wird.

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

Ich habe dieses Skript mit einem Outlook-Konto (hotmail.com) getestet. Ich habe nicht getestet, ob es mit einem Gmail-Konto richtig funktioniert. Dieses E-Mail-System ist recht beliebt, aber es gibt viele mögliche Optionen. Wenn Sie ein Hotmail-Konto verwenden, müssen Sie nur Ihre Daten in den Code eingeben, damit alles funktioniert.

Wenn Sie verstehen möchten, was genau in bestimmten Abschnitten des Codes für diese Funktion geschieht, können Sie diese kopieren und damit experimentieren. Das Experimentieren mit dem Code ist die einzige Möglichkeit, ihn wirklich zu verstehen.

Fertiges System

Nachdem wir nun alles erledigt haben, worüber wir gesprochen haben, können wir eine einfache Schleife erstellen, die unsere Funktionen aufruft. Das Skript fordert vom Benutzer Daten zu Städten und Daten an. Beim Testen mit ständigem Neustart des Skripts möchten Sie diese Daten wahrscheinlich nicht jedes Mal manuell eingeben. Daher können Sie die entsprechenden Zeilen für die Dauer des Tests auskommentieren, indem Sie die darunter liegenden Zeilen, in denen die von der benötigten Daten enthalten sind, auskommentieren Das Skript ist fest codiert.

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

So sieht ein Testlauf des Skripts aus.
Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber
Testlauf des Skripts

Ergebnisse

Wenn Sie es bis hierher geschafft haben, herzlichen Glückwunsch! Sie haben jetzt einen funktionierenden Web-Scraper, obwohl ich bereits viele Möglichkeiten zur Verbesserung sehe. Es kann beispielsweise in Twilio integriert werden, sodass Textnachrichten anstelle von E-Mails gesendet werden. Sie können ein VPN oder etwas anderes verwenden, um gleichzeitig Ergebnisse von mehreren Servern zu empfangen. Es gibt auch ein periodisch auftretendes Problem bei der Überprüfung des Site-Benutzers, um festzustellen, ob es sich um eine Person handelt, aber auch dieses Problem kann gelöst werden. Auf jeden Fall haben Sie jetzt eine Basis, die Sie bei Bedarf erweitern können. Stellen Sie beispielsweise sicher, dass eine Excel-Datei als Anhang einer E-Mail an den Benutzer gesendet wird.

Python – ein Assistent bei der Suche nach günstigen Flugtickets für Reiseliebhaber

An der Umfrage können nur registrierte Benutzer teilnehmen. Einloggenbitte.

Nutzen Sie Web-Scraping-Technologien?

  • Ja

  • Nein

8 Benutzer haben abgestimmt. 1 Nutzer enthielten sich der Stimme.

Source: habr.com

Kommentar hinzufügen