Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida

Artikli, mille tõlke täna avaldame, autor ütleb, et selle eesmärk on rääkida Pythonis veebikaabitsa arendamisest Seleniumi abil, mis otsib lennupiletite hindu. Piletite otsimisel kasutatakse paindlikke kuupäevi (+- 3 päeva määratud kuupäevade suhtes). Kaabits salvestab otsingutulemused Exceli faili ja saadab selle käivitanud inimesele meili, milles on kokkuvõte leitud kohta. Selle projekti eesmärk on aidata reisijatel leida parimad pakkumised.

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida

Kui tunnete end materjali mõistmise ajal eksinud, vaadake see artiklit.

Mida me otsima hakkame?

Siin kirjeldatud süsteemi võite vabalt kasutada vastavalt oma soovile. Näiteks kasutasin seda nädalavahetuse ekskursioonide ja piletite otsimiseks oma kodulinna. Kui soovite tõsiselt kasumlike piletite leidmist, võite skripti serveris käivitada (lihtne server, 130 rubla kuus, on selleks üsna sobiv) ja veenduge, et see töötab üks või kaks korda päevas. Otsingutulemused saadetakse teile e-posti teel. Lisaks soovitan kõik seadistada nii, et skript salvestaks Exceli faili koos otsingutulemustega Dropboxi kausta, mis võimaldab selliseid faile vaadata igal pool ja igal ajal.

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Ma pole veel vigadega tariife leidnud, kuid arvan, et see on võimalik

Otsimisel, nagu juba mainitud, kasutatakse “paindlikku kuupäeva”, skript leiab pakkumised, mis jäävad antud kuupäevadest kolme päeva jooksul. Kuigi skripti käivitades otsib see pakkumisi ainult ühes suunas, on seda lihtne muuta nii, et see suudab koguda andmeid mitme lennusuuna kohta. Selle abiga saate otsida isegi ekslikke tariife, sellised leiud võivad olla väga huvitavad.

Miks vajate teist veebikaabitsat?

Kui ma esimest korda veebikraapimist alustasin, ei tundnud see mind ausalt öeldes eriti huvitatud. Tahtsin teha rohkem projekte ennustava modelleerimise, finantsanalüüsi ja võib-olla ka tekstide emotsionaalse värvingu analüüsi valdkonnas. Aga selgus, et väga huvitav oli välja mõelda, kuidas luua programm, mis veebilehtedelt andmeid kogub. Sellesse teemasse süvenedes mõistsin, et veebikraapimine on Interneti "mootor".

Võib arvata, et see on liiga julge väide. Kuid arvestage, et Google alustas veebikaabitsaga, mille Larry Page lõi Java ja Pythoni abil. Google'i robotid on uurinud Internetti, püüdes pakkuda kasutajatele nende küsimustele parimaid vastuseid. Veebikraapimisel on lõputult kasutusvõimalusi ja isegi kui olete andmeteaduses millestki muust huvitatud, vajate analüüsimiseks vajalike andmete hankimiseks mõningaid kraapimisoskusi.

Leidsin mõned siin kasutatud tehnikad imelisena raamat veebikraapimise kohta, mille hiljuti omandasin. See sisaldab palju lihtsaid näiteid ja ideid õpitu praktiliseks rakendamiseks. Lisaks on väga huvitav peatükk reCaptcha kontrollidest möödahiilimise kohta. See tuli mulle uudisena, kuna ma isegi ei teadnud, et selliste probleemide lahendamiseks on olemas spetsiaalsed tööriistad ja isegi terved teenused.

Kas sulle meeldib reisida?!

Selle jaotise pealkirjas püstitatud lihtsale ja üsna kahjutule küsimusele võib sageli kuulda positiivset vastust, millele on lisatud paar lugu selle inimese reisidelt, kellele see esitati. Enamik meist nõustub, et reisimine on suurepärane võimalus sukelduda uude kultuurikeskkonda ja avardada oma silmaringi. Kui aga küsida kelleltki, kas talle meeldib lennupileteid otsida, siis olen kindel, et vastus ei ole nii positiivne. Tegelikult tuleb Python meile siin appi.

Esimeseks ülesandeks, mida peame lahendama teel lennupiletite info otsimise süsteemi loomisel, on sobiva platvormi valimine, kust infot võtame. Selle probleemi lahendamine ei olnud minu jaoks lihtne, kuid lõpuks valisin Kayaki teenuse. Proovisin Momondo, Skyscanneri, Expedia ja mõne muu teenuseid, kuid nende ressursside robotikaitsemehhanismid olid läbimatud. Pärast mitmeid katseid, mille jooksul pidin tegelema fooride, ülekäiguradade ja jalgratastega, püüdes süsteeme veenda, et olen inimene, otsustasin, et Kayak sobib mulle kõige paremini, hoolimata sellest, et isegi kui laaditakse liiga palju lehti lühikese aja jooksul ja algavad ka kontrollid. Mul õnnestus panna robot saatma saidile päringuid 4–6-tunniste intervallidega ja kõik toimis hästi. Aeg-ajalt tuleb Kayakiga töötades ette raskusi, aga kui nad hakkavad tšekkidega kiusama, siis pead nendega kas käsitsi tegelema ja siis bot käivitama või ootama paar tundi ja kontrollid peaksid lõppema. Vajadusel saate koodi hõlpsalt mõnele teisele platvormile kohandada ja kui seda teete, saate sellest kommentaarides teada anda.

Kui te alles alustate veebikraapimisega ja ei tea, miks mõned veebisaidid sellega vaeva näevad, siis enne oma esimese projektiga selles valdkonnas alustamist tehke endale teene ja otsige Google'is sõnu "veebi kraapimise etikett" . Teie katsed võivad lõppeda varem, kui arvate, kui teete ebamõistlikult veebikraapimist.

Alustamine

Siin on üldine ülevaade sellest, mis meie veebikaabitsa koodis juhtub:

  • Importige vajalikud teegid.
  • Google Chrome'i vahekaardi avamine.
  • Kutsuge funktsioon, mis käivitab roboti, edastades sellele linnad ja kuupäevad, mida piletite otsimisel kasutatakse.
  • See funktsioon võtab esimesed otsingutulemused, mis on sorteeritud parimate järgi, ja klõpsab nuppu, et laadida rohkem tulemusi.
  • Teine funktsioon kogub andmeid kogu lehelt ja tagastab andmeraami.
  • Kaks eelmist sammu sooritatakse sorteerimistüüpide abil piletihinna (odav) ja lennukiiruse (kiireim) järgi.
  • Skripti kasutajale saadetakse e-kiri, mis sisaldab kokkuvõtet piletihindadest (odavaimad piletid ja keskmine hind) ning Exceli failina salvestatakse andmeraam kolme eelnimetatud indikaatori järgi sorteeritud teabega.
  • Kõik ülaltoodud toimingud tehakse tsüklina pärast kindlaksmääratud ajavahemikku.

Tuleb märkida, et iga Seleniumi projekt algab veebidraiverist. ma kasutan Chromedriver, töötan Google Chrome'iga, kuid on ka teisi võimalusi. Populaarsed on ka PhantomJS ja Firefox. Pärast draiveri allalaadimist peate selle paigutama vastavasse kausta ja sellega on selle kasutamiseks ettevalmistamine lõpule viidud. Meie skripti esimesed read avavad uue Chrome'i vahekaardi.

Pidage meeles, et ma ei püüa oma loos avada uusi horisonte soodsate lennupiletite pakkumiste leidmiseks. Selliste pakkumiste otsimiseks on palju täpsemaid meetodeid. Ma tahan lihtsalt pakkuda selle materjali lugejatele lihtsat, kuid praktilist viisi selle probleemi lahendamiseks.

Siin on kood, millest me eespool rääkisime.

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)

Koodi alguses näete pakettide impordi käske, mida kasutatakse kogu meie projektis. Niisiis, randint kasutatakse selleks, et robot suvaline arv sekundeid enne uue otsinguoperatsiooni alustamist "uinuks". Tavaliselt ei saa ükski robot ilma selleta hakkama. Kui käivitate ülaltoodud koodi, avaneb Chrome'i aken, mida robot kasutab saitidega töötamiseks.

Teeme väikese katse ja avame eraldi aknas veebilehe kayak.com. Valime linna, kust lendame, ja linna, kuhu tahame jõuda, samuti lennukuupäevad. Kuupäevade valikul jälgi, et oleks kasutatud vahemikku +-3 päeva. Kirjutasin koodi, võttes arvesse, mida sait sellistele taotlustele vastuseks toodab. Kui peate näiteks otsima pileteid ainult kindlaksmääratud kuupäevadeks, siis on suur tõenäosus, et peate roboti koodi muutma. Kui räägin koodist, annan asjakohased selgitused, kuid kui tunnete segadust, andke mulle teada.

Nüüd klõpsake otsingunupul ja vaadake aadressiribal olevat linki. See peaks olema sarnane lingiga, mida kasutan allolevas näites, kus muutuja deklareeritakse kayak, mis salvestab URL-i ja meetodit kasutatakse get veebidraiver. Pärast otsingunupul klõpsamist peaksid lehele ilmuma tulemused.

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Kui kasutasin käsku get rohkem kui kaks või kolm korda mõne minuti jooksul paluti mul reCaptcha abil kinnitamine lõpule viia. Saate selle kontrolli käsitsi läbida ja jätkata katsetamist, kuni süsteem otsustab uue kontrolli käivitada. Kui ma skripti testisin, tundus, et esimene otsinguseanss läks alati sujuvalt, nii et kui soovite koodiga katsetada, peate ainult perioodiliselt käsitsi kontrollima ja laskma koodil käitada, kasutades pikki otsinguseansside vahelisi intervalle. Ja kui järele mõelda, siis tõenäoliselt ei vaja inimene otsingutoimingute vahel 10-minutilise intervalliga saadud teavet piletihindade kohta.

Lehega töötamine XPathi abil

Niisiis, avasime akna ja laadisime saidi. Hindade ja muu teabe saamiseks peame kasutama XPathi tehnoloogiat või CSS-i valijaid. Otsustasin jääda XPathi juurde ega tundnud vajadust CSS-i selektoreid kasutada, kuid nii on täiesti võimalik. XPathi abil lehel ringi liikumine võib olla keeruline ja isegi siis, kui kasutate kirjeldatud tehnikaid see artiklist, mis hõlmas vastavate identifikaatorite kopeerimist lehe koodist, sain aru, et tegelikult pole see optimaalne viis vajalikele elementidele ligi pääseda. Muide, sisse see Raamat annab suurepärase kirjelduse XPathi ja CSS-i valijate abil lehtedega töötamise põhitõdedest. Nii näeb välja vastav veebidraiveri meetod.

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Niisiis, jätkame robotiga töötamist. Kasutame programmi võimalusi soodsaimate piletite valimiseks. Järgmisel pildil on XPathi valija kood punasega esile tõstetud. Koodi vaatamiseks tuleb teha huvipakkuval leheelemendil paremklõps ja valida ilmuvast menüüst käsk Kontrolli. Seda käsku saab kutsuda erinevate leheelementide jaoks, mille kood kuvatakse ja tõstetakse esile koodivaaturis.

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Vaadake lehe koodi

Et leida kinnitust oma arutlustele koodist valijate kopeerimise puuduste kohta, pöörake tähelepanu järgmistele funktsioonidele.

See on see, mida saate koodi kopeerimisel:

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

Millegi sellise kopeerimiseks tuleb paremklõpsata sind huvitaval koodilõigul ja valida ilmuvast menüüst käsk Copy > Copy XPath.

Odavaim nupu määratlemiseks kasutasin järgmist:

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

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Kopeeri käsk > Kopeeri XPath

On üsna ilmne, et teine ​​variant tundub palju lihtsam. Kui seda kasutatakse, otsib see elementi a, millel on atribuut data-codevõrdne price. Esimese valiku kasutamisel otsitakse elementi id mis on võrdne wtKI-price_aTab, ja XPath tee elemendini näeb välja selline /div[1]/div/div/div[1]/div/span/span. Selline XPathi päring lehele teeb asja ära, kuid ainult üks kord. Ma võin seda kohe öelda id muutub lehe järgmisel laadimisel. Tegelaste järjestus wtKI muutub dünaamiliselt iga kord, kui leht laaditakse, nii et seda kasutav kood on pärast järgmist lehe uuesti laadimist kasutu. Nii et võtke natuke aega XPathi mõistmiseks. Need teadmised on teile kasulikud.

Siiski tuleb märkida, et XPathi valijate kopeerimine võib olla kasulik, kui töötate üsna lihtsate saitidega ja kui olete sellega rahul, pole selles midagi halba.

Nüüd mõelgem, mida teha, kui teil on vaja kõiki otsingutulemusi loendis mitme rea kaupa hankida. Väga lihtne. Iga tulemus on klassiga objekti sees resultWrapper. Kõikide tulemuste laadimist saab teha sarnaselt allpool näidatud tsükliga.

Tuleb märkida, et kui saate ülaltoodust aru, peaksite hõlpsasti aru saama enamikust analüüsitavast koodist. Selle koodi käitamise ajal pääseme mingi tee määramise mehhanismi (XPath) abil juurde sellele, mida vajame (tegelikult elemendile, millesse tulemus mähitakse). Seda tehakse selleks, et saada elemendi tekst ja paigutada see objekti, kust saab andmeid lugeda (esmakordselt flight_containers, siis - flights_list).

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Kuvatakse kolm esimest rida ja me näeme selgelt kõike, mida vajame. Meil on aga huvitavamaid viise info hankimiseks. Peame võtma andmed iga elemendi kohta eraldi.

Asu tööle!

Lihtsaim viis funktsiooni kirjutamiseks on laadida lisatulemusi, seega alustame sellest. Soovin maksimeerida lendude arvu, mille kohta programm teavet saab, ilma et see ülevaatuseni viivas teenuses kahtlustaks, nii et klõpsan iga kord, kui leht kuvatakse, nuppu Laadi rohkem tulemusi. Selles koodis peaksite tähelepanu pöörama plokile try, mille lisasin, sest vahel ei lae nupp korralikult. Kui ka seda kohtate, kommenteerige funktsiooni koodis selle funktsiooni väljakutsed start_kayak, mida me allpool vaatleme.

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

Nüüd, pärast selle funktsiooni pikka analüüsi (mõnikord võin end ära teha), oleme valmis deklareerima funktsiooni, mis lehe kraapib.

Olen juba kogunud suurema osa vajalikust järgmises funktsioonis nimega page_scrape. Mõnikord kombineeritakse tagastatud teeandmeid, seega kasutan nende eraldamiseks lihtsat meetodit. Näiteks kui ma kasutan muutujaid esimest korda section_a_list и section_b_list. Meie funktsioon tagastab andmeraami flights_df, võimaldab see eri andmesorteerimismeetoditest saadud tulemusi eraldada ja hiljem kombineerida.

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

Püüdsin muutujaid nimetada nii, et kood oleks arusaadav. Pidage meeles, et muutujad algavad tähega a kuuluvad tee esimesse etappi ja b - teisele. Liigume edasi järgmise funktsiooni juurde.

Tugimehhanismid

Meil on nüüd funktsioon, mis võimaldab laadida täiendavaid otsingutulemusi, ja funktsioon nende tulemuste töötlemiseks. See artikkel oleks võinud siinkohal lõppeda, kuna need kaks funktsiooni pakuvad kõike, mida vajate lehtede kraapimiseks, mida saate ise avada. Kuid me pole veel mõnda ülalpool käsitletud abimehhanismi kaalunud. Näiteks on see kood e-kirjade saatmiseks ja mõnede muude asjade jaoks. Kõik see on leitav funktsioonist start_kayak, mida me nüüd kaalume.

See funktsioon nõuab teavet linnade ja kuupäevade kohta. Seda teavet kasutades moodustab see muutujas lingi kayak, mida kasutatakse, et suunata teid lehele, mis sisaldab otsingutulemusi, mis on sorteeritud nende parima vaste järgi. Pärast esimest kraapimise seanssi töötame hindadega lehe ülaosas olevas tabelis. Nimelt leiame pileti miinimumhinna ja keskmise hinna. Kõik see koos saidi ennustusega saadetakse meili teel. Lehel peaks vastav tabel olema vasakus ülanurgas. Muide, selle tabeliga töötamine võib täpsete kuupäevadega otsimisel põhjustada tõrke, kuna sel juhul tabelit lehel ei kuvata.

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

Testisin seda skripti Outlooki kontoga (hotmail.com). Ma pole testinud, et see Gmaili kontoga õigesti töötaks, see meilisüsteem on üsna populaarne, kuid võimalikke valikuid on palju. Kui kasutate Hotmaili kontot, siis selleks, et kõik toimiks, peate lihtsalt oma andmed koodi sisestama.

Kui soovite mõista, mida selle funktsiooni koodi konkreetsetes jaotistes täpselt tehakse, saate need kopeerida ja nendega katsetada. Koodiga katsetamine on ainus viis sellest tõeliselt aru saada.

Valmis süsteem

Nüüd, kui oleme teinud kõik, millest rääkisime, saame luua lihtsa tsükli, mis kutsub meie funktsioone. Skript küsib kasutajalt andmeid linnade ja kuupäevade kohta. Pideva skripti taaskäivitusega testimisel ei soovi te tõenäoliselt neid andmeid iga kord käsitsi sisestada, mistõttu saab testimise ajaks vastavaid ridu välja kommenteerida, eemaldades nende all olevad kommentaarid, kus skript on kõvakodeeritud.

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

Selline näeb välja skripti testkäivitus.
Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida
Skripti testkäivitus

Tulemused

Kui olete nii kaugele jõudnud, palju õnne! Nüüd on teil töötav veebikaabits, kuigi ma näen juba praegu palju võimalusi selle täiustamiseks. Näiteks saab selle integreerida Twilioga, nii et see saadab e-kirjade asemel tekstisõnumeid. Võite kasutada VPN-i või midagi muud, et saada korraga tulemusi mitmelt serverilt. Samuti tekib perioodiliselt probleem saidi kasutaja kontrollimisel, kas ta on isik, kuid seda probleemi saab ka lahendada. Igal juhul on teil nüüd baas, mida saate soovi korral laiendada. Näiteks veenduge, et kasutajale saadetakse meili manusena Exceli fail.

Python - abiline odavate lennupiletite leidmisel neile, kes armastavad reisida

Küsitluses saavad osaleda ainult registreerunud kasutajad. Logi sissepalun.

Kas kasutate veebikraapimise tehnoloogiaid?

  • Jah

  • ei

8 kasutajat hääletas. 1 kasutaja jäi erapooletuks.

Allikas: www.habr.com

Lisa kommentaar