Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.

Die Autorin des Artikels, dessen Übersetzung wir heute veröffentlichen, erklärt, dass es ihr Ziel ist, über die Entwicklung eines Web-Scrapers in Python mit Selenium zu berichten, der die Preise für Flugtickets sucht. Bei der Suche nach Tickets werden flexible Daten (+- 3 Tage um die angegebenen Daten) verwendet. Der Scraper speichert die Suchergebnisse in einer Excel-Datei und sendet eine E-Mail an denjenigen, der ihn gestartet hat, mit allgemeinen Informationen darüber, was er gefunden hat. Ziel dieses Projekts ist es, Reisenden bei der Suche nach den günstigsten Angeboten zu helfen.

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.

Wenn Sie beim Durcharbeiten des Materials das Gefühl haben, verloren zu gehen - schauen Sie sich Maschine verwendet. Artikel an.

Was suchen wir?

Sie sind frei, das hier beschriebene System nach Belieben zu nutzen. Ich habe es zum Beispiel verwendet, um Wochenendtouren und Tickets in meine Heimatstadt zu suchen. Wenn Sie ernsthaft auf der Suche nach günstigen Tickets sind, können Sie das Skript auf einem Server (ein einfacher Server, für 130 Rubel pro Monat, eignet sich dafür gut) einrichten und es so konfigurieren, dass es ein- bis zweimal am Tag ausgeführt wird. Die Suchergebnisse werden Ihnen per E-Mail zugeschickt. Außerdem empfehle ich, alles so einzurichten, dass das Skript eine Excel-Datei mit den Suchergebnissen in einem Dropbox-Ordner speichert, damit Sie solche Dateien von überall und jederzeit einsehen können.

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.
Ich habe noch keine Tarife mit Fehlern gefunden, aber ich denke, das ist möglich.

Wie bereits erwähnt, wird bei der Suche das 'flexible Datum' verwendet, das Skript findet Angebote, die innerhalb von drei Tagen der angegebenen Daten liegen. Obwohl bei der Ausführung des Skripts nur Angebote für eine Richtung gesucht werden, ist es nicht schwierig, es so zu ändern, dass es Daten für mehrere Flugrichtungen sammeln kann. Mit seiner Hilfe können sogar fehlerhafte Tarife gesucht werden; solche Funde können sehr interessant sein.

Warum wird ein weiterer Web-Scraper benötigt?

Als ich zum ersten Mal mit Web-Scraping begann, fand ich es ehrlich gesagt nicht besonders interessant. Ich wollte mehr Projekte im Bereich der Vorhersagemodellierung, Finanzanalyse und vielleicht im Bereich der Sentiment-Analyse von Texten machen. Aber ich stellte fest, dass es wirklich faszinierend ist, herauszufinden, wie man ein Programm erstellt, das Daten von Websites sammelt. Je mehr ich mich mit dem Thema beschäftigte, desto mehr wurde mir klar, dass Web-Scraping der 'Motor' des Internets ist.

Vielleicht denken Sie, dass das eine zu gewagte Behauptung ist. Aber denken Sie daran, dass Google mit einem Web-Scraper begann, den Larry Page mit Java und Python erstellt hat. Die Google-Roboter durchforsten das Internet, um ihren Nutzern die besten Antworten auf ihre Fragen zu liefern. Web-Scraping hat unzählige Anwendungsmöglichkeiten, und selbst wenn Sie im Bereich Data Science an etwas anderem interessiert sind, werden Sie einige Scraping-Fähigkeiten benötigen, um Daten für Ihre Analysen zu erhalten.

Einige der hier verwendeten Techniken fand ich in einem wunderbaren Buch über Web-Scraping, das ich kürzlich erworben habe. Darin gibt es viele einfache Beispiele und Ideen für die praktische Anwendung des Erlernten. Zudem gibt es ein sehr interessantes Kapitel über das Umgehen von reCaptcha-Überprüfungen. Das war für mich neu, da ich nicht wusste, dass es spezielle Werkzeuge und sogar ganze Dienste für solche Aufgaben gibt.

Lieben Sie es zu reisen?!

Auf die einfache und recht harmlos scheinende Frage, die in diesem Abschnitt als Überschrift steht, erhält man oft eine positive Antwort, begleitet von ein paar Reisegeschichten des Befragten. Die meisten von uns sind sich einig, dass Reisen eine großartige Möglichkeit ist, in neue Kulturen einzutauchen und den eigenen Horizont zu erweitern. Wenn man jedoch jemanden fragt, ob er gerne nach Flugtickets sucht, bin ich mir sicher, dass die Antwort darauf nicht so positiv ausfallen wird. Tatsächlich kommt uns hier Python zur Hilfe.

Die erste Aufgabe, die wir beim Aufbau eines Systems zur Suche nach Informationen über Flugtickets lösen müssen, besteht darin, eine geeignete Plattform auszuwählen, von der wir Informationen beziehen. Diese Entscheidung fiel mir nicht leicht, aber letztendlich wählte ich den Dienst Kayak. Ich habe auch die Dienste Momondo, Skyscanner, Expedia und einige andere ausprobiert, jedoch waren die Schutzmechanismen gegen Bots auf diesen Plattformen unüberwindbar. Nach mehreren Versuchen, bei denen ich versuchte, die Systeme davon zu überzeugen, dass ich ein Mensch bin, war ich mit Ampeln, Fußgängerüberwegen und Fahrrädern konfrontiert. Ich entschied mich für Kayak, auch wenn hier, wenn man in kurzer Zeit zu viele Seiten lädt, ebenfalls Prüfungen beginnen. Es gelang mir, den Bot so zu programmieren, dass er Anfragen an die Website in Intervallen von 4 bis 6 Stunden sendet, und alles funktioniert gut. Gelegentlich treten Schwierigkeiten bei der Arbeit mit Kayak auf, aber wenn Sie mit Prüfungen belästigt werden, müssen Sie entweder manuell damit umgehen und dann den Bot starten oder einige Stunden warten, bis die Prüfungen aufhören. Wenn notwendig, können Sie den Code problemlos für eine andere Plattform anpassen, und wenn Sie das tun, können Sie dies in den Kommentaren mitteilen.

Wenn Sie gerade erst mit Web Scraping beginnen und nicht wissen, warum einige Websites alles daran setzen, dies zu verhindern, dann tun Sie sich einen Gefallen und suchen Sie vor Ihrem ersten Projekt in diesem Bereich bei Google nach Materialien zu den Stichworten „Web Scraping-Etikette“. Ihre Experimente könnten schneller enden, als Sie denken, wenn Sie beim Web Scraping unvernünftig vorgehen.

Erste Schritte

Hier ist ein allgemeiner Überblick über das, was im Code unseres Web Scrapers geschehen wird:

  • Import der benötigten Bibliotheken.
  • Öffnen eines Tabs in Google Chrome.
  • Aufruf einer Funktion, die den Bot startet und ihm die Städte und Daten übergibt, die bei der Ticket Suche verwendet werden.
  • Diese Funktion erhält die ersten Suchergebnisse, die nach dem Kriterium der Attraktivität (best) sortiert sind, und klickt auf die Schaltfläche, um zusätzliche Ergebnisse zu laden.
  • Eine weitere Funktion sammelt Daten von der gesamten Seite und gibt ein Datenrahmen zurück.
  • Die beiden vorherigen Schritte werden unter Verwendung von Sortierung nach Ticketpreis (cheap) und nach Fluggeschwindigkeit (fastest) durchgeführt.
  • Dem Benutzer des Skripts wird eine E-Mail mit einer kurzen Zusammenfassung der Ticketpreise (die günstigsten Tickets und der Durchschnittspreis) gesendet, während die Daten in einem Excel-Dokument gespeichert werden, das nach den drei oben genannten Indikatoren sortiert ist.
  • Alle oben beschriebenen Aktionen werden in einer Schleife über einen festgelegten Zeitraum ausgeführt.

Es ist zu beachten, dass jedes Selenium-Projekt mit dem Webdriver beginnt. Ich verwende Chromedriver, arbeite mit Google Chrome, es gibt jedoch auch andere Optionen. Beliebt sind auch PhantomJS und Firefox. Nach dem Herunterladen des Drivers muss er in den entsprechenden Ordner verschoben werden; damit ist die Vorbereitung für seine Verwendung abgeschlossen. In den ersten Zeilen unseres Skripts wird ein neuer Tab in Chrome geöffnet.

Bitte beachten Sie, dass ich in meiner Erzählung nicht versuche, neue Horizonte bei der Suche nach günstigen Flugangeboten zu eröffnen. Es gibt viel ausgeklügeltere Methoden zur Suche solcher Angebote. Ich möchte den Lesern dieses Materials lediglich eine einfache, aber praxisnahe Möglichkeit zur Lösung dieser Aufgabe anbieten.

Hier ist der Code, über den wir zuvor 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

# Verwenden Sie hier Ihren Pfad zum Chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # Dieser Befehl öffnet das Fenster von Chrome
sleep(2)

Zu Beginn des Codes sind die Importbefehle für die Pakete zu sehen, die in unserem gesamten Projekt verwendet werden. So wird randint verwendet, um den Bot für eine zufällige Anzahl von Sekunden "schlafen" zu lassen, bevor er mit einer neuen Suchoperation beginnt. Normalerweise kommt kein Bot ohne dies aus. Wenn Sie den oben genannten Code ausführen, wird ein Chrome-Fenster geöffnet, das der Bot zur Bearbeitung von Websites verwenden wird.

Lassen Sie uns ein kleines Experiment durchführen und die Website kayak.com in einem separaten Fenster öffnen. Wählen Sie die Stadt, aus der Sie fliegen möchten, und die Stadt, in die Sie reisen möchten, sowie die Flugdaten. Achten Sie beim Auswählen der Daten darauf, dass ein Bereich von +-3 Tagen verwendet wird. Ich habe den Code so geschrieben, dass er das ausgibt, was die Website auf solche Anfragen antwortet. Wenn Sie zum Beispiel nur nach Tickets an bestimmten Daten suchen müssen, besteht eine hohe Wahrscheinlichkeit, dass Sie den Bot-Code anpassen müssen. Während ich den Code erkläre, mache ich entsprechende Erläuterungen, aber wenn Sie das Gefühl haben, dass Sie den Überblick verlieren — lassen Sie es mich wissen.

Jetzt klicken wir auf die Schaltfläche zum Starten der Suche und schauen uns den Link in der Adresszeile an. Er sollte ähnlich aussehen wie der Link, den ich im Beispiel unten verwende, wo die Variable kayak, die die URL speichert, und die Methode get des Webdrivers verwendet wird. Nach dem Klicken auf die Suchschaltfläche sollten die Ergebnisse auf der Seite erscheinen.

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.
Als ich den Befehl get mehr als zwei- oder dreimal innerhalb weniger Minuten verwendet habe, wurde ich gebeten, einen reCaptcha-Test durchzuführen. Diese Überprüfung kann manuell durchgeführt werden, und die Experimente können fortgesetzt werden, bis das System beschließt, einen neuen Test durchzuführen. Als ich das Skript testete, hatte ich das Gefühl, dass die erste Suchsitzung immer problemlos verläuft, sodass Sie, wenn Sie mit dem Code experimentieren möchten, nur gelegentlich manuell den Test bestehen und den Code mit längeren Pausen zwischen den Suchsitzungen ausführen lassen müssen. Und wenn man darüber nachdenkt, wird eine Person wahrscheinlich keine Preisinformationen über Tickets benötigen, die mit 10 Minuten Abstand zwischen den Suchvorgängen gesammelt werden.

Arbeiten mit der Seite unter Verwendung von XPath

Also, wir haben das Fenster geöffnet und die Website geladen. Um Informationen über Preise und andere Details zu erhalten, müssen wir die Technologie XPath oder CSS-Selektoren verwenden. Ich habe mich für XPath entschieden und fühlte nicht die Notwendigkeit, CSS-Selektoren anzuwenden, aber es ist auch möglich, so zu arbeiten. Die Navigation auf der Seite mithilfe von XPath kann schwierig sein, selbst wenn Sie die Methoden anwenden, die ich beschrieben habe. dieser In dem Artikel, in dem entsprechende IDs aus dem Quellcode der Seite kopiert wurden, wurde mir klar, dass dies tatsächlich keine optimale Methode ist, um an die benötigten Elemente zu gelangen. Übrigens findet man in dieser dem Buch eine hervorragende Beschreibung der Grundlagen der Arbeit mit Seiten unter Verwendung von XPath und CSS-Selektoren. So sieht die entsprechende Methode des Webdrivers aus.

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.
Also, wir setzen die Arbeit am Bot fort. Wir werden die Möglichkeiten des Programms nutzen, um die günstigsten Tickets auszuwählen. Auf dem nächsten Bild ist der XPath-Selektor rot hervorgehoben. Um den Code anzuzeigen, muss man mit der rechten Maustaste auf das gewünschte Element der Seite klicken und im sich öffnenden Menü die Option Inspect auswählen. Diese Option kann für verschiedene Elemente der Seite aufgerufen werden, deren Code dann im Codeansicht-Fenster angezeigt und hervorgehoben wird.

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.
Seitenquellcode anzeigen

Um eine Bestätigung meiner Überlegungen zu den Nachteilen des Kopierens von Selektoren aus dem Code zu finden, achten Sie auf die folgenden Besonderheiten.

Das ergibt sich aus dem Kopieren des Codes:

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

Um etwas Ähnliches zu kopieren, müssen Sie mit der rechten Maustaste auf den gewünschten Codeabschnitt klicken und im sich öffnenden Menü die Option Copy > Copy XPath wählen.

Das habe ich verwendet, um die Schaltfläche Cheapest zu bestimmen:

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

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.
Befehl Copy > Copy XPath

Es ist ganz offensichtlich, dass die zweite Variante viel einfacher aussieht. Bei ihrer Verwendung wird ein Element a gesucht, das das Attribut data-codehat, das gleich ist PreisBeim ersten Ansatz wird ein Element gesucht, dessen id Wert gleich ist wtKI-price_aTab, wobei der XPath-Pfad zum Element wie folgt aussieht /div[1]/div/div/div[1]/div/span/span. Eine solche XPath-Anfrage an die Seite wird ihre Aufgabe erfüllen, aber nur einmal. Ich kann jetzt schon sagen, dass id sich bei der nächsten Seite neu laden wird. Die Zeichenfolge wtKI ändert sich dynamisch bei jedem Seitenaufruf, sodass der Code, in dem sie verwendet wird, nach dem nächsten Neuladen der Seite unbrauchbar sein wird. Daher ist es ratsam, sich etwas Zeit zu nehmen, um sich mit XPath vertraut zu machen. Dieses Wissen wird Ihnen gute Dienste leisten.

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

Lassen Sie uns nun darüber nachdenken, wie man alle Suchergebnisse in mehreren Zeilen innerhalb einer Liste erhält. Das ist ganz einfach. Jedes Ergebnis befindet sich innerhalb eines Objekts mit der Klasse resultWrapper. Das Laden aller Ergebnisse kann in einer Schleife erfolgen, die der unten gezeigten ähnelt.

Es ist zu beachten, dass Sie, wenn das oben Gesagte für Sie klar ist, die meisten Codes, die wir durchgehen werden, problemlos verstehen sollten. Im Verlauf der Ausführung dieses Codes greifen wir auf das, was wir benötigen (tatsächlich ist es das Element, in dem das Ergebnis eingewickelt ist), mit einem bestimmten Mechanismus zum Angeben des Pfades (XPath) zu. Dies geschieht, um den Text des Elements zu extrahieren und in ein Objekt zu speichern, aus dem Daten gelesen werden können (zuerst wird flight_containers, dann wird flights_list).

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.
ausgegeben. Die ersten drei Zeilen werden angezeigt und wir können klar sehen, was wir benötigen. Allerdings gibt es auch interessantere Möglichkeiten, Informationen zu erhalten. Wir müssen die Daten aus jedem Element einzeln entnehmen.

Legen wir los!

Es ist am einfachsten, eine Funktion zum Laden zusätzlicher Ergebnisse zu schreiben, also fangen wir damit an. Ich möchte die Anzahl der Flüge maximieren, deren Informationen das Programm erhält, und gleichzeitig keinen Verdacht bei dem Dienst erregen, der zu einer Überprüfung führt, daher klicke ich jedes Mal, wenn die Seite angezeigt wird, einmal auf die Schaltfläche ‚Weitere Ergebnisse laden‘. In diesem Code ist es wichtig, auf den Block try, den ich hinzugefügt habe, zu achten, da die Schaltfläche manchmal nicht richtig lädt. Wenn Sie ebenfalls damit konfrontiert werden, kommentieren Sie die Aufrufe dieser Funktion im Code der Funktion start_kayak, die wir gleich betrachten 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

Jetzt, nach einer langen Analyse dieser Funktion (manchmal kann ich mich da verlieren), sind wir bereit, eine Funktion zu deklarieren, die sich mit dem Scraping der Seite beschäftigt.

Ich habe bereits den Großteil dessen gesammelt, was nötig ist, in der folgenden Funktion, die page_scrapegenannt wird. Manchmal werden die zurückgegebenen Daten zu den Etappen des Pfades vereint, um sie zu trennen, verwende ich eine einfache Methode. Zum Beispiel, wenn ich beim ersten Mal die Variablen section_a_list und section_b_list. Unsere Funktion gibt einen DataFrame zurück flights_df, sodass wir die Ergebnisse, die mit verschiedenen Methoden zur Datensortierung erhalten wurden, trennen und später wieder zusammenführen können.

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, die mit a zum ersten Schritt des Prozesses gehören, während b zum zweiten Schritt gehört. Lassen Sie uns zur nächsten Funktion übergehen.

Hilfsmechanismen

Jetzt haben wir eine Funktion, die es ermöglicht, weitere Suchergebnisse zu laden, und eine Funktion zur Verarbeitung dieser Ergebnisse. An dieser Stelle könnte man den Artikel beenden, da diese beiden Funktionen alles Notwendige zum Scraping von Seiten bieten, die selbst geöffnet werden können. Allerdings haben wir einige Hilfsmechanismen, über die wir zuvor gesprochen haben, noch nicht betrachtet. Zum Beispiel handelt es sich um den Code zum Versenden von E-Mails und einige andere Dinge. All dies finden wir in der Funktion start_kayak, die wir jetzt betrachten werden.

Für die Funktion sind Informationen über Städte und Daten erforderlich. Sie erstellt mithilfe dieser Informationen einen Link in der Variablen kayak, der verwendet wird, um zur Seite zu gelangen, auf der sich die Ergebnisse der Suche befinden, sortiert nach ihrer besten Übereinstimmung mit der Anfrage. Nach der ersten Scraping-Sitzung werden wir mit den Preisen arbeiten, die sich in der Tabelle im oberen Teil der Seite befinden. Genauer gesagt, wir werden den minimalen Ticketpreis und den durchschnittlichen Preis ermitteln. All dies, zusammen mit der Vorhersage, die von der Website bereitgestellt wird, wird per E-Mail gesendet. Auf der Seite sollte die entsprechende Tabelle in der oberen linken Ecke sein. Das Arbeiten mit dieser Tabelle kann übrigens zu Fehlern bei der Suche mit genauen Daten führen, da in diesem Fall die Tabelle auf der Seite nicht angezeigt wird.

def start_kayak(city_from, city_to, date_start, date_end):
    """Stadtcodes - es sind die IATA-Codes!
    Datumsformat - 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))
    
    # Manchmal erscheint ein Pop-up, um darauf zu prüfen und es zu schließen, kann man den try-Block verwenden
    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('lädt mehr.....')
    
#     load_more()
    
    print('Starte den ersten Scraping-Vorgang.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Nimm den niedrigsten Preis aus der Tabelle, die sich oben auf der Seite befindet
    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('Wechsel zu den günstigsten Ergebnissen.....')
    cheap_results = '//a[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('lädt mehr.....')
    
#     load_more()
    
    print('Starte den zweiten Scraping-Vorgang.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('Wechsel zu den schnellsten Ergebnissen.....')
    quick_results = '//a[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('lädt mehr.....')
    
#     load_more()
    
    print('Starte den dritten Scraping-Vorgang.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Speichern des neuen DataFrames in eine Excel-Datei, deren Name die Städte und Daten widerspiegelt
    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('DataFrame gespeichert.....')
    
    # Man kann beobachten, wie die Vorhersage der Website mit der Realität übereinstimmt
    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)
    
    # Manchmal ist in der Variablen loading diese Zeile, die später Probleme beim Versenden der E-Mail verursacht
    # Wenn dies geschieht - ändern wir es in "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Nicht sicher'
    
    username = 'YOUREMAIL@hotmail.com'
    password = 'YOUR PASSWORD'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Betreff: Flug-Scraper
Günstigster Flug: {}nDurchschnittspreis: {}nnEmpfehlung: {}nnEnde der Nachricht'.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('E-Mail gesendet.....')

Ich habe dieses Skript mit einem Outlook-Konto (hotmail.com) getestet. Ich habe nicht überprüft, wie es mit einem Gmail-Konto funktioniert, da dieses E-Mail-System sehr beliebt ist, aber es gibt viele mögliche Varianten. Wenn Sie jedoch 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 dieser Funktion durchgeführt wird, können Sie sie kopieren und mit ihnen experimentieren. Experimente mit dem Code sind der einzige Weg, ihn wirklich zu verstehen.

Fertiges System

Jetzt, wo alles, worüber wir gesprochen haben, erledigt ist, können wir eine einfache Schleife erstellen, in der unsere Funktionen aufgerufen werden. Das Skript fragt den Benutzer nach Städten und Daten. Bei wiederholtem Testen des Skripts möchten Sie wahrscheinlich nicht jedes Mal diese Daten manuell eingeben, daher können die entsprechenden Zeilen während des Testens auskommentiert werden, indem Sie die darunterstehenden Zeilen, in denen die benötigten Daten hartkodiert sind, wieder einkommentieren.

city_from = input('Von welcher Stadt? ')
city_to = input('Wohin? ')
date_start = input('Um welches Abreisedatum soll gesucht werden? Bitte nur das Format JJJJ-MM-TT verwenden ')
date_end = input('Wann zurück? Bitte nur das Format JJJJ-MM-TT verwenden ')

# 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 {} wurde @ {} abgeschlossen'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Warten Sie 4 Stunden
    sleep(60*60*4)
    print('Wartezeit beendet.....')

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

Ergebnisse

Wenn Sie bis zu diesem Punkt gekommen sind, herzlichen Glückwunsch! Jetzt haben Sie einen funktionierenden Web-Scraper, obwohl ich bereits viele Möglichkeiten zur Verbesserung sehe. Zum Beispiel könnte man ihn mit Twilio integrieren, damit er anstelle von E-Mails Textnachrichten sendet. Man könnte ein VPN oder etwas Ähnliches verwenden, um gleichzeitig Ergebnisse von mehreren Servern zu erhalten. Es gibt außerdem das gelegentlich auftretende Problem, dass die Website den Benutzer überprüft, ob er ein Mensch ist, aber auch dieses Problem kann gelöst werden. Auf jeden Fall haben Sie jetzt eine Basis, die Sie nach Belieben erweitern können. Zum Beispiel könnte die Excel-Datei als Anhang in einer E-Mail an den Benutzer gesendet werden.

Python – ein Helfer bei der Suche nach günstigen Flugtickets für Reisebegeisterte.

Nur registrierte Benutzer können an der Umfrage teilnehmen. Bitte einloggen.

Nutzen Sie Web-Scraping-Technologien?

  • Ja

  • Nein

8 Benutzer haben abgestimmt. 1 Benutzer hat sich enthalten.

Quelle: habr.com

60GB SSD 8Gb DDR4