Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje

De skriuwer fan it artikel, de oersetting wêrfan wy hjoed publisearje, seit dat har doel is om te praten oer de ûntwikkeling fan in webskraper yn Python mei Selenium, dy't siket nei prizen foar fleantúchkaarten. By it sykjen nei kaartsjes wurde fleksibele datums brûkt (+- 3 dagen relatyf oan de opjûne datums). De skraper bewarret de sykresultaten yn in Excel-bestân en stjoert de persoan dy't it sykjen hat in e-post mei in gearfetting fan wat se fûn hawwe. It doel fan dit projekt is om reizgers te helpen de bêste deals te finen.

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje

As jo, wylst jo it materiaal begripe, fiele ferlern, sjoch dan ris nei dit lidwurd.

Wat sille wy sykje?

Jo binne frij om it hjir beskreaune systeem te brûken as jo wolle. Bygelyks, ik brûkte it om te sykjen nei wykeintochten en kaartsjes nei myn wenplak. As jo ​​​​serieus binne oer it finen fan rendabele kaartsjes, kinne jo it skript op 'e server útfiere (ienfâldich server, foar 130 roebel per moanne, is hiel geskikt foar dit) en soargje derfoar dat it rint ien of twa kear deis. Sykresultaten wurde nei jo stjoerd fia e-post. Derneist advisearje ik alles yn te stellen sadat it skript in Excel-bestân mei sykresultaten opslaat yn in Dropbox-map, wêrtroch jo sokke bestannen oeral en op elk momint kinne besjen.

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
Ik haw noch gjin tariven fûn mei flaters, mar ik tink dat it mooglik is

By it sykjen, lykas al neamd, wurdt in "fleksibele datum" brûkt, it skript fynt oanbiedingen dy't binnen trije dagen nei de opjûne datums binne. Hoewol by it útfieren fan it skript, it siket nei oanbiedingen yn mar ien rjochting, is it maklik om it te feroarjen sadat it gegevens sammelje kin oer ferskate flechtrjochtingen. Mei har help kinne jo sels sykje nei ferkearde tariven sokke fynsten kinne tige nijsgjirrich wêze.

Wêrom hawwe jo in oare webskraper nedich?

Doe't ik foar it earst begon mei webskrapen, wie ik der earlik net spesjaal ynteressearre yn. Ik woe mear projekten dwaan op it mêd fan foarsizzend modellewurk, finansjele analyze, en mooglik op it mêd fan it analysearjen fan de emosjonele kleuring fan teksten. Mar it die bliken dat it tige nijsgjirrich wie om út te finen hoe't jo in programma meitsje kinne dat gegevens sammelt fan websiden. Doe't ik yn dit ûnderwerp dûkte, realisearre ik dat webskrapping de "motor" fan it ynternet is.

Jo kinne tinke dat dit in te fet útspraak is. Mar tink dat Google begon mei in webskraper dy't Larry Page makke mei Java en Python. Google-robots hawwe it ynternet ferkend, besykje har brûkers de bêste antwurden op har fragen te jaan. Webskrapping hat einleaze gebrûk, en sels as jo ynteressearre binne yn wat oars yn Data Science, sille jo wat skrapfeardigens nedich hawwe om de gegevens te krijen dy't jo moatte analysearje.

Ik fûn guon fan 'e techniken brûkt hjir yn in prachtich it boek oer webskrapen, dy't ik koartlyn oankocht. It befettet in protte ienfâldige foarbylden en ideeën foar praktyske tapassing fan wat jo hawwe leard. Derneist is d'r in heul ynteressant haadstik oer it omgean fan reCaptcha-kontrôles. Dit kaam my as nijs, om't ik net iens wist dat d'r spesjale ark en sels folsleine tsjinsten wiene foar it oplossen fan sokke problemen.

Meisto graach reizgje?!

Op de ienfâldige en frij ûnskuldich fraach steld yn 'e titel fan dizze seksje, kinne jo faaks in posityf antwurd hearre, begelaat troch in pear ferhalen út' e reizen fan 'e persoan oan wa't it waard frege. De measten fan ús soene it iens wêze dat reizgjen in geweldige manier is om josels te dompeljen yn nije kulturele omjouwings en jo horizonten te ferbreedzjen. As jo ​​​​lykwols immen freegje oft se graach sykje nei fleankaarten, bin ik der wis fan dat it antwurd net sa posityf sil wêze. Feitlik komt Python ús hjir te helpen.

De earste taak dy't wy moatte oplosse op 'e wei nei it meitsjen fan in systeem foar it sykjen fan ynformaasje oer fleankaarten, sil in gaadlik platfoarm selektearje wêrfan wy ynformaasje sille nimme. Oplossen fan dit probleem wie net maklik foar my, mar op it lêst keas ik de Kayak tsjinst. Ik besocht de tsjinsten fan Momondo, Skyscanner, Expedia, en in pear oaren, mar de robotbeskermingsmeganismen op dizze boarnen wiene ûntrochsichtich. Nei ferskate besykjen, wêrby't ik te krijen hie mei ferkearsljochten, fuotgongersoergongen en fytsen, besykje de systemen te oertsjûgjen dat ik in minske wie, besleat ik dat Kayak it bêste foar my wie, nettsjinsteande it feit dat sels as Tefolle siden laden wurde yn koarte tiid, en kontrôles begjinne ek. Ik slagge om de bot fersiken nei de side te stjoeren mei yntervallen fan 4 oant 6 oeren, en alles wurke goed. Fan tiid ta tiid ûntsteane swierrichheden by it wurkjen mei Kayak, mar as se jo mei kontrôles begjinne te pesterjen, dan moatte jo of mei de hân omgean en dan de bot starte, of wachtsje in pear oeren en de kontrôles moatte stopje. As it nedich is, kinne jo de koade maklik oanpasse foar in oar platfoarm, en as jo dat dogge, kinne jo it rapportearje yn 'e kommentaren.

As jo ​​​​gewoan begjinne mei webskrapping en net witte wêrom't guon websides dermei wrakselje, dan foardat jo jo earste projekt yn dit gebiet begjinne, doch josels in geunst en sykje in Google-sykje op 'e wurden "web scraping etikette" . Jo eksperiminten kinne earder einigje dan jo tinke as jo ûnferstannich webskraping dogge.

Om te begjinnen

Hjir is in algemien oersjoch fan wat der sil barre yn ús webskraperkoade:

  • Ymportearje de fereaske biblioteken.
  • It iepenjen fan in Google Chrome-ljepper.
  • Rop in funksje dy't de bot begjint, troch de stêden en datums troch te jaan dy't brûkt wurde by it sykjen nei kaartsjes.
  • Dizze funksje nimt de earste sykresultaten, sortearre op bêste, en klikt op in knop om mear resultaten te laden.
  • In oare funksje sammelt gegevens fan 'e heule side en jout in gegevensframe werom.
  • De twa foargeande stappen wurde útfierd mei help fan sortearring typen troch ticket priis (goedkeap) en troch flecht snelheid (fluchste).
  • De brûker fan it skript wurdt stjoerd in e-mail mei in gearfetting fan kaartsjes prizen (goedkeapste kaartsjes en gemiddelde priis), en in gegevens frame mei ynformaasje sortearre troch de trije boppeneamde yndikatoaren wurdt bewarre as in Excel triem.
  • Alle boppesteande aksjes wurde útfierd yn in syklus nei in opjûne perioade fan tiid.

It moat opmurken wurde dat elk Selenium-projekt begjint mei in webbestjoerder. ik brûk Chromedriver, Ik wurkje mei Google Chrome, mar der binne oare opsjes. PhantomJS en Firefox binne ek populêr. Nei it downloaden fan de bestjoerder, moatte jo it yn 'e passende map pleatse, en dit foltôget de tarieding foar it gebrûk. De earste rigels fan ús skript iepenje in nije Chrome-ljepper.

Hâld der rekken mei dat ik yn myn ferhaal net besykje nije horizonten te iepenjen foar it finen fan geweldige deals op fleankaarten. D'r binne folle mear avansearre metoaden foar it sykjen nei sokke oanbiedingen. Ik wol gewoan lêzers fan dit materiaal in ienfâldige, mar praktyske manier oanbiede om dit probleem op te lossen.

Hjir is de koade wêr't wy hjirboppe oer praat hawwe.

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)

Oan it begjin fan 'e koade kinne jo de kommando's foar ymportearje fan pakket sjen dy't yn ús heule projekt wurde brûkt. Sa, randint brûkt om de bot in willekeurich oantal sekonden yn 'e sliep te fallen foardat in nije sykaksje begjint. Normaal kin net ien bot sûnder dit dwaan. As jo ​​​​de boppesteande koade útfiere, sil in Chrome-finster iepenje, dat de bot sil brûke om mei siden te wurkjen.

Litte wy in lyts eksperimint dwaan en de webside kayak.com iepenje yn in apart finster. Wy sille de stêd selektearje wêrfan wy sille fleane, en de stêd wêr't wy wolle komme, lykas de flechtdata. By it kiezen fan datums, soargje derfoar dat it berik fan +-3 dagen wurdt brûkt. Ik skreau de koade mei rekken hâldend mei wat de side produsearret yn reaksje op sokke fersiken. As jo ​​bygelyks allinich foar opjûne datums sykje moatte nei kaartsjes, dan is d'r in hege kâns dat jo de botkoade moatte wizigje. As ik praat oer de koade, Ik jou passende ferklearrings, mar as jo fiele betize, lit my witte.

Klikje no op de sykknop en sjoch nei de keppeling yn 'e adresbalke. It moat lykje op de keppeling dy't ik brûk yn it foarbyld hjirûnder wêr't de fariabele wurdt ferklearre kayak, dy't de URL bewarret, en de metoade wurdt brûkt get web stjoerprogramma. Nei it klikken op de sykknop moatte resultaten op 'e side ferskine.

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
Doe't ik brûkte it kommando get mear as twa of trije kear binnen in pear minuten, waard ik frege om ferifikaasje te foltôgjen mei reCaptcha. Jo kinne dizze kontrôle mei de hân trochjaan en trochgean mei eksperimintearjen oant it systeem beslút om in nije kontrôle út te fieren. Doe't ik it skript hifke, like it dat de earste syksesje altyd soepel gie, dus as jo mei de koade eksperimintearje woene, soene jo allinich periodyk manueel moatte kontrolearje en de koade rinne litte, mei lange yntervallen tusken syksesjes. En, as jo der oer tinke, is it net wierskynlik dat in persoan ynformaasje nedich hat oer ticketprizen ûntfongen op 10-minuten yntervallen tusken sykaksjes.

Wurkje mei in side mei XPath

Dat, wy hawwe in finster iepene en de side laden. Om prizen en oare ynformaasje te krijen, moatte wy XPath-technology of CSS-selektors brûke. Ik besleat om by XPath te bliuwen en fielde net de needsaak om CSS-selektors te brûken, mar it is heul mooglik om sa te wurkjen. Navigearje om in side mei XPath kin lestich wêze, en sels as jo de techniken brûke dy't ik beskreaun yn dit artikel, dy't belutsen by it kopiearjen fan de korrespondearjende identifiers út de side koade, Ik realisearre dat dit is, yn feite, net de optimale manier om tagong ta de nedige eleminten. Troch de wei, yn dit It boek jout in poerbêste beskriuwing fan 'e basis fan wurkjen mei siden mei XPath- en CSS-selektors. Dit is hoe't de oerienkommende metoade foar webbestjoerder derút sjocht.

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
Dat, lit ús trochgean mei wurkjen oan 'e bot. Litte wy de mooglikheden fan it programma brûke om de goedkeapste kaartsjes te selektearjen. Yn 'e folgjende ôfbylding is de XPath-selektorkoade yn read markearre. Om de koade te besjen, moatte jo rjochtsklikke op it side-elemint wêryn jo ynteressearre binne en it kommando Ynspektearje selektearje út it menu dat ferskynt. Dit kommando kin neamd wurde foar ferskate side-eleminten, wêrfan de koade sil wurde werjûn en markearre yn 'e koade werjouwer.

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
View side koade

Om befêstiging te finen fan myn redenearring oer de neidielen fan it kopiearjen fan selectors út koade, betelje omtinken oan de folgjende funksjes.

Dit is wat jo krije as jo de koade kopiearje:

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

Om sa'n ding te kopiearjen, moatte jo rjochts klikke op 'e seksje fan koade wêryn jo ynteressearre binne en selektearje it kommando Kopiearje> Kopiearje XPath út it menu dat ferskynt.

Hjir is wat ik brûkte om de goedkeapste knop te definiearjen:

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

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
Kommando kopiearje> XPath kopiearje

It is dúdlik dat de twadde opsje folle ienfâldiger liket. As it brûkt wurdt, siket it nei in elemint a dat it attribút hat data-code, gelyk price. By it brûken fan de earste opsje wurdt it elemint socht id dat is gelyk oan wtKI-price_aTab, en it XPath-paad nei it elemint liket /div[1]/div/div/div[1]/div/span/span. In XPath-fraach lykas dizze nei in side sil de trúk dwaan, mar mar ien kear. Dat kin ik no sizze id sil feroarje de folgjende kear de side wurdt laden. Karakter folchoarder wtKI feroaret dynamysk elke kear as de side wurdt laden, sadat de koade dy't it brûkt sil nutteloos wêze nei de folgjende side opnij laden. Nim dus wat tiid om XPath te begripen. Dizze kennis sil jo goed tsjinje.

It moat lykwols opmurken wurde dat it kopiearjen fan XPath-selektors nuttich wêze kin as jo wurkje mei frij ienfâldige siden, en as jo hjir noflik binne, is d'r neat mis mei.

Litte wy no tinke oer wat te dwaan as jo alle sykresultaten moatte krije yn ferskate rigels, binnen in list. Hiel ienfâldich. Elk resultaat is binnen in objekt mei in klasse resultWrapper. It laden fan alle resultaten kin dien wurde yn in lus fergelykber mei de hjirûnder werjûn.

It moat opmurken wurde dat as jo it boppesteande begripe, dan moatte jo it measte fan 'e koade maklik begripe dy't wy sille analysearje. As dizze koade rint, krije wy tagong ta wat wy nedich binne (yn feite, it elemint wêryn it resultaat is ferpakt) mei in soarte fan paadspesifisearjend meganisme (XPath). Dit wurdt dien om de tekst fan it elemint te krijen en it yn in objekt te pleatsen wêrfan gegevens lêzen wurde kinne (earst brûkt flight_containers, dan - flights_list).

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
De earste trije rigels wurde werjûn en wy kinne dúdlik sjen alles wat wy nedich hawwe. Wy hawwe lykwols mear nijsgjirrige manieren om ynformaasje te krijen. Wy moatte gegevens fan elk elemint apart nimme.

Gean oan'e slach!

De maklikste manier om in funksje te skriuwen is om ekstra resultaten te laden, dus dêr sille wy begjinne. Ik wol it oantal flechten maksimalisearje wêr't it programma ynformaasje oer ûntfangt, sûnder fertochten te wekken yn 'e tsjinst dy't liedt ta in ynspeksje, dus ik klik ien kear op de knop Mear resultaten laden elke kear as de side wurdt werjûn. Yn dizze koade moatte jo omtinken jaan oan it blok try, dy't ik tafoege om't soms de knop net goed lade. As jo ​​ek tsjinkomme dit, kommentaar út oproppen nei dizze funksje yn de funksje koade start_kayak, dêr't wy hjirûnder sille sjen.

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

No, nei in lange analyze fan dizze funksje (soms kin ik meidwaan), binne wy ​​ree om in funksje te ferklearjen dy't de side sil skrape.

Ik haw al sammele measte fan wat nedich is yn de folgjende funksje neamd page_scrape. Soms wurde de weromjûne paadgegevens kombineare, dus ik brûk in ienfâldige metoade om it te skieden. Bygelyks as ik foar it earst fariabelen brûke section_a_list и section_b_list. Us funksje jout in gegevensframe werom flights_df, Hjirmei kinne wy ​​de resultaten skieden dy't krigen binne fan ferskate metoaden foar gegevenssortering en letter kombinearje.

def page_scrape():
    """This function takes care of the scraping part"""
    
    xp_sections = '//*[@class="section duration"]'
    sections = driver.find_elements_by_xpath(xp_sections)
    sections_list = [value.text for value in sections]
    section_a_list = sections_list[::2] # так мы разделяем информацию о двух полётах
    section_b_list = sections_list[1::2]
    
    # Если вы наткнулись на reCaptcha, вам может понадобиться что-то предпринять.
    # О том, что что-то пошло не так, вы узнаете исходя из того, что вышеприведённые списки пусты
    # это выражение if позволяет завершить работу программы или сделать ещё что-нибудь
    # тут можно приостановить работу, что позволит вам пройти проверку и продолжить скрапинг
    # я использую тут SystemExit так как хочу протестировать всё с самого начала
    if section_a_list == []:
        raise SystemExit
    
    # Я буду использовать букву A для уходящих рейсов и B для прибывающих
    a_duration = []
    a_section_names = []
    for n in section_a_list:
        # Получаем время
        a_section_names.append(''.join(n.split()[2:5]))
        a_duration.append(''.join(n.split()[0:2]))
    b_duration = []
    b_section_names = []
    for n in section_b_list:
        # Получаем время
        b_section_names.append(''.join(n.split()[2:5]))
        b_duration.append(''.join(n.split()[0:2]))

    xp_dates = '//div[@class="section date"]'
    dates = driver.find_elements_by_xpath(xp_dates)
    dates_list = [value.text for value in dates]
    a_date_list = dates_list[::2]
    b_date_list = dates_list[1::2]
    # Получаем день недели
    a_day = [value.split()[0] for value in a_date_list]
    a_weekday = [value.split()[1] for value in a_date_list]
    b_day = [value.split()[0] for value in b_date_list]
    b_weekday = [value.split()[1] for value in b_date_list]
    
    # Получаем цены
    xp_prices = '//a[@class="booking-link"]/span[@class="price option-text"]'
    prices = driver.find_elements_by_xpath(xp_prices)
    prices_list = [price.text.replace('$','') for price in prices if price.text != '']
    prices_list = list(map(int, prices_list))

    # stops - это большой список, в котором первый фрагмент пути находится по чётному индексу, а второй - по нечётному
    xp_stops = '//div[@class="section stops"]/div[1]'
    stops = driver.find_elements_by_xpath(xp_stops)
    stops_list = [stop.text[0].replace('n','0') for stop in stops]
    a_stop_list = stops_list[::2]
    b_stop_list = stops_list[1::2]

    xp_stops_cities = '//div[@class="section stops"]/div[2]'
    stops_cities = driver.find_elements_by_xpath(xp_stops_cities)
    stops_cities_list = [stop.text for stop in stops_cities]
    a_stop_name_list = stops_cities_list[::2]
    b_stop_name_list = stops_cities_list[1::2]
    
    # сведения о компании-перевозчике, время отправления и прибытия для обоих рейсов
    xp_schedule = '//div[@class="section times"]'
    schedules = driver.find_elements_by_xpath(xp_schedule)
    hours_list = []
    carrier_list = []
    for schedule in schedules:
        hours_list.append(schedule.text.split('n')[0])
        carrier_list.append(schedule.text.split('n')[1])
    # разделяем сведения о времени и о перевозчиках между рейсами a и b
    a_hours = hours_list[::2]
    a_carrier = carrier_list[1::2]
    b_hours = hours_list[::2]
    b_carrier = carrier_list[1::2]

    
    cols = (['Out Day', 'Out Time', 'Out Weekday', 'Out Airline', 'Out Cities', 'Out Duration', 'Out Stops', 'Out Stop Cities',
            'Return Day', 'Return Time', 'Return Weekday', 'Return Airline', 'Return Cities', 'Return Duration', 'Return Stops', 'Return Stop Cities',
            'Price'])

    flights_df = pd.DataFrame({'Out Day': a_day,
                               'Out Weekday': a_weekday,
                               'Out Duration': a_duration,
                               'Out Cities': a_section_names,
                               'Return Day': b_day,
                               'Return Weekday': b_weekday,
                               'Return Duration': b_duration,
                               'Return Cities': b_section_names,
                               'Out Stops': a_stop_list,
                               'Out Stop Cities': a_stop_name_list,
                               'Return Stops': b_stop_list,
                               'Return Stop Cities': b_stop_name_list,
                               'Out Time': a_hours,
                               'Out Airline': a_carrier,
                               'Return Time': b_hours,
                               'Return Airline': b_carrier,                           
                               'Price': prices_list})[cols]
    
    flights_df['timestamp'] = strftime("%Y%m%d-%H%M") # время сбора данных
    return flights_df

Ik besocht de fariabelen te neamen sadat de koade begryplik wêze soe. Unthâld dat fariabelen begjinnend mei a hearre ta de earste etappe fan it paad, en b - nei de twadde. Litte wy nei de folgjende funksje gean.

Stipe meganismen

Wy hawwe no in funksje wêrmei wy ekstra sykresultaten kinne laden en in funksje om dy resultaten te ferwurkjen. Dit artikel koe hjir einigje, om't dizze twa funksjes alles leverje wat jo nedich binne om siden te skraapjen dy't jo sels kinne iepenje. Mar guon fan 'e hjirboppe besprutsen helpmeganismen hawwe wy noch net beskôge. Dit is bygelyks de koade foar it ferstjoeren fan e-mails en wat oare dingen. Dit alles is te finen yn 'e funksje start_kayak, dy't wy no sille beskôgje.

Om dizze funksje te wurkjen hawwe jo ynformaasje nedich oer stêden en datums. Mei dizze ynformaasje foarmet it in keppeling yn in fariabele kayak, dy't brûkt wurdt om jo nei in side te bringen dy't sykresultaten sil befetsje, sortearre op har bêste oerienkomst mei de query. Nei de earste skrapsesje sille wy wurkje mei de prizen yn 'e tabel oan' e boppekant fan 'e side. Wy fine nammentlik de minimale ticketpriis en de gemiddelde priis. Dit alles, tegearre mei de foarsizzing útjûn troch de side, sil per e-post stjoerd wurde. Op 'e side moat de oerienkommende tabel yn' e boppeste linker hoeke wêze. Wurkje mei dizze tabel kin trouwens in flater feroarsaakje by it sykjen mei krekte datums, om't yn dit gefal de tabel net op 'e side wurdt werjûn.

def start_kayak(city_from, city_to, date_start, date_end):
    """City codes - it's the IATA codes!
    Date format -  YYYY-MM-DD"""
    
    kayak = ('https://www.kayak.com/flights/' + city_from + '-' + city_to +
             '/' + date_start + '-flexible/' + date_end + '-flexible?sort=bestflight_a')
    driver.get(kayak)
    sleep(randint(8,10))
    
    # иногда появляется всплывающее окно, для проверки на это и его закрытия можно воспользоваться блоком try
    try:
        xp_popup_close = '//button[contains(@id,"dialog-close") and contains(@class,"Button-No-Standard-Style close ")]'
        driver.find_elements_by_xpath(xp_popup_close)[5].click()
    except Exception as e:
        pass
    sleep(randint(60,95))
    print('loading more.....')
    
#     load_more()
    
    print('starting first scrape.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Возьмём самую низкую цену из таблицы, расположенной в верхней части страницы
    matrix = driver.find_elements_by_xpath('//*[contains(@id,"FlexMatrixCell")]')
    matrix_prices = [price.text.replace('$','') for price in matrix]
    matrix_prices = list(map(int, matrix_prices))
    matrix_min = min(matrix_prices)
    matrix_avg = sum(matrix_prices)/len(matrix_prices)
    
    print('switching to cheapest results.....')
    cheap_results = '//a[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting second scrape.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('switching to quickest results.....')
    quick_results = '//a[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('loading more.....')
    
#     load_more()
    
    print('starting third scrape.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Сохранение нового фрейма в Excel-файл, имя которого отражает города и даты
    final_df = df_flights_cheap.append(df_flights_best).append(df_flights_fast)
    final_df.to_excel('search_backups//{}_flights_{}-{}_from_{}_to_{}.xlsx'.format(strftime("%Y%m%d-%H%M"),
                                                                                   city_from, city_to, 
                                                                                   date_start, date_end), index=False)
    print('saved df.....')
    
    # Можно следить за тем, как прогноз, выдаваемый сайтом, соотносится с реальностью
    xp_loading = '//div[contains(@id,"advice")]'
    loading = driver.find_element_by_xpath(xp_loading).text
    xp_prediction = '//span[@class="info-text"]'
    prediction = driver.find_element_by_xpath(xp_prediction).text
    print(loading+'n'+prediction)
    
    # иногда в переменной loading оказывается эта строка, которая, позже, вызывает проблемы с отправкой письма
    # если это прозошло - меняем её на "Not Sure"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'Not sure'
    
    username = '[email protected]'
    password = 'YOUR PASSWORD'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Subject: Flight Scrapernn
Cheapest Flight: {}nAverage Price: {}nnRecommendation: {}nnEnd of message'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
    message = MIMEMultipart()
    message['From'] = '[email protected]'
    message['to'] = '[email protected]'
    server.sendmail('[email protected]', '[email protected]', msg)
    print('sent email.....')

Ik hifke dit skript mei in Outlook-akkount (hotmail.com). Ik haw it net testen om goed te wurkjen mei in Gmail akkount, dit e-postsysteem is frij populêr, mar d'r binne in protte mooglike opsjes. As jo ​​​​in Hotmail-akkount brûke, dan moatte jo gewoan jo gegevens yn 'e koade ynfiere om alles te wurkjen.

As jo ​​​​wolle begripe wat der krekt wurdt dien yn spesifike seksjes fan 'e koade foar dizze funksje, kinne jo se kopiearje en mei har eksperimintearje. Eksperimintearjen mei de koade is de ienige manier om it wirklik te begripen.

Klear systeem

No't wy alles dien hawwe wêr't wy oer praat hawwe, kinne wy ​​​​in ienfâldige loop meitsje dy't ús funksjes neamt. It skript freget gegevens fan de brûker oer stêden en datums. As jo ​​​​testen mei konstante werstart fan it skript, is it net wierskynlik dat jo dizze gegevens elke kear mei de hân ynfiere wolle, sadat de oerienkommende rigels, foar de tiid fan testen, kinne wurde kommentearre troch dejingen dy't ûnder har te kommentearjen, wêryn de gegevens nedich binne troch de skript is hardcoded.

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't in testrun fan it skript derút sjocht.
Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje
Testrun fan it skript

Resultaten

As jo ​​​​it sa fier binne makke, lokwinsken! Jo hawwe no in wurkjende webskraper, hoewol ik al in protte manieren kin sjen om it te ferbetterjen. Bygelyks, it kin wurde yntegrearre mei Twilio sadat it stjoert tekst berjochten ynstee fan e-mails. Jo kinne in VPN of wat oars brûke om tagelyk resultaten fan ferskate servers te ûntfangen. D'r is ek in periodyk ûntstien probleem mei it kontrolearjen fan de side-brûker om te sjen oft hy in persoan is, mar dit probleem kin ek oplost wurde. Yn alle gefallen, no hawwe jo in basis dy't jo kinne útwreidzje as jo wolle. Soargje der bygelyks foar dat in Excel-bestân nei de brûker stjoerd wurdt as in taheaksel oan in e-post.

Python - in assistint by it finen fan goedkeape fleankaarten foar dyjingen dy't graach reizgje

Allinnich registrearre brûkers kinne meidwaan oan 'e enkête. Ynlogge, asjebleaft.

Brûk jo technologyen foar webskrapping?

  • dat

  • gjin

8 brûkers stimden. 1 brûker ûnthâlde him.

Boarne: www.habr.com

Add a comment