Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.

Täna avaldamise korral väidab artikli autor, et tema eesmärk on rääkida veebiskraaperi arendamisest Pythonis, kasutades Seleniumit, mis otsib lennupiletite hindu. Piletite otsimisel kasutatakse paindlikke kuupäevi (+- 3 päeva antud kuupäevadest). Skräppija salvestab otsingu tulemused Exceli faili ja saadab selle käivitajale e-kirja kokkuvõttega leitud informatsioonist. Projekti eesmärk on aidata reisijatel leida parimaid pakkumisi.

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.

Kui te kättevõtmise käigus tunnete, et olete eksinud — vaadake veebilehte seda artiklit.

Mida me otsime?

Te võite kasutada siin kirjeldatud süsteemi nii, nagu soovite. Mina kasutasin seda näiteks nädalavahetuse reiside ja piletite otsimiseks oma kodulinna. Kui olete tõsiselt otsustanud leida soodsaid pileteid — võite skripti serveris käivitada (lihtsalt server, 130 rubla kuus, sobib selleks päris hästi) ja teha nii, et see käivituks üks-kaks korda päevas. Otsingutulemused saadetakse teile e-posti teel. Lisaks soovitan seadistada kõik nii, et skript salvestaks Exceli faili otsingutulemustest Dropboxi kausta, mis võimaldab teil sarnaseid faile igal ajal ja igast kohast vaadata.

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Ma ei ole veel leidnud vigu hindades, kuid arvan, et see on võimalik

Otsingul, nagu juba mainitud, kasutatakse „paindlikku kuupäeva”, skript leiab pakkumised, mis on määratud kuupäevadega kolme päeva jooksul. Kuigi skripti käivitamisel otsitakse pakkumisi ainult ühes suunas, ei ole seda keeruline täiendada, et see saaks koguda andmeid mitme lennu suuna kohta. Selle abil saab isegi otsida vigaseid hindu, sellised leiud võivad olla väga huvitavad.

Miks on vaja veel üht veebiskreipijat?

Kui ma esmakordselt veebikraapimisega alustasin, polnud see ausalt öeldes eriti huvitav. Soovisisin tegeleda rohkem prognoosimise, finanalüüsi ja võib-olla ka tekstide emotsionaalse analüüsiga. Kuid osutus, et see on äärmiselt huvitav — õppida, kuidas luua programm, mis kogub andmeid veebilehtedelt. Süvenedes sellesse valdkonda, mõistsin, et just veebikraapimine on interneti "mootor".

Võib-olla arvate, et see on liiga julge väide. Kuid mõelge sellele, et Google'i ettevõte algas veebikraapijast, mille Larry Page lõi Java ja Pythoniga. Google'i robotid uurivad internetti, et pakkuda oma kasutajatele parimaid vastuseid nende küsimustele. Veebikraapimisest on lõpmatu hulk rakendusi ning isegi kui tegelete Data Science'is millegi muuga, vajate andmete analüüsimiseks mõningaid kraapimisoskusi.

Mõned siin kasutatud tehnikad leidsin suurepärasest a book veebikäppimist, mille olen hiljuti soetanud. Sealt leiab palju lihtsaid näiteid ja ideid õpitu praktiliseks rakendamiseks. Lisaks on seal väga huvitav peatükk reCaptcha testide ületamisest. See oli mulle uudiseks, kuna ma ei teadnud, et selliste probleemide lahendamiseks on olemas spetsiaalsed tööriistad ja isegi täis teenuseid.

Armastate reisida?!

Lihtsale ja üsna kahjutule küsimusele, mis on selle jaotise pealkirjas, kuuleb sageli positiivset vastust, mida saadab paar reisi lugu inimese poolt, kellele see esitati. Enamik meist nõustub, et reisid on suurepärane võimalus uute kultuuride keskkonda sukeldumiseks ja oma silmaringi avardamiseks. Siiski, kui kelleltki küsida, kas talle meeldib lennupiletite otsimine, siis olen kindel, et vastus sellele ei ole enam nii positiivne. Siinkohal tuleme appi Pythoniga.

Esimene ülesanne, mille me peame lahendama lennupiletite teabeotsingusüsteemi loomisel, on sobiva platvormi leidmine, kust me teavet hankime. Selle probleemi lahendamine ei olnud mulle lihtne, kuid lõpuks valisin Kayaki teenuse. Olin proovinud Momondo, Skyscanneri, Expedia ja veel mõningaid teenuseid, kuid nende saitide robotikaitse mehhanismid olid läbimatud. Pärast mitmeid katseid, mille käigus püüdsin süsteeme veenda, et olen inimene, pidin ma tegelema valgusfooride, ülekäiguteede ja jalgratastega. Otsustasin, et Kayak sobib mulle kõige paremini, hoolimata sellest, et ka siin, kui lühikese aja jooksul laadida liiga palju lehti, algavad kontrollid. Mulle õnnestus teha nii, et bot saadaks päringud saidile 4–6 tunni intervallidega, ja kõik töötas normaalselt. Aeg-ajalt tekib Kayakiga töötamisel raskusi, kuid kui teid hakkavad kontrollid vaevama, peate kas nende üle käsitsi rahu saavutama, enne kui bot käivitada, või ootama paar tundi, kuni kontrollid peaksid lõppema. Kui soovite, võite täiesti kohandada koodi uue platvormi jaoks, ja kui te seda teete, võite sellest kommentaarides teada anda.

Kui te alles alustate veebiskrabeeringuga ja ei tea, miks mõned veebisaidid selle vastu tõeliselt võitlevad, siis enne kui asute oma esimest projekti selles valdkonnas käivitama, tehke endale teene ja otsige Google'ist materjale „web scraping etiquette“. Teie katsetused võivad lõppeda kiiremini, kui arvate, kui teete veebiskrabeeringut mõtlematult.

Alustamine

Siin on ülevaade sellest, mis toimub meie veebiskrabeeri koodis:

  • Vajalikud teegid imporditakse.
  • Avatakse Google Chrome'i vahekaart.
  • Kutsutakse välja funktsioon, mis käivitab boti, edastades sellele linnad ja kuupäevad, mida kasutatakse piletite otsingul.
  • See funktsioon hankib esimesed otsingutulemused, mis on sorteeritud suurima atraktiivsuse kriteeriumi (best) järgi, ja klõpsab nuppu lisatulemustega laadimiseks.
  • Veel üks funktsioon kogub andmeid kogu lehelt ja tagastab andmeframe.
  • Kaks eelmist sammu viiakse läbi kasutades pileti hindade (cheap) ja lennu kiirus (fastest) sorteeringu tüüpe.
  • Kasutaja saab skripti kaudu e-kirja, mis sisaldab lühikest kokkuvõtet pileti hindadest (odavaimad piletid ja keskmine hind), samas kui andmeframe, mis sisaldab teavet, sorteerituna kolme eespool mainitud näitaja järgi, salvestatakse Exceli faili.
  • Kõiki ülaltoodud toiminguid teostatakse tsüklis kindla ajavahemiku järel.

Oluline on märkida, et iga Seleniumi projekt algab veebijuhist. Kasutan Chromedriver, töötades Google Chrome’iga, kuid on ka teisi valikuid. Populaarsed on veel PhantomJS ja Firefox. Pärast draiveri allalaadimist tuleb see paigutada vastavasse kausta, mis lõpetab selle kasutamiseks ettevalmistuse. Skripti alguses avame uue Chrome’i vahekaardi.

Pange tähele, et oma jutustuses ei püüa ma avada uusi horisonte odavate lennupiletite leidmiseks. On veelgi paremaid meetodeid sarnaste pakkumiste leidmiseks. Soovin lihtsalt pakkuda selle materjali lugejatele lihtsat, kuid praktiliselt rakendatavat lahendust selle ülesande lahendamiseks.

Siin on kood, millest me eelnevalt 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

# Siin kasutage oma chromedriveri teed!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # See käsk avab Chrome'i akna
sleep(2)

Koodi alguses näete paketihalduskomande, mida kasutatakse kogu meie projektis. Nii et randint kasutatakse selleks, et bot „magaks“ juhusliku arvu sekundeid enne, kui alustatakse uut otsingutegevust. Tavaliselt ei saa ükski bot ilma selleta hakkama. Kui käitada ülaltoodud koodi, avaneb Chrome'i aken, mida bot kasutab veebisaitide haldamiseks.

Teeme väikese eksperimendi ja avame eraldi aknas veebisaidi kayak.com. Valime linna, kust kavatseme lennata, ja linna, kuhu soovime jõuda, samuti lennukuupäevad. Kuupäevade valimisel kontrollime, et vahemik oleks +-3 päeva. Ma kirjutasin koodi, arvestades, mida sellistele päringutele vastab veebisait. Kui soovite näiteks otsida pileteid ainult kindlaksmääratud kuupäevadel, siis on suur tõenäosus, et peate boti koodi kohandama. Rääkides koodist, teen vastavad selgitused, kuid kui tunnete, et jääte hätta — andke mulle teada.

Nüüd vajutame otsingu käivitamise nupule ja vaatame aadressiribal linki. See peaks olema sarnane lingile, mida kasutan allpool olevas näites, kus kuulutatakse välja muutuja kayak, mis hoiab URL-i, ja kasutatakse meetodit get veebi draiveri. Pärast otsingunupule vajutamist peaks lehe tulemustes ilmuma tulemused.

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Kui kasutasin käsku get kaks-kolm korda mitu minutit jooksul pakuti mulle võimalust kinnitada oma identiteeti reCaptchaga. Seda kontrolli saab läbida käsitsi ja jätkata eksperimente, kuni süsteem otsustab korraldada uue kontrolli. Kui ma skripti testisin, tekkis selline tunne, et esimene otsinguseanss läheb alati probleemideta, seega, kui soovite koodiga katsetada, peate vaid perioodiliselt käsitsi kontrolli läbima ja laskma koodil töötada, kasutades pikemaid intervalle otsingute vahel. Ja kui aus olla, siis tõenäoliselt ei vaja inimene piletihindade kohta teavet, mis on saadud 10-minutiliste intervallidega otsinguoperationite vahel.

Töö lehega kasutades XPath

Nii, avasime akna ja laadisime saidi. Hindade ja muu teabe saamiseks peame kasutama XPath-tehnoloogiat või CSS-valijaid. Otsustasin jääda XPath'i juurde ja ei tundnud vajadust CSS-valijate kasutamiseks, kuid täiesti võimalik on töötada ka nii. Leheküljel navigeerimine XPath'i kasutamisel võib osutuda keeruliseks ning isegi kui kasutate neid meetodeid, mida ma varem kirjeldasin sellest artiklis, kus kopeerisin lehe koodist vastavad tuvastajad, sain aru, et see ei ole tegelikult optimaalne viis vajalikesse elementidesse pääsemiseks. Muide, sellest raamatust leiate suurepärase ülevaate sellest, kuidas töötada lehtede lugemisega XPath'i ja CSS-valijate abil. Nii näeb välja vastav veebivahendi meetod.

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Nii, jätkame roboti kallal töötamist. Kasutame programmi võimalusi odavaimate piletite valimiseks. Järgmisel pildil on redigeeritud XPath selektori kood punasega. Koodi vaatamiseks tuleb hiire parema klõpsuga valida huvipakkuv lehe element ja ilmuvast menüüst valida Käsk Koodi vaata (Inspect). Sarnast käsku saab kasutada erinevate lehe elementide jaoks, kelle kood kuvatakse ja esile tõstetakse koodivaates.

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Lehe koodi vaatamine

Minu arutluste puuduste kinnitamise leidmiseks pöörake tähelepanu järgmistele tunnustele.

Nii see välja näeb, kui kopeerite koodi:

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

Sarnase kopeerimiseks tuleb hiire parema klõpsuga valida huvipakkuv koodi osa ja valida ilmuvast menüüst käsk Copy > Copy XPath.

Siin on, mida ma kasutasin Cheapest nupu määratlemiseks:

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

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Käsk Copy > Copy XPath

On täiesti selge, et teine variant näeb palju lihtsam välja. Selle kasutamisel otsitakse a elementi, millel on atribuut data-code, mille väärtus on price. Esimese variandi puhul otsitakse elementi id mille väärtus on wtKI-price_aTab, mille XPath-tee elemendini näeb välja järgmine /div[1]/div/div/div[1]/div/span/span. Selline XPath-päring lehe kohta teeb oma töö, kuid ainult ühe korra. Ma võin kohe öelda, et id muutub järgmise lehe laadimisega. Sümbolite järjestus wtKI muutub iga lehe laadimisega, mis tähendab, et kood, milles seda kasutatakse, muutub pärast järgmist lehe uuesti laadimist kasutuks. Seetõttu võtke aega XPath-iga tutvuda. Need teadmised tulevad teile kasuks.

Siiski tuleb märkida, et XPath-valijate kopeerimine võib osutuda kasulikuks lihtsamate lehtede puhul, ja kui see teile sobib, pole selles midagi halba.

Nüüd mõelgem, kuidas saada kõiki otsingutulemusi mitmes reas, nimekirja sees. See on väga lihtne. Iga tulemus asub objekti sees, mille klass on resultWrapper. Kõikide tulemuste laadimine võib toimuda tsüklis, mis sarnaneb allolevale.

Oluline on märkida, et kui te saate eelnevalt öeldust aru, siis peaksite probleemideta mõistma enamikku koodist, mida me analüüsime. Selle koodi töö käigus, et jõuda vajalikuni (tõepoolest — see on element, kuhu on pakitud tulemus), kasutame me mingit mehhanismi tee näitamiseks (XPath). See toimub, et saada elementide tekst ja paigutada see objekti, millest saab andmeid lugeda. flight_containers, seejärel — flights_list).

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Kuvatakse esimesed kolm rida ja me saame selgelt näha kõike, mis meile vajalik on. Kuid meil on ka huvitavamaid viise teabe saamiseks. Peame andmeid võtma igast elemendist eraldi.

Käime tööle!

Kergem on kirjutada funktsioon, mis laadib täiendavaid tulemusi, seega alustame sellest. Soovin maksimeerida lendude arvu, mille kohta programm teavet saab, ja mitte kahtlusi äratada, mis võivad viia kontrollini, seega klikin ma nuppu "Laadige rohkem tulemusi" iga kord, kui leht kuvatakse. Selle koodi osas tuleks tähelepanu pöörata plokile try, mille ma lisasin selle, kuna mõnikord nupp ei laadi korrektselt. Kui ka sina selle probleemiga kokku puutud — kommentaari välja selle funktsiooni kutsed koodis start_kayak, mida me allpool vaatame.

# Загрузка большего количества результатов для того, чтобы максимизировать объём собираемых данных
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 pikaaegset selle funktsiooni analüüsi (mõnikord võiksin ma isegi ühte asja liialt vaadata), oleme valmis deklareerima funktsiooni, mis tegeleb lehe kraapimisega.

Olen juba kogunud suure osa vajalikust järgmises funktsioonis, mida nimetatakse page_scrape. Mõnikord tagastatud teedetapid on kokku pandud, et neid eraldada, kasutan lihtsat meetodit. Näiteks, kui ma esmakordselt kasutan muutujaid section_a_list ja section_b_list. Meie funktsioon tagastab andmekaadri flights_df, mis võimaldab meil jagada tulemusi, mis on saadud erinevaid andmete sortimise meetodeid kasutades, ja hiljem need uuesti kokku liita.

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

Olen püüdnud nimetada muutujaid nii, et kood oleks arusaadav. Pea meeles, et muutujaid, mis algavad a seostatakse teise etapi teega, ja b — esimesega. Liigume edasi järgmise funktsiooni juurde.

Abimehhanismid

Nüüd on meil funktsioon, mis võimaldab laadida täiendavaid otsingutulemusi ja funktsioon nende tulemuste töötlemiseks. Selle artikli võikski lõpetada, kuna need kaks funktsiooni annavad kõik vajaliku, et lehti kraapida, mida saab iseseisvalt avada. Kuid me ei ole veel arutanud mõningaid abimehhanisme, millest eelnevalt rääkisime. Näiteks — see on kood e-kirjade saatmiseks ja veel mõned asjad. Kõike seda saab leida funktsioonist start_kayak, mida me praegu vaatame.

Selle funktsiooni tööks on vajalikud andmed linnade ja kuupäevade kohta. See, kasutades neid andmeid, loob lingi muutuja kayak, mis kasutatakse lehe avamiseks, kus kuvatakse otsingu tulemused, sorteeritud nende parima vastavuse järgi päringule. Pärast esimest andmete kraapimise seanssi töötame hindadega, mis on lehe ülaosas tabelis. Eelkõige leiame pileti madalaima hinna ja keskmise hinna. Kõik see koos saidi antud prognoosiga saadetakse e-posti teel. Vastav tabel peaks olema lehe vasakus ülanurgas. Töö selle tabeliga võib muide põhjustada viga täpsete kuupäevadega otsimisel, kuna sel juhul ei kuvata lehe tabelit.

def start_kayak(city_from, city_to, date_start, date_end):
    """Linnukoodid - need on IATA koodid!
    Kuupäeva formaat -  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))
    
    # mõnikord ilmub hüpikaken, et seda kontrollida ja sulgeda, saab kasutada try plokki
    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('laadimine....')
    
#     load_more()
    
    print('alustame esimest kraapimist.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Võtame kõige madalama hinna tabelist, mis asub lehe ülaosas
    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('lülitame odavatele tulemusele.....')
    cheap_results = '//a[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('laadimine....')
    
#     load_more()
    
    print('alustame teist kraapimist.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('lülitame kiiretele tulemusele.....')
    quick_results = '//a[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('laadimine....')
    
#     load_more()
    
    print('alustame kolmandat kraapimist.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Uue raami salvestamine Exceli faili, mille nimi kajastab linnu ja kuupäevi
    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('salvestatud df.....')
    
    # Võib jälgida, kuidas veebisaidi prognoos vastab tegelikkusele
    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)
    
    # mõnikord on muutuja loadingis see rida, mis hiljem põhjustab probleeme kirja saatmisega
    # kui see juhtus - muudame selle "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Not sure'
    
    username = 'YOUREMAIL@hotmail.com'
    password = 'YOUR PASSWORD'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Subject: Flight Scrapernn
Odavaim lend: {}nKeskmine hind: {}nnSoovitus: {}nnSõnumi lõpp'.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('saadetud kiri.....')

Testisin selle skripti Outlooki (hotmail.com) konto kasutamisega. Ma ei ole seda Gmaili kontoga testinud, mis on väga populaarne, kuid on palju võimalusi. Kui kasutate Hotmaili kontot, piisab kõikide funktsioonide toimimiseks, kui sisestate koodi oma andmed.

Kui soovite aru saada, mida täpselt täidavad skripti erinevad osad, saate need kopeerida ja nendega eksperimenteerida. Koodiga katsetamine on ainus viis selle põhjalikult mõistmiseks.

Valmis süsteem

Nüüd, kui oleme kõik vajalikud toimingud teinud, saame luua lihtsa tsükli, kus meie funktsioonid kutsutakse välja. Skript küsib kasutajalt linnade ja kuupäevade andmeid. Juhul kui testite skripti pideva taaskäivitamisega, ei soovi te ilmselt iga kord neid andmeid käsitsi sisestada, seega saate vastavad read testimise ajaks kommenteerida, eemaldades kommenteerimise need read, kus vajalikke andmeid on kindlalt määratud.

city_from = input('Millises linnas? ')
city_to = input('Kuhu? ')
date_start = input('Millal otsida? Palun kasutage ainult YYYY-MM-DD formaati ')
date_end = input('Millal naasta? Palun kasutage ainult YYYY-MM-DD formaati ')

# 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('iteratsioon {} on lõppenud @ {}'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Ootame 4 tundi
    sleep(60*60*4)
    print('uni on lõppenud.....')

Nii näeb välja skripti testkäivitamine.
Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.
Skripti testkäivitamine

Kokkuvõte

Kui olete selle punkti jõudnud — õnnitleme! Nüüd on teil toimiv veebiskraber, kuigi näen juba palju viise, kuidas seda täiustada. Näiteks võib selle integreerida Twilioga, et see saadaks tekstsõnumeid e-kirjade asemel. Saate kasutada VPN-i või midagi muud, et saada tulemusi samaaegselt mitmelt serverilt. Samuti on aeg-ajalt tekkinud probleem, kus veebisaidi kasutaja peab tõendama, et ta on inimene, kuid isegi seda saab lahendada. Igal juhul on teil nüüd alus, mida soovikorral saate laiendada. Näiteks, et Exceli fail saadetakse kasutajale e-kirja manusena.

Python — abimees odavate lennupiletite leidmisel neile, kes armastavad reisida.

Ainult registreeritud kasutajad saavad küsitluses osaleda. Logige sisse, palun.

Kas kasutate veebikaevandamise tehnoloogiaid?

  • Jah

  • Ei

Olenenud 8 kasutajalt. 1 kasutaja hoidub.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster