Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen

De auteur van het artikel, waarvan we de vertaling vandaag publiceren, zegt dat het doel is om te praten over de ontwikkeling van een webschraper in Python met behulp van Selenium, die zoekt naar vliegticketprijzen. Bij het zoeken naar tickets wordt gebruik gemaakt van flexibele data (+- 3 dagen ten opzichte van de opgegeven data). De scraper slaat de zoekresultaten op in een Excel-bestand en stuurt de persoon die de zoekopdracht heeft uitgevoerd een e-mail met een samenvatting van wat hij of zij heeft gevonden. Het doel van dit project is om reizigers te helpen de beste deals te vinden.

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen

Als u zich, terwijl u de stof begrijpt, verloren voelt, kijk er dan eens naar deze artikel.

Wat zoeken we?

U bent vrij om het hier beschreven systeem te gebruiken zoals u dat wilt. Ik gebruikte het bijvoorbeeld om te zoeken naar weekendtours en kaartjes voor mijn geboorteplaats. Als u serieus op zoek bent naar winstgevende tickets, kunt u het script op de server uitvoeren (eenvoudig server, voor 130 roebel per maand, is hiervoor redelijk geschikt) en zorg ervoor dat het een of twee keer per dag draait. De zoekresultaten worden per e-mail naar u verzonden. Daarnaast raad ik aan om alles zo in te stellen dat het script een Excel-bestand met zoekresultaten opslaat in een Dropbox-map, waardoor je dergelijke bestanden overal en altijd kunt bekijken.

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
Ik heb nog geen tarieven met fouten gevonden, maar ik denk dat het mogelijk is

Bij het zoeken wordt, zoals reeds vermeld, gebruik gemaakt van een “flexibele datum”; het script vindt aanbiedingen die binnen drie dagen na de opgegeven data liggen. Hoewel het script bij het uitvoeren van het script slechts in één richting naar aanbiedingen zoekt, is het eenvoudig aan te passen zodat het gegevens over verschillende vliegrichtingen kan verzamelen. Met zijn hulp kun je zelfs zoeken naar foutieve tarieven; dergelijke vondsten kunnen erg interessant zijn.

Waarom heb je nog een webschraper nodig?

Toen ik voor het eerst met webscrapen begon, was ik er eerlijk gezegd niet bijzonder in geïnteresseerd. Ik wilde meer projecten doen op het gebied van voorspellende modellering, financiële analyse en mogelijk op het gebied van het analyseren van de emotionele inkleuring van teksten. Maar het bleek heel interessant om erachter te komen hoe je een programma kunt maken dat gegevens van websites verzamelt. Toen ik me in dit onderwerp verdiepte, realiseerde ik me dat webscraping de ‘motor’ van internet is.

Misschien vindt u dit een te gewaagde uitspraak. Maar bedenk dat Google begon met een webschraper die Larry Page maakte met behulp van Java en Python. Google-robots hebben het internet verkend en geprobeerd gebruikers de beste antwoorden op hun vragen te bieden. Webscraping heeft eindeloze toepassingen, en zelfs als je geïnteresseerd bent in iets anders in Data Science, zul je wat scrapingvaardigheden nodig hebben om de gegevens te krijgen die je nodig hebt om te analyseren.

Ik vond enkele van de hier gebruikte technieken prachtig het boek over webscraping, dat ik onlangs heb aangeschaft. Het bevat veel eenvoudige voorbeelden en ideeën voor praktische toepassing van wat u hebt geleerd. Daarnaast is er een heel interessant hoofdstuk over het omzeilen van reCaptcha-controles. Dit kwam voor mij als nieuws, omdat ik niet eens wist dat er speciale tools en zelfs hele services bestonden om dergelijke problemen op te lossen.

Vind je het leuk om te reizen?!

Op de eenvoudige en tamelijk onschuldige vraag die in de titel van dit gedeelte wordt gesteld, kun je vaak een positief antwoord horen, vergezeld van een paar verhalen over de reizen van de persoon aan wie de vraag werd gesteld. De meesten van ons zijn het erover eens dat reizen een geweldige manier is om jezelf onder te dompelen in nieuwe culturele omgevingen en je horizon te verbreden. Als je echter aan iemand vraagt ​​of hij het leuk vindt om naar vliegtickets te zoeken, weet ik zeker dat het antwoord niet zo positief zal zijn. Python komt ons hier trouwens te hulp.

De eerste taak die we moeten oplossen op weg naar het creëren van een systeem voor het zoeken naar informatie over vliegtickets zal het selecteren van een geschikt platform zijn waar we informatie uit zullen halen. Het oplossen van dit probleem was voor mij niet eenvoudig, maar uiteindelijk heb ik voor de Kayak-service gekozen. Ik heb de diensten van Momondo, Skyscanner, Expedia en een paar anderen geprobeerd, maar de robotbeschermingsmechanismen op deze bronnen waren ondoordringbaar. Na verschillende pogingen, waarbij ik te maken kreeg met verkeerslichten, oversteekplaatsen voor voetgangers en fietsen, in een poging de systemen ervan te overtuigen dat ik een mens was, besloot ik dat Kayak het meest geschikt voor mij was, ondanks het feit dat zelfs als er te veel pagina's zijn geladen in korte tijd, en ook de controles beginnen. Het lukte me om de bot met tussenpozen van 4 tot 6 uur verzoeken naar de site te laten sturen, en alles werkte prima. Van tijd tot tijd doen zich problemen voor bij het werken met Kayak, maar als ze je lastigvallen met cheques, moet je ze handmatig afhandelen en vervolgens de bot starten, of een paar uur wachten en de controles moeten stoppen. Indien nodig kun je de code eenvoudig aanpassen voor een ander platform en als je dat toch doet, kun je dit melden in de reacties.

Als u net begint met webscrapen en niet weet waarom sommige websites ermee worstelen, doe uzelf dan een plezier en voer een Google-zoekopdracht uit op de woorden "webscraping-etiquette" voordat u aan uw eerste project op dit gebied begint. . Uw experimenten kunnen eerder eindigen dan u denkt als u onverstandig aan webscrapen doet.

Aan de slag

Hier is een algemeen overzicht van wat er zal gebeuren in onze webschrapercode:

  • Importeer de vereiste bibliotheken.
  • Een Google Chrome-tabblad openen.
  • Roep een functie op die de bot start en de steden en datums doorgeeft die zullen worden gebruikt bij het zoeken naar tickets.
  • Deze functie neemt de eerste zoekresultaten, gesorteerd op beste, en klikt op een knop om meer resultaten te laden.
  • Een andere functie verzamelt gegevens van de hele pagina en retourneert een gegevensframe.
  • De twee voorgaande stappen worden uitgevoerd met sortering op ticketprijs (goedkoop) en op vluchtsnelheid (snelste).
  • De gebruiker van het script krijgt een e-mail met een samenvatting van de ticketprijzen (goedkoopste tickets en gemiddelde prijs), en een dataframe met informatie gesorteerd op de drie bovengenoemde indicatoren wordt opgeslagen als een Excel-bestand.
  • Alle bovenstaande acties worden na een bepaalde tijdsperiode in een cyclus uitgevoerd.

Opgemerkt moet worden dat elk Selenium-project begint met een webstuurprogramma. ik gebruik Chrome-stuurprogrammaIk werk met Google Chrome, maar er zijn ook andere mogelijkheden. PhantomJS en Firefox zijn ook populair. Nadat u het stuurprogramma hebt gedownload, moet u het in de juiste map plaatsen, waarmee de voorbereiding voor het gebruik ervan is voltooid. De eerste regels van ons script openen een nieuw Chrome-tabblad.

Houd er rekening mee dat ik in mijn verhaal niet probeer nieuwe horizonten te openen voor het vinden van geweldige aanbiedingen voor vliegtickets. Er zijn veel geavanceerdere methoden om naar dergelijke aanbiedingen te zoeken. Ik wil lezers van dit materiaal alleen een eenvoudige maar praktische manier bieden om dit probleem op te lossen.

Hier is de code waar we het hierboven over hadden.

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)

Aan het begin van de code ziet u de pakketimportopdrachten die tijdens ons project worden gebruikt. Dus, randint wordt gebruikt om de bot een willekeurig aantal seconden ‘in slaap te laten vallen’ voordat hij een nieuwe zoekactie start. Meestal kan geen enkele bot zonder. Als u de bovenstaande code uitvoert, wordt er een Chrome-venster geopend, dat de bot zal gebruiken om met sites te werken.

Laten we een klein experiment doen en de website Kayak.com in een apart venster openen. We selecteren de stad van waaruit we gaan vliegen, de stad waar we naartoe willen, evenals de vluchtdata. Zorg er bij het kiezen van data voor dat het bereik van +-3 dagen wordt gebruikt. Ik heb de code geschreven, rekening houdend met wat de site produceert als reactie op dergelijke verzoeken. Als u bijvoorbeeld alleen naar tickets voor bepaalde data moet zoeken, is de kans groot dat u de botcode moet wijzigen. Als ik over de code praat, geef ik passende uitleg, maar als je je in de war voelt, laat het me dan weten.

Klik nu op de zoekknop en kijk naar de link in de adresbalk. Het zou vergelijkbaar moeten zijn met de link die ik gebruik in het onderstaande voorbeeld waar de variabele wordt gedeclareerd kayak, waarin de URL wordt opgeslagen, en de methode wordt gebruikt get webstuurprogramma. Nadat u op de zoekknop heeft geklikt, zouden de resultaten op de pagina moeten verschijnen.

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
Toen ik het commando gebruikte get meer dan twee of drie keer binnen een paar minuten werd mij gevraagd de verificatie te voltooien met reCaptcha. U kunt deze controle handmatig doorstaan ​​en doorgaan met experimenteren totdat het systeem besluit een nieuwe controle uit te voeren. Toen ik het script testte, leek het alsof de eerste zoeksessie altijd soepel verliep, dus als je met de code wilde experimenteren, hoefde je de code alleen periodiek handmatig te controleren en te laten uitvoeren, met lange intervallen tussen zoeksessies. En als je erover nadenkt, is het onwaarschijnlijk dat iemand informatie nodig heeft over de ticketprijzen die hij met tussenpozen van 10 minuten tussen zoekbewerkingen ontvangt.

Werken met een pagina met behulp van XPath

Dus openden we een venster en laadden de site. Om prijzen en andere informatie te krijgen, moeten we XPath-technologie of CSS-selectors gebruiken. Ik besloot bij XPath te blijven en had niet de behoefte om CSS-selectors te gebruiken, maar het is heel goed mogelijk om op die manier te werken. Navigeren op een pagina met XPath kan lastig zijn, en zelfs als je de technieken gebruikt die ik heb beschreven dit artikel, waarbij de overeenkomstige identificatiegegevens uit de paginacode moesten worden gekopieerd, besefte ik dat dit in feite niet de optimale manier is om toegang te krijgen tot de noodzakelijke elementen. Trouwens, binnen dit Het boek biedt een uitstekende beschrijving van de basisbeginselen van het werken met pagina's met behulp van XPath- en CSS-selectors. Zo ziet de overeenkomstige webdrivermethode eruit.

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
Laten we dus verder werken aan de bot. Laten we de mogelijkheden van het programma gebruiken om de goedkoopste tickets te selecteren. In de volgende afbeelding is de XPath-selectorcode rood gemarkeerd. Om de code te bekijken, klikt u met de rechtermuisknop op het pagina-element waarin u geïnteresseerd bent en selecteert u de opdracht Inspecteren in het menu dat verschijnt. Dit commando kan worden aangeroepen voor verschillende pagina-elementen, waarvan de code wordt weergegeven en gemarkeerd in de codeviewer.

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
Paginacode bekijken

Om bevestiging te vinden van mijn redenering over de nadelen van het kopiëren van selectors uit code, moet u op de volgende kenmerken letten.

Dit is wat je krijgt als je de code kopieert:

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

Om zoiets als dit te kopiëren, moet je met de rechtermuisknop klikken op het codegedeelte waarin je geïnteresseerd bent en de opdracht Kopiëren > XPath kopiëren selecteren in het menu dat verschijnt.

Dit is wat ik heb gebruikt om de knop Goedkoopste te definiëren:

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

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
Kopieeropdracht > Kopieer XPath

Het is vrij duidelijk dat de tweede optie er veel eenvoudiger uitziet. Bij gebruik zoekt het naar een element a dat het attribuut heeft data-codegelijk aan price. Bij gebruik van de eerste optie wordt het element doorzocht id wat gelijk is aan wtKI-price_aTab, en het XPath-pad naar het element ziet er zo uit /div[1]/div/div/div[1]/div/span/span. Een dergelijke XPath-query op een pagina volstaat, maar slechts één keer. Dat kan ik nu meteen zeggen id verandert de volgende keer dat de pagina wordt geladen. Karaktervolgorde wtKI verandert dynamisch elke keer dat de pagina wordt geladen, dus de code die er gebruik van maakt, is nutteloos nadat de volgende pagina opnieuw is geladen. Neem dus even de tijd om XPath te begrijpen. Deze kennis zal je goed van pas komen.

Er moet echter worden opgemerkt dat het kopiëren van XPath-selectors nuttig kan zijn bij het werken met vrij eenvoudige sites, en als u zich hier prettig bij voelt, is er niets mis mee.

Laten we nu eens nadenken over wat we moeten doen als u alle zoekresultaten in verschillende regels in een lijst wilt krijgen. Erg makkelijk. Elk resultaat bevindt zich in een object met een klasse resultWrapper. Het laden van alle resultaten kan in een lus gebeuren, vergelijkbaar met de onderstaande lus.

Opgemerkt moet worden dat als u het bovenstaande begrijpt, u ​​het grootste deel van de code die we zullen analyseren gemakkelijk zou moeten begrijpen. Terwijl deze code wordt uitgevoerd, hebben we toegang tot wat we nodig hebben (in feite het element waarin het resultaat is verpakt) met behulp van een soort padspecifiek mechanisme (XPath). Dit wordt gedaan om de tekst van het element te verkrijgen en deze in een object te plaatsen waaruit gegevens kunnen worden gelezen (eerst gebruikt). flight_containers, Dan - flights_list).

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
De eerste drie regels worden weergegeven en we kunnen duidelijk alles zien wat we nodig hebben. Er zijn echter interessantere manieren om informatie te verkrijgen. We moeten gegevens van elk element afzonderlijk verzamelen.

Ga aan het werk!

De eenvoudigste manier om een ​​functie te schrijven is door extra resultaten te laden, dus daar beginnen we mee. Ik wil het aantal vluchten waarover het programma informatie ontvangt maximaliseren, zonder argwaan te wekken bij de dienst die tot een inspectie leidt. Daarom klik ik telkens wanneer de pagina wordt weergegeven op de knop Meer resultaten laden. In deze code moet je op het blok letten try, die ik heb toegevoegd omdat de knop soms niet goed laadt. Als u dit ook tegenkomt, becommentarieer dan de oproepen naar deze functie in de functiecode start_kayak, die we hieronder zullen bekijken.

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

Nu, na een lange analyse van deze functie (soms kan ik me laten meeslepen), zijn we klaar om een ​​functie te declareren die de pagina zal schrapen.

Ik heb het meeste van wat nodig is al verzameld in de volgende functie genaamd page_scrape. Soms worden de geretourneerde padgegevens gecombineerd, dus ik gebruik een eenvoudige methode om deze te scheiden. Bijvoorbeeld wanneer ik voor het eerst variabelen gebruik section_a_list и section_b_list. Onze functie retourneert een dataframe flights_dfDit stelt ons in staat de resultaten die zijn verkregen uit verschillende methoden voor het sorteren van gegevens te scheiden en deze later te combineren.

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

Ik heb geprobeerd de variabelen een naam te geven, zodat de code begrijpelijk zou zijn. Onthoud dat variabelen beginnen met a behoren tot de eerste fase van het pad, en b - tot de tweede. Laten we verder gaan met de volgende functie.

Ondersteuningsmechanismen

We hebben nu een functie waarmee we extra zoekresultaten kunnen laden en een functie om die resultaten te verwerken. Dit artikel had hier kunnen eindigen, omdat deze twee functies alles bieden wat je nodig hebt om pagina's te schrapen die je zelf kunt openen. Maar we hebben enkele van de hierboven besproken hulpmechanismen nog niet besproken. Dit is bijvoorbeeld de code voor het verzenden van e-mails en enkele andere dingen. Dit alles vind je in de functie start_kayak, die we nu zullen overwegen.

Deze functie vereist informatie over steden en datums. Met behulp van deze informatie vormt het een link in een variabele kayak, die wordt gebruikt om u naar een pagina te brengen die zoekresultaten bevat, gesorteerd op hun beste overeenkomst met de zoekopdracht. Na de eerste scrapsessie gaan we aan de slag met de prijzen in de tabel bovenaan de pagina. We zullen namelijk de minimale ticketprijs en de gemiddelde prijs vinden. Dit alles, samen met de voorspelling van de site, wordt per e-mail verzonden. Op de pagina moet de bijbehorende tabel zich in de linkerbovenhoek bevinden. Het werken met deze tabel kan overigens een fout veroorzaken bij het zoeken op exacte datums, aangezien de tabel in dit geval niet op de pagina wordt weergegeven.

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

Ik heb dit script getest met een Outlook-account (hotmail.com). Ik heb niet getest of het correct werkt met een Gmail-account, dit e-mailsysteem is behoorlijk populair, maar er zijn veel opties mogelijk. Als u een Hotmail-account gebruikt, hoeft u alleen maar uw gegevens in de code in te voeren om alles te laten werken.

Als u wilt begrijpen wat er precies wordt gedaan in specifieke delen van de code voor deze functie, kunt u deze kopiëren en ermee experimenteren. Experimenteren met de code is de enige manier om deze echt te begrijpen.

Klaar systeem

Nu we alles hebben gedaan waar we het over hadden, kunnen we een eenvoudige lus maken die onze functies oproept. Het script vraagt ​​gegevens van de gebruiker op over steden en datums. Wanneer u test met een constante herstart van het script, is het onwaarschijnlijk dat u deze gegevens elke keer handmatig wilt invoeren. Daarom kunnen de overeenkomstige regels, voor het moment van testen, worden becommentarieerd door de opmerkingen eronder te verwijderen, waarin de gegevens die nodig zijn voor de test script is hardgecodeerd.

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

Zo ziet een testrun van het script eruit.
Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen
Testrun van het script

Resultaten van

Als je zover bent gekomen: gefeliciteerd! Je hebt nu een werkende webschraper, hoewel ik al veel manieren zie om deze te verbeteren. Het kan bijvoorbeeld worden geïntegreerd met Twilio, zodat het sms-berichten verzendt in plaats van e-mails. U kunt een VPN of iets anders gebruiken om tegelijkertijd resultaten van meerdere servers te ontvangen. Er is ook een periodiek optredend probleem bij het controleren van de sitegebruiker om te zien of hij een persoon is, maar dit probleem kan ook worden opgelost. Je hebt nu in ieder geval een basis die je kunt uitbreiden als je dat wilt. Zorg er bijvoorbeeld voor dat er een Excel-bestand als bijlage bij een e-mail naar de gebruiker wordt gestuurd.

Python - een assistent bij het vinden van goedkope vliegtickets voor liefhebbers van reizen

Alleen geregistreerde gebruikers kunnen deelnemen aan het onderzoek. Inloggen, Alsjeblieft.

Maakt u gebruik van webscraping-technologieën?

  • Ja

  • Geen

8 gebruikers hebben gestemd. 1 gebruiker onthield zich van stemming.

Bron: www.habr.com

Voeg een reactie