Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis

Die skrywer van die artikel, waarvan die vertaling ons vandag publiseer, sê dat die doel daarvan is om te praat oor die ontwikkeling van 'n webskraper in Python wat Selenium gebruik, wat na vliegkaartjiepryse soek. Wanneer daar na kaartjies gesoek word, word buigsame datums gebruik (+- 3 dae relatief tot die gespesifiseerde datums). Die skraper stoor die soekresultate in 'n Excel-lêer en stuur vir die persoon wat die soektog uitgevoer het 'n e-pos met 'n opsomming van wat hulle gevind het. Die doel van hierdie projek is om reisigers te help om die beste aanbiedings te vind.

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis

As jy verlore voel terwyl jy die materiaal verstaan, kyk na hierdie artikel.

Waarna gaan ons soek?

Jy is vry om die stelsel wat hier beskryf word te gebruik soos jy wil. Ek het dit byvoorbeeld gebruik om na naweektoere en kaartjies na my tuisdorp te soek. As jy ernstig is om winsgewende kaartjies te vind, kan jy die skrif op die bediener laat loop (eenvoudig bediener, vir 130 roebels per maand, is baie geskik hiervoor) en maak seker dat dit een of twee keer per dag loop. Soekresultate sal per e-pos aan jou gestuur word. Daarbenewens beveel ek aan om alles so op te stel dat die skrif 'n Excel-lêer met soekresultate in 'n Dropbox-lêergids stoor, wat jou sal toelaat om sulke lêers van enige plek en enige tyd te bekyk.

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
Ek het nog nie tariewe met foute gekry nie, maar ek dink dit is moontlik

By soek, soos reeds genoem, word 'n "buigsame datum" gebruik; die skrif vind aanbiedinge wat binne drie dae vanaf die gegewe datums is. Alhoewel dit net in een rigting na aanbiedings soek wanneer die skrip uitgevoer word, is dit maklik om dit te verander sodat dit data oor verskeie vlugrigtings kan versamel. Met sy hulp kan jy selfs na foutiewe tariewe soek; sulke vondste kan baie interessant wees.

Hoekom het jy nog 'n webskraper nodig?

Toe ek die eerste keer met webskraap begin het, was ek eerlikwaar nie juis daarin geïnteresseerd nie. Ek wou meer projekte doen op die gebied van voorspellende modellering, finansiële analise, en moontlik op die gebied van die ontleding van die emosionele kleur van tekste. Maar dit het geblyk dat dit baie interessant was om uit te vind hoe om 'n program te skep wat data van webwerwe insamel. Soos ek in hierdie onderwerp gedelf het, het ek besef dat webskraping die "enjin" van die internet is.

Jy dink dalk dat dit 'n te gewaagde stelling is. Maar dink daaraan dat Google begin het met 'n webskraper wat Larry Page geskep het met Java en Python. Google-robotte het die internet verken en probeer om sy gebruikers die beste antwoorde op hul vrae te gee. Webskraping het eindelose gebruike, en selfs al stel jy belang in iets anders in Data Science, sal jy 'n paar skraapvaardighede nodig hê om die data te kry wat jy moet ontleed.

Ek het gevind dat sommige van die tegnieke wat hier gebruik word in 'n wonderlike die boek oor webskraping, wat ek onlangs bekom het. Dit bevat baie eenvoudige voorbeelde en idees vir praktiese toepassing van wat jy geleer het. Daarbenewens is daar 'n baie interessante hoofstuk oor die omseil van reCaptcha-tjeks. Dit het vir my as nuus gekom, aangesien ek nie eers geweet het dat daar spesiale gereedskap en selfs volledige dienste is om sulke probleme op te los nie.

Hou jy daarvan om te reis?!

Op die eenvoudige en redelik onskadelike vraag wat in die titel van hierdie afdeling gestel word, kan jy dikwels 'n positiewe antwoord hoor, vergesel van 'n paar stories uit die reise van die persoon aan wie dit gevra is. Die meeste van ons sal saamstem dat reis 'n goeie manier is om jouself in nuwe kulturele omgewings te verdiep en jou horisonne te verbreed. As jy egter iemand vra of hulle daarvan hou om na vliegkaartjies te soek, is ek seker dat die antwoord nie so positief sal wees nie. Om die waarheid te sê, Python kom ons hier te hulp.

Die eerste taak wat ons moet oplos op pad na die skep van 'n stelsel om inligting oor vliegkaartjies te soek, sal wees om 'n geskikte platform te kies waaruit ons inligting sal neem. Om hierdie probleem op te los was nie vir my maklik nie, maar ek het uiteindelik die Kayak-diens gekies. Ek het die dienste van Momondo, Skyscanner, Expedia en 'n paar ander probeer, maar die robotbeskermingsmeganismes op hierdie hulpbronne was ondeurdringbaar. Na verskeie pogings, waartydens ek met verkeersligte, voetgangeroorgange en fietse te doen gehad het en die stelsels probeer oortuig het dat ek 'n mens is, het ek besluit dat Kayak die beste vir my pas, ten spyte van die feit dat selfs al is daar te veel bladsye gelaai. binne 'n kort tydjie, en tjeks begin ook. Ek het daarin geslaag om die bot met tussenposes van 4 tot 6 uur na die webwerf te laat stuur, en alles het goed gewerk. Van tyd tot tyd ontstaan ​​probleme wanneer jy met Kayak werk, maar as hulle jou met tjeks begin pla, moet jy dit óf handmatig hanteer en dan die bot begin, óf 'n paar uur wag en die tjeks moet stop. Indien nodig, kan u die kode maklik vir 'n ander platform aanpas, en as u dit doen, kan u dit in die kommentaar rapporteer.

As jy net begin met webskraping en nie weet hoekom sommige webwerwe daarmee sukkel nie, doen dan jouself 'n guns voordat jy jou eerste projek in hierdie area begin en doen 'n Google-soektog op die woorde "webskraap etiket" . Jou eksperimente kan gouer eindig as wat jy dink as jy onverstandig webskraap.

Aan die slag

Hier is 'n algemene oorsig van wat in ons webskraperkode gaan gebeur:

  • Voer die vereiste biblioteke in.
  • Maak 'n Google Chrome-oortjie oop.
  • Roep 'n funksie wat die bot begin, gee dit die stede en datums deur wat gebruik sal word wanneer jy na kaartjies soek.
  • Hierdie funksie neem die eerste soekresultate, gesorteer volgens beste, en klik 'n knoppie om meer resultate te laai.
  • 'n Ander funksie versamel data van die hele bladsy en gee 'n dataraam terug.
  • Die twee vorige stappe word uitgevoer met behulp van sorteertipes volgens kaartjieprys (goedkoop) en volgens vlugspoed (vinnigste).
  • Die gebruiker van die skrif word 'n e-pos gestuur met 'n opsomming van kaartjiepryse (goedkoopste kaartjies en gemiddelde prys), en 'n dataraam met inligting gesorteer volgens die drie bogenoemde aanwysers word as 'n Excel-lêer gestoor.
  • Al die bogenoemde aksies word uitgevoer in 'n siklus na 'n bepaalde tydperk.

Daar moet kennis geneem word dat elke Selenium-projek met 'n webbestuurder begin. ek gebruik Chromebestuurder, Ek werk met Google Chrome, maar daar is ander opsies. PhantomJS en Firefox is ook gewild. Nadat u die bestuurder afgelaai het, moet u dit in die toepaslike gids plaas, en dit voltooi die voorbereiding vir die gebruik daarvan. Die eerste reëls van ons skrif maak 'n nuwe Chrome-oortjie oop.

Hou in gedagte dat ek in my storie nie nuwe horisonne probeer oopmaak om goeie aanbiedings op vliegkaartjies te vind nie. Daar is baie meer gevorderde metodes om na sulke aanbiedings te soek. Ek wil net vir lesers van hierdie materiaal 'n eenvoudige maar praktiese manier bied om hierdie probleem op te los.

Hier is die kode waaroor ons hierbo gepraat het.

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 die begin van die kode kan u die pakketinvoeropdragte sien wat deur ons projek gebruik word. Dus, randint gebruik om die bot vir 'n ewekansige aantal sekondes te laat "aan die slaap raak" voordat 'n nuwe soekoperasie begin word. Gewoonlik kan nie 'n enkele bot hiersonder klaarkom nie. As jy die bogenoemde kode gebruik, sal 'n Chrome-venster oopmaak wat die bot sal gebruik om met werwe te werk.

Kom ons doen 'n klein eksperiment en maak die kayak.com-webwerf in 'n aparte venster oop. Ons sal die stad kies waaruit ons gaan vlieg, en die stad waarheen ons wil kom, asook die vlugdatums. Wanneer jy datums kies, maak seker dat die reeks van +-3 dae gebruik word. Ek het die kode geskryf met inagneming van wat die webwerf produseer in reaksie op sulke versoeke. As jy byvoorbeeld net vir spesifieke datums vir kaartjies moet soek, dan is daar 'n groot waarskynlikheid dat jy die botkode sal moet wysig. Wanneer ek oor die kode praat, gee ek gepaste verduidelikings, maar as jy verward voel, laat weet my.

Klik nou op die soekknoppie en kyk na die skakel in die adresbalk. Dit moet soortgelyk wees aan die skakel wat ek in die voorbeeld hieronder gebruik waar die veranderlike verklaar word kayak, wat die URL stoor, en die metode word gebruik get web bestuurder. Nadat u op die soekknoppie geklik het, moet resultate op die bladsy verskyn.

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
Toe ek die opdrag gebruik het get meer as twee of drie keer binne 'n paar minute, is ek gevra om verifikasie met behulp van reCaptcha te voltooi. Jy kan hierdie kontrole handmatig slaag en voortgaan om te eksperimenteer totdat die stelsel besluit om 'n nuwe kontrole uit te voer. Toe ek die skrif getoets het, het dit gelyk of die eerste soeksessie altyd glad verloop het, so as jy met die kode wil eksperimenteer, sal jy net periodiek met die hand moet kyk en die kode laat loop, met lang intervalle tussen soeksessies. En as jy daaraan dink, is dit onwaarskynlik dat 'n persoon inligting benodig oor kaartjiepryse wat met 10 minute tussenposes tussen soekoperasies ontvang word.

Werk met 'n bladsy wat XPath gebruik

So, ons het 'n venster oopgemaak en die webwerf gelaai. Om pryse en ander inligting te kry, moet ons XPath-tegnologie of CSS-keurders gebruik. Ek het besluit om by XPath te bly en het nie die behoefte gevoel om CSS-keurders te gebruik nie, maar dit is heel moontlik om so te werk. Dit kan moeilik wees om deur 'n bladsy met XPath te navigeer, en selfs al gebruik jy die tegnieke wat ek beskryf het hierdie artikel, wat die kopiëring van die ooreenstemmende identifiseerders van die bladsykode behels het, het ek besef dat dit in werklikheid nie die optimale manier is om toegang tot die nodige elemente te verkry nie. Terloops, in hierdie Die boek bied 'n uitstekende beskrywing van die basiese beginsels van werk met bladsye deur XPath- en CSS-keurders te gebruik. Dit is hoe die ooreenstemmende webbestuurdermetode lyk.

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
So, laat ons voortgaan om aan die bot te werk. Kom ons gebruik die program se vermoëns om die goedkoopste kaartjies te kies. In die volgende prent word die XPath-kieserkode in rooi uitgelig. Om die kode te sien, moet jy regskliek op die bladsy-element waarin jy belangstel en die Inspekteer-opdrag kies uit die kieslys wat verskyn. Hierdie opdrag kan vir verskillende bladsy-elemente geroep word, waarvan die kode in die kodekyker vertoon en uitgelig sal word.

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
Bekyk bladsykode

Om bevestiging te vind van my redenasie oor die nadele van die kopiëring van keurders vanaf kode, let op die volgende kenmerke.

Dit is wat jy kry wanneer jy die kode kopieer:

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

Om so iets te kopieer, moet jy regskliek op die kodegedeelte waarin jy belangstel en die opdrag Kopieer > Kopieer XPath kies in die kieslys wat verskyn.

Hier is wat ek gebruik het om die goedkoopste knoppie te definieer:

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

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
Kopieer opdrag > Kopieer XPath

Dit is duidelik dat die tweede opsie baie eenvoudiger lyk. Wanneer dit gebruik word, soek dit na 'n element a wat die kenmerk het data-codegelyk aan price. Wanneer die eerste opsie gebruik word, word die element gesoek id wat gelyk is aan wtKI-price_aTab, en die XPath-pad na die element lyk /div[1]/div/div/div[1]/div/span/span. 'n XPath-navraag soos hierdie na 'n bladsy sal die ding doen, maar net een keer. Ek kan dit nou sê id sal verander die volgende keer as die bladsy gelaai word. Karaktervolgorde wtKI verander dinamies elke keer as die bladsy gelaai word, so die kode wat dit gebruik sal nutteloos wees na die volgende bladsy herlaai. Neem dus tyd om XPath te verstaan. Hierdie kennis sal jou goed dien.

Daar moet egter op gelet word dat die kopiëring van XPath-kiesers nuttig kan wees wanneer u met redelik eenvoudige werwe werk, en as u gemaklik hiermee is, is daar niks fout daarmee nie.

Kom ons dink nou oor wat om te doen as jy al die soekresultate in verskeie reëls binne 'n lys moet kry. Baie eenvoudig. Elke resultaat is binne 'n voorwerp met 'n klas resultWrapper. Die laai van al die resultate kan gedoen word in 'n lus soortgelyk aan die een wat hieronder getoon word.

Daar moet kennis geneem word dat as u die bogenoemde verstaan, u die meeste van die kode wat ons sal ontleed maklik moet verstaan. Soos hierdie kode loop, kry ons toegang tot wat ons nodig het (in werklikheid, die element waarin die resultaat toegedraai is) met behulp van 'n soort pad-spesifiserende meganisme (XPath). Dit word gedoen om die teks van die element te kry en dit in 'n voorwerp te plaas waaruit data gelees kan word (eerste gebruik flight_containers, dan - flights_list).

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
Die eerste drie reëls word vertoon en ons kan alles duidelik sien wat ons nodig het. Ons het egter meer interessante maniere om inligting te bekom. Ons moet data van elke element afsonderlik neem.

Gaan aan die werk!

Die maklikste manier om 'n funksie te skryf is om bykomende resultate te laai, so dit is waar ons sal begin. Ek wil graag die aantal vlugte waaroor die program inligting ontvang maksimeer, sonder om vermoedens in die diens te wek wat tot 'n inspeksie lei, so ek klik een keer op die Laai meer resultate-knoppie elke keer as die bladsy vertoon word. In hierdie kode moet u aandag gee aan die blok try, wat ek bygevoeg het omdat die knoppie soms nie behoorlik laai nie. As jy dit ook teëkom, maak kommentaar op oproepe na hierdie funksie in die funksiekode start_kayak, waarna ons hieronder sal kyk.

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

Nou, na 'n lang ontleding van hierdie funksie (soms kan ek meegevoer raak), is ons gereed om 'n funksie te verklaar wat die bladsy sal krap.

Ek het reeds die meeste van wat nodig is in die volgende funksie genaamd versamel page_scrape. Soms word die teruggekeerde paddata gekombineer, so ek gebruik 'n eenvoudige metode om dit te skei. Byvoorbeeld, wanneer ek veranderlikes vir die eerste keer gebruik section_a_list и section_b_list. Ons funksie gee 'n dataraam terug flights_df, dit stel ons in staat om die resultate wat verkry is van verskillende datasorteringmetodes te skei en later te kombineer.

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

Ek het probeer om die veranderlikes te benoem sodat die kode verstaanbaar sou wees. Onthou dat veranderlikes wat begin met a behoort tot die eerste fase van die pad, en b - na die tweede. Kom ons gaan aan na die volgende funksie.

Ondersteuningsmeganismes

Ons het nou 'n funksie wat ons toelaat om bykomende soekresultate te laai en 'n funksie om daardie resultate te verwerk. Hierdie artikel kon hier geëindig het, aangesien hierdie twee funksies alles verskaf wat jy nodig het om bladsye te krap wat jy self kan oopmaak. Maar ons het nog nie sommige van die hulpmeganismes wat hierbo bespreek is, oorweeg nie. Dit is byvoorbeeld die kode vir die stuur van e-posse en 'n paar ander dinge. Dit alles kan gevind word in die funksie start_kayak, wat ons nou sal oorweeg.

Vir hierdie funksie om te werk, benodig jy inligting oor stede en datums. Deur hierdie inligting te gebruik, vorm dit 'n skakel in 'n veranderlike kayak, wat gebruik word om jou na 'n bladsy te neem wat soekresultate sal bevat, gesorteer volgens hul beste passing by die navraag. Na die eerste skraapsessie sal ons met die pryse in die tabel boaan die bladsy werk. Ons sal naamlik die minimum kaartjieprys en die gemiddelde prys vind. Dit alles, saam met die voorspelling wat deur die webwerf uitgereik is, sal per e-pos gestuur word. Op die bladsy moet die ooreenstemmende tabel in die boonste linkerhoek wees. Om met hierdie tabel te werk, kan terloops 'n fout veroorsaak wanneer jy soek met presiese datums, aangesien die tabel in hierdie geval nie op die bladsy vertoon word nie.

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

Ek het hierdie skrif getoets met 'n Outlook-rekening (hotmail.com). Ek het dit nie getoets om reg te werk met 'n Gmail-rekening nie, hierdie e-posstelsel is baie gewild, maar daar is baie moontlike opsies. As jy 'n Hotmail-rekening gebruik, moet jy net jou data in die kode invoer om alles te laat werk.

As jy wil verstaan ​​wat presies in spesifieke afdelings van die kode vir hierdie funksie gedoen word, kan jy dit kopieer en daarmee eksperimenteer. Eksperimentering met die kode is die enigste manier om dit werklik te verstaan.

Gereed stelsel

Noudat ons alles gedoen het waaroor ons gepraat het, kan ons 'n eenvoudige lus skep wat ons funksies oproep. Die skrif versoek data van die gebruiker oor stede en datums. Wanneer u toets met konstante herbegin van die skrif, is dit onwaarskynlik dat u hierdie data elke keer met die hand wil invoer, dus kan die ooreenstemmende reëls, vir die tyd van toetsing, opgemerk word deur die kommentaar onder hulle te verwyder, waarin die data benodig deur die skrif is hardkodeer.

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

Dit is hoe 'n toetslopie van die skrif lyk.
Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis
Toetsloop van die skrif

Resultate van

As jy dit so ver gemaak het, baie geluk! Jy het nou 'n werkende webskraper, alhoewel ek reeds baie maniere kan sien om dit te verbeter. Dit kan byvoorbeeld met Twilio geïntegreer word sodat dit teksboodskappe in plaas van e-posse stuur. U kan ‘n VPN of iets anders gebruik om gelyktydig resultate van verskeie bedieners te ontvang. Daar is ook 'n gereelde probleem om die werfgebruiker na te gaan om te sien of hy 'n persoon is, maar hierdie probleem kan ook opgelos word. In elk geval, nou het jy 'n basis wat jy kan uitbrei as jy wil. Maak byvoorbeeld seker dat 'n Excel-lêer aan die gebruiker gestuur word as 'n aanhangsel by 'n e-pos.

Python - 'n assistent om goedkoop vliegkaartjies te vind vir diegene wat daarvan hou om te reis

Slegs geregistreerde gebruikers kan aan die opname deelneem. Meld aan, asseblief.

Gebruik jy webskraaptegnologieë?

  • Ja

  • Geen

8 gebruikers het gestem. 1 gebruiker het buite stemming gebly.

Bron: will.com

Voeg 'n opmerking