Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti

Straipsnio, kurio vertimą publikuojame šiandien, autorius teigia, kad jo tikslas – pakalbėti apie internetinio skreperio kūrimą Python programoje naudojant Selenium, ieškantį lėktuvų bilietų kainų. Ieškant bilietų, naudojamos lanksčios datos (+- 3 dienos, palyginti su nurodytomis datomis). Grandiklis išsaugo paieškos rezultatus „Excel“ faile ir paiešką vykdžiusiam asmeniui siunčia el. laišką su to, ką rado, santrauka. Šio projekto tikslas – padėti keliautojams rasti geriausius pasiūlymus.

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti

Jei suprasdami medžiagą jaučiatės pasimetę, pažiūrėkite tai straipsnis.

Ko mes ieškosime?

Galite laisvai naudotis čia aprašyta sistema, kaip norite. Pavyzdžiui, aš jį naudojau ieškodamas savaitgalio kelionių ir bilietų į savo gimtąjį miestą. Jei rimtai ieškote pelningų bilietų, galite paleisti scenarijų serveryje (paprasta serverio, už 130 rublių per mėnesį, tam visiškai tinka) ir įsitikinkite, kad jis veikia vieną ar du kartus per dieną. Paieškos rezultatai bus išsiųsti jums el. paštu. Be to, rekomenduoju viską susidėlioti taip, kad scenarijus įrašytų Excel failą su paieškos rezultatais į Dropbox aplanką, kuris leis tokius failus peržiūrėti iš bet kur ir bet kada.

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Tarifų su klaidomis dar neradau, bet manau, kad tai įmanoma

Atliekant paiešką, kaip jau minėta, naudojama „lanksti data“, scenarijus randa pasiūlymus, pateiktus per tris dienas nuo nurodytų datų. Nors paleisdamas scenarijų jis pasiūlymų ieško tik viena kryptimi, jį lengva modifikuoti taip, kad galėtų rinkti duomenis keliomis skrydžių kryptimis. Su jo pagalba netgi galite ieškoti klaidingų tarifų, tokie radiniai gali būti labai įdomūs.

Kodėl jums reikia kito žiniatinklio grandiklio?

Kai pirmą kartą pradėjau rinkti žiniatinklį, manęs tai nelabai domino. Norėjau atlikti daugiau projektų nuspėjamojo modeliavimo, finansinės analizės, galbūt ir emocinio tekstų kolorito analizės srityje. Tačiau pasirodė, kad buvo labai įdomu sugalvoti, kaip sukurti programą, kuri renka duomenis iš svetainių. Kai gilinausi į šią temą, supratau, kad interneto grandymas yra interneto „variklis“.

Galbūt manote, kad tai per drąsus teiginys. Tačiau turėkite omenyje, kad „Google“ pradėjo nuo žiniatinklio grandiklio, kurį Larry Page sukūrė naudodamas „Java“ ir „Python“. „Google“ robotai tyrinėja internetą, bandydami jo vartotojams pateikti geriausius atsakymus į jų klausimus. Žiniatinklio rinkimas gali būti naudojamas be galo daug, ir net jei domitės kuo nors kitu duomenų mokslo srityje, jums reikės tam tikrų duomenų rinkimo įgūdžių, kad gautumėte analizuojamus duomenis.

Kai kuriuos čia naudojamus metodus radau nuostabiai knyga apie žiniatinklio grandymą, kurį neseniai įsigijau. Jame yra daug paprastų pavyzdžių ir idėjų, kaip praktiškai pritaikyti tai, ko išmokote. Be to, yra labai įdomus skyrius, kaip apeiti „reCaptcha“ patikras. Man tai buvo naujiena, nes net nežinojau, kad yra specialių įrankių ir net ištisų paslaugų tokioms problemoms spręsti.

Ar mėgsti keliauti?!

Į paprastą ir gana nekenksmingą klausimą, pateiktą šios rubrikos pavadinime, dažnai galima išgirsti teigiamą atsakymą, kurį lydi pora istorijų iš kelionių to žmogaus, kuriam jis buvo užduotas. Daugelis iš mūsų sutiktų, kad kelionės yra puikus būdas pasinerti į naują kultūrinę aplinką ir praplėsti akiratį. Tačiau jei ko nors paklaustumėte, ar jam patinka ieškoti lėktuvo bilietų, esu tikras, kad atsakymas nebus toks teigiamas. Tiesą sakant, Python čia mums į pagalbą.

Pirmoji užduotis, kurią turėsime išspręsti kuriant informacijos apie lėktuvų bilietus paieškos sistemą, bus tinkamos platformos, iš kurios imsime informaciją, pasirinkimas. Išspręsti šią problemą man nebuvo lengva, bet galiausiai pasirinkau „Kayak“ paslaugą. Išbandžiau Momondo, Skyscanner, Expedia ir keleto kitų paslaugas, tačiau šių išteklių robotų apsaugos mechanizmai buvo neįveikiami. Po kelių bandymų, kurių metu teko susidurti su šviesoforais, pėsčiųjų perėjomis ir dviračiais, bandant įtikinti sistemas, kad esu žmogus, nusprendžiau, kad baidarės man labiausiai tinka, nepaisant to, kad net jei įkeliama per daug puslapių. per trumpą laiką, taip pat prasideda patikrinimai. Man pavyko padaryti, kad robotas siųstų užklausas į svetainę kas 4–6 valandas, ir viskas veikė gerai. Kartkartėmis dirbant su „Kayak“ iškyla sunkumų, tačiau jei jie ima jus varginti su čekiais, tuomet turite su jais susidoroti rankiniu būdu ir paleisti robotą, arba palaukti kelias valandas ir patikrinimai turėtų sustoti. Esant poreikiui, kodą nesunkiai pritaikysite kitai platformai, o jei tai padarysite, apie tai galite pranešti komentaruose.

Jei dar tik pradedate naudoti žiniatinklio duomenų rinkimą ir nežinote, kodėl kai kurioms svetainėms tai sunku, prieš pradėdami savo pirmąjį projektą šioje srityje, padarykite sau paslaugą ir atlikite „Google“ paiešką pagal žodžius „žiniatinklio rinkimo etiketas“ . Jūsų eksperimentai gali baigtis anksčiau, nei manote, jei neprotingai braukysite žiniatinklyje.

Darbo pradžia

Štai bendra apžvalga, kas atsitiks mūsų žiniatinklio grandiklio kode:

  • Importuokite reikiamas bibliotekas.
  • „Google Chrome“ skirtuko atidarymas.
  • Iškvieskite funkciją, kuri paleidžia robotą, perduodant jam miestus ir datas, kurios bus naudojamos ieškant bilietų.
  • Ši funkcija paima pirmuosius paieškos rezultatus, surūšiuotus pagal geriausius, ir spusteli mygtuką, kad įkeltų daugiau rezultatų.
  • Kita funkcija renka duomenis iš viso puslapio ir grąžina duomenų rėmelį.
  • Du ankstesni veiksmai atliekami naudojant rūšiavimo tipus pagal bilieto kainą (pigus) ir pagal skrydžio greitį (greičiausias).
  • Scenarijaus vartotojui išsiunčiamas el. laiškas, kuriame yra bilietų kainų suvestinė (pigiausi bilietai ir vidutinė kaina), o duomenų rėmelis su informacija, surūšiuota pagal tris anksčiau minėtas metrikas, išsaugomas kaip Excel failas.
  • Visi pirmiau minėti veiksmai atliekami ciklu po tam tikro laiko.

Reikėtų pažymėti, kad kiekvienas Selenium projektas prasideda nuo žiniatinklio tvarkyklės. aš naudoju Chromedriver, dirbu su Google Chrome, bet yra ir kitų parinkčių. PhantomJS ir Firefox taip pat yra populiarūs. Atsisiuntę tvarkyklę turite įdėti ją į atitinkamą aplanką, ir tai užbaigia pasirengimą naudoti. Pirmosiose scenarijaus eilutėse atidaromas naujas „Chrome“ skirtukas.

Turėkite omenyje, kad savo istorijoje aš nesistengiu atverti naujų horizontų, kad galėčiau rasti puikių bilietų pasiūlymų. Yra daug pažangesnių tokių pasiūlymų paieškos būdų. Tiesiog noriu šios medžiagos skaitytojams pasiūlyti paprastą, bet praktišką šios problemos sprendimo būdą.

Štai kodas, apie kurį kalbėjome aukščiau.

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)

Kodo pradžioje galite pamatyti paketų importavimo komandas, kurios naudojamos visame mūsų projekte. Taigi, randint naudojamas, kad robotas „užmigtų“ atsitiktiniam sekundžių skaičiui prieš pradedant naują paieškos operaciją. Paprastai be to neapsieina nė vienas robotas. Jei paleisite aukščiau pateiktą kodą, atsidarys „Chrome“ langas, kurį robotas naudos dirbdamas su svetainėmis.

Atlikime nedidelį eksperimentą ir atskirame lange atidarykime kayak.com svetainę. Mes pasirinksime miestą, iš kurio skrisime, ir miestą, į kurį norime patekti, bei skrydžių datas. Renkantis datas atkreipkite dėmesį, kad būtų naudojamas +-3 dienų intervalas. Kodą parašiau atsižvelgdamas į tai, ką svetainė gamina reaguodama į tokius prašymus. Jei, pavyzdžiui, reikia ieškoti bilietų tik nurodytoms datoms, tuomet didelė tikimybė, kad teks keisti boto kodą. Kai kalbu apie kodą, pateikiu atitinkamus paaiškinimus, bet jei jaučiatės sutrikę, praneškite.

Dabar spustelėkite paieškos mygtuką ir pažiūrėkite į nuorodą adreso juostoje. Ji turėtų būti panaši į nuorodą, kurią naudoju toliau pateiktame pavyzdyje, kur deklaruojamas kintamasis kayak, kuriame saugomas URL, ir naudojamas metodas get žiniatinklio tvarkyklė. Paspaudus paieškos mygtuką, puslapyje turėtų pasirodyti rezultatai.

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Kai naudojau komandą get daugiau nei du ar tris kartus per kelias minutes manęs paprašė užbaigti patvirtinimą naudojant „reCaptcha“. Galite atlikti šį patikrinimą rankiniu būdu ir tęsti eksperimentą, kol sistema nuspręs atlikti naują patikrinimą. Kai išbandžiau scenarijų, atrodė, kad pirmoji paieškos sesija visada vyko sklandžiai, todėl, jei norite eksperimentuoti su kodu, turėsite tik periodiškai rankiniu būdu tikrinti ir leisti kodui paleisti, naudojant ilgus intervalus tarp paieškos seansų. Ir, gerai pagalvojus, vargu ar žmogui prireiks informacijos apie bilietų kainas, gaunamas 10 minučių intervalais tarp paieškos operacijų.

Darbas su puslapiu naudojant XPath

Taigi, atidarėme langą ir įkėlėme svetainę. Norėdami gauti kainodarą ir kitą informaciją, turime naudoti XPath technologiją arba CSS selektorius. Nusprendžiau pasilikti prie XPath ir nejaučiau poreikio naudoti CSS selektorių, bet taip dirbti visiškai įmanoma. Naršymas puslapyje naudojant XPath gali būti sudėtingas, net jei naudojate mano aprašytus metodus tai straipsnį, kuriame buvo nukopijuoti atitinkami identifikatoriai iš puslapio kodo, supratau, kad tai iš tikrųjų nėra optimalus būdas pasiekti reikiamus elementus. Beje, į tai Knygoje puikiai aprašomi darbo su puslapiais naudojant XPath ir CSS parinkiklius pagrindai. Taip atrodo atitinkamas žiniatinklio tvarkyklės metodas.

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Taigi, dirbkime su robotu. Pasinaudokime programos galimybėmis išsirinkdami pigiausius bilietus. Kitame paveikslėlyje XPath parinkiklio kodas paryškintas raudonai. Norint peržiūrėti kodą, reikia dešiniuoju pelės mygtuku spustelėti jus dominantį puslapio elementą ir pasirodžiusiame meniu pasirinkti komandą Apžiūrėti. Šią komandą galima iškviesti skirtingiems puslapio elementams, kurių kodas bus rodomas ir paryškintas kodo peržiūros programoje.

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Peržiūrėkite puslapio kodą

Norėdami patvirtinti mano samprotavimus apie selektorių kopijavimo iš kodo trūkumus, atkreipkite dėmesį į šias funkcijas.

Štai ką gausite nukopijuodami kodą:

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

Norint nukopijuoti kažką panašaus, reikia dešiniuoju pelės mygtuku spustelėti jus dominančią kodo skiltį ir pasirodžiusiame meniu pasirinkti komandą Kopijuoti > Kopijuoti XPath.

Štai ką aš naudojau nustatydamas pigiausią mygtuką:

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

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Kopijuoti komandą > Kopijuoti XPath

Visiškai akivaizdu, kad antrasis variantas atrodo daug paprastesnis. Kai naudojamas, jis ieško elemento a, turinčio atributą data-codelygus price. Naudojant pirmąją parinktį, elemento ieškoma id kuri yra lygi wtKI-price_aTab, o XPath kelias į elementą atrodo taip /div[1]/div/div/div[1]/div/span/span. Tokia „XPath“ užklausa puslapyje pasiteisins, bet tik vieną kartą. Galiu pasakyti, kad dabar id pasikeis kitą kartą įkėlus puslapį. Simbolių seka wtKI dinamiškai keičiasi kiekvieną kartą įkeliant puslapį, todėl jį naudojantis kodas bus nenaudingas po kito puslapio įkėlimo iš naujo. Taigi skirkite šiek tiek laiko, kad suprastumėte XPath. Šios žinios jums pasitarnaus.

Tačiau reikia pažymėti, kad XPath selektorių kopijavimas gali būti naudingas dirbant su gana paprastomis svetainėmis, o jei jums tai patinka, nieko blogo.

Dabar pagalvokime, ką daryti, jei visus paieškos rezultatus reikia gauti keliose eilutėse sąraše. Labai paprasta. Kiekvienas rezultatas yra objekte su klase resultWrapper. Visus rezultatus galima įkelti pagal kilpą, panašią į žemiau pateiktą.

Reikėtų pažymėti, kad jei suprasite tai, kas išdėstyta pirmiau, turėtumėte lengvai suprasti didžiąją dalį kodo, kurį analizuosime. Vykdant šiam kodui pasiekiame tai, ko mums reikia (iš tikrųjų elementą, į kurį suvyniotas rezultatas), naudodami tam tikrą kelio nustatymo mechanizmą (XPath). Tai daroma siekiant gauti elemento tekstą ir įdėti jį į objektą, iš kurio galima nuskaityti duomenis (pirmą kartą panaudota flight_containers, tada - flights_list).

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Rodomos pirmosios trys eilutės ir mes aiškiai matome viską, ko mums reikia. Tačiau turime įdomesnių informacijos gavimo būdų. Turime paimti duomenis iš kiekvieno elemento atskirai.

Į darbą!

Paprasčiausias būdas parašyti funkciją yra įkelti papildomų rezultatų, todėl nuo to ir pradėsime. Norėčiau maksimaliai padidinti skrydžių, apie kuriuos programa gauna informaciją, skaičių, nesukeliant įtarimų tarnyboje, kuri veda į patikrinimą, todėl kiekvieną kartą, kai rodomas puslapis, vieną kartą paspaudžiu mygtuką Įkelti daugiau rezultatų. Šiame kode turėtumėte atkreipti dėmesį į bloką try, kurį pridėjau, nes kartais mygtukas neįsikelia tinkamai. Jei taip pat susiduriate su tuo, komentuokite šios funkcijos iškvietimus funkcijos kode start_kayak, kurią apžvelgsime toliau.

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

Dabar, po ilgos šios funkcijos analizės (kartais galiu ir pasinerti), esame pasiruošę paskelbti funkciją, kuri nubrauks puslapį.

Aš jau surinkau didžiąją dalį to, ko reikia šioje funkcijoje, vadinamoje page_scrape. Kartais grąžinami kelio duomenys sujungiami, todėl juos atskiriant naudoju paprastą metodą. Pavyzdžiui, kai pirmą kartą naudoju kintamuosius section_a_list и section_b_list. Mūsų funkcija grąžina duomenų rėmelį flights_df, tai leidžia atskirti gautus rezultatus naudojant skirtingus duomenų rūšiavimo metodus ir vėliau juos sujungti.

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

Stengiausi pavadinti kintamuosius taip, kad kodas būtų suprantamas. Atminkite, kad kintamieji prasideda a priklauso pirmajam kelio etapui ir b - į antrą. Pereikime prie kitos funkcijos.

Palaikymo mechanizmai

Dabar turime funkciją, leidžiančią įkelti papildomų paieškos rezultatų, ir funkciją tiems rezultatams apdoroti. Šis straipsnis galėjo baigtis čia, nes šios dvi funkcijos suteikia viską, ko reikia norint nuskaityti puslapius, kuriuos galite atidaryti patys. Tačiau kai kurių aukščiau aptartų pagalbinių mechanizmų dar nesvarstėme. Pavyzdžiui, tai yra el. laiškų siuntimo ir kai kurių kitų dalykų kodas. Visa tai galima rasti funkcijoje start_kayak, kurį dabar apsvarstysime.

Kad ši funkcija veiktų, reikia informacijos apie miestus ir datas. Naudodamas šią informaciją, jis sudaro nuorodą į kintamąjį kayak, kuris naudojamas nukreipti jus į puslapį, kuriame bus paieškos rezultatai, surūšiuoti pagal geriausiai atitinkančius užklausą. Po pirmo grandymo seanso dirbsime su kainomis puslapio viršuje esančioje lentelėje. Būtent, rasime minimalią bilieto kainą ir vidutinę kainą. Visa tai kartu su svetainės paskelbta prognoze bus išsiųsta el. Puslapyje atitinkama lentelė turi būti viršutiniame kairiajame kampe. Beje, dirbant su šia lentele, ieškant naudojant tikslias datas gali kilti klaida, nes tokiu atveju lentelė puslapyje nerodoma.

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

Išbandžiau šį scenarijų naudodamas „Outlook“ paskyrą (hotmail.com). Neišbandžiau, kad ji tinkamai veiktų su Gmail paskyra, ši el. pašto sistema yra gana populiari, tačiau yra daug galimų variantų. Jei naudojate „Hotmail“ paskyrą, tada, kad viskas veiktų, jums tereikia įvesti savo duomenis į kodą.

Jei norite suprasti, kas tiksliai daroma konkrečiose šios funkcijos kodo dalyse, galite jas nukopijuoti ir eksperimentuoti. Vienintelis būdas iš tikrųjų jį suprasti yra eksperimentavimas su kodu.

Paruošta sistema

Dabar, kai padarėme viską, apie ką kalbėjome, galime sukurti paprastą kilpą, kuri iškviečia mūsų funkcijas. Scenarijus reikalauja duomenų iš vartotojo apie miestus ir datas. Bandydami su nuolatiniu scenarijaus paleidimu iš naujo, vargu ar norėsite kaskart įvesti šiuos duomenis rankiniu būdu, todėl atitinkamas testavimo laikui skirtas eilutes galima komentuoti panaikinus komentarus po jomis esančias, kuriose yra reikalingi duomenys. scenarijus yra užkoduotas.

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

Taip atrodo bandomasis scenarijaus paleidimas.
Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti
Bandomasis scenarijaus paleidimas

rezultatai

Jei jau pasiekėte tiek daug, sveikiname! Dabar turite veikiantį žiniatinklio grandiklį, nors jau matau daug būdų jį patobulinti. Pavyzdžiui, jis gali būti integruotas su „Twilio“, kad siųstų tekstinius pranešimus, o ne el. Galite naudoti VPN ar ką nors kita, kad vienu metu gautumėte rezultatus iš kelių serverių. Taip pat periodiškai iškyla problema tikrinant svetainės naudotoją, ar jis yra asmuo, tačiau šią problemą taip pat galima išspręsti. Bet kokiu atveju dabar turite bazę, kurią galite išplėsti, jei norite. Pavyzdžiui, įsitikinkite, kad Excel failas yra išsiųstas vartotojui kaip el. laiško priedas.

Python – asistentas ieškant nebrangių lėktuvo bilietų mėgstantiems keliauti

Apklausoje gali dalyvauti tik registruoti vartotojai. Prisijungti, Prašau.

Ar naudojate žiniatinklio grandymo technologijas?

  • Taip

  • Ne

Balsavo 8 vartotojai. 1 vartotojas susilaikė.

Šaltinis: www.habr.com

Добавить комментарий