Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot

Raksta, kura tulkojumu šodien publicējam, autore stāsta, ka tā mērķis ir runāt par tīmekļa skrāpja izstrādi Python, izmantojot Selenium, kas meklē aviobiļešu cenas. Meklējot biļetes, tiek izmantoti elastīgi datumi (+- 3 dienas attiecībā pret norādītajiem datumiem). Skrāpis saglabā meklēšanas rezultātus Excel failā un nosūta personai, kas veica meklēšanu, e-pasta ziņojumu ar kopsavilkumu par atrasto. Šī projekta mērķis ir palīdzēt ceļotājiem atrast labākos piedāvājumus.

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot

Ja, izprotot materiālu, jūtaties apmaldījies, ieskatieties šis rakstu.

Ko mēs meklēsim?

Jūs varat brīvi izmantot šeit aprakstīto sistēmu, kā vēlaties. Piemēram, es to izmantoju, lai meklētu nedēļas nogales ekskursijas un biļetes uz savu dzimto pilsētu. Ja jūs nopietni domājat atrast ienesīgas biļetes, varat palaist skriptu serverī (vienkāršs serveri, par 130 rubļiem mēnesī, ir diezgan piemērots šim) un pārliecinieties, ka tas darbojas vienu vai divas reizes dienā. Meklēšanas rezultāti tiks nosūtīti jums pa e-pastu. Turklāt es iesaku visu iestatīt tā, lai skripts saglabātu Excel failu ar meklēšanas rezultātiem Dropbox mapē, kas ļaus jums apskatīt šādus failus no jebkuras vietas un jebkurā laikā.

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Tarifus ar kļūdām vēl neesmu atradis, bet domāju, ka tas ir iespējams

Meklējot, kā jau minēts, tiek izmantots “elastīgs datums”, skripts atrod piedāvājumus, kas ir trīs dienu laikā no norādītajiem datumiem. Lai gan, izpildot skriptu, tas meklē piedāvājumus tikai vienā virzienā, to ir viegli pārveidot, lai tas varētu apkopot datus par vairākiem lidojumu virzieniem. Ar tās palīdzību jūs pat varat meklēt kļūdainus tarifus, šādi atradumi var būt ļoti interesanti.

Kāpēc jums ir nepieciešams cits tīmekļa skrāpis?

Kad es pirmo reizi sāku skrāpēt tīmekli, mani tas īpaši neinteresēja. Es gribēju veikt vairāk projektu prognozēšanas modelēšanas, finanšu analīzes un, iespējams, tekstu emocionālās krāsojuma analīzes jomā. Bet izrādījās, ka bija ļoti interesanti izdomāt, kā izveidot programmu, kas apkopo datus no vietnēm. Iedziļinoties šajā tēmā, es sapratu, ka tīmekļa skrāpēšana ir interneta “dzinējs”.

Jums var šķist, ka tas ir pārāk drosmīgs apgalvojums. Taču ņemiet vērā, ka Google sāka ar tīmekļa skrāpi, ko Lerijs Peidžs izveidoja, izmantojot Java un Python. Google roboti ir pētījuši internetu, cenšoties sniegt lietotājiem vislabākās atbildes uz viņu jautājumiem. Tīmekļa skrāpēšanai ir bezgalīgi pielietojumi, un pat tad, ja jūs interesē kaut kas cits datu zinātnē, jums būs nepieciešamas dažas kopšanas prasmes, lai iegūtu analizējamos datus.

Es atklāju dažas no šeit izmantotajām metodēm brīnišķīgā grāmata par tīmekļa nokasīšanu, ko nesen iegādājos. Tajā ir daudz vienkāršu piemēru un ideju apgūtā praktiskai pielietošanai. Turklāt ir ļoti interesanta nodaļa par reCaptcha pārbaužu apiešanu. Tas man bija jaunums, jo es pat nezināju, ka šādu problēmu risināšanai ir īpaši rīki un pat veseli pakalpojumi.

Vai tev patīk ceļot?!

Uz šīs sadaļas nosaukumā uzdoto vienkāršo un visai nekaitīgo jautājumu nereti var dzirdēt pozitīvu atbildi, kurai pievienoti pāris stāsti no tā cilvēka ceļojumiem, kuram tas tika uzdots. Lielākā daļa no mums piekrīt, ka ceļošana ir lielisks veids, kā iegremdēties jaunā kultūras vidē un paplašināt savu redzesloku. Taču, ja kādam jautājat, vai viņam patīk meklēt aviobiļetes, esmu pārliecināts, ka atbilde nebūs tik pozitīva. Faktiski Python mums šeit nāk palīgā.

Pirmais uzdevums, kas mums jāatrisina ceļā uz aviobiļešu informācijas meklēšanas sistēmas izveidi, būs piemērotas platformas izvēle, no kuras mēs ņemsim informāciju. Šīs problēmas risināšana man nebija viegla, bet galu galā izvēlējos Kayak servisu. Es izmēģināju Momondo, Skyscanner, Expedia un dažus citus pakalpojumus, taču robotu aizsardzības mehānismi šajos resursos bija necaurlaidīgi. Pēc vairākiem mēģinājumiem, kuru laikā man nācās saskarties ar luksoforiem, gājēju pārejām un velosipēdiem, mēģinot pārliecināt sistēmas, ka esmu cilvēks, es nolēmu, ka Kayak man ir vispiemērotākais, neskatoties uz to, ka pat tad, ja tiek ielādēts pārāk daudz lapu. īsā laikā, un sākas arī pārbaudes. Man izdevās panākt, ka robots sūta pieprasījumus vietnei ar intervālu no 4 līdz 6 stundām, un viss darbojās labi. Laiku pa laikam, strādājot ar Kayak, rodas grūtības, taču, ja viņi sāk jūs apgrūtināt ar čekiem, jums ir vai nu jātiek galā ar tiem manuāli un pēc tam jāpalaiž bots, vai arī jāpagaida dažas stundas, un pārbaudes jāpārtrauc. Ja nepieciešams, varat ērti pielāgot kodu citai platformai, un, ja to darāt, varat ziņot par to komentāros.

Ja jūs tikko sākat darbu ar tīmekļa skrāpēšanu un nezināt, kāpēc dažas vietnes ar to saskaras, tad, pirms sākat savu pirmo projektu šajā jomā, izdariet sev pakalpojumu un veiciet Google meklēšanu ar vārdiem "tīmekļa skrāpēšanas etiķete". . Jūsu eksperimenti var beigties ātrāk, nekā jūs domājat, ja tīmeklī veiksit nepārdomāti.

Darba sākšana

Tālāk ir sniegts vispārīgs pārskats par to, kas notiks mūsu tīmekļa skrāpja kodā.

  • Importējiet vajadzīgās bibliotēkas.
  • Google Chrome cilnes atvēršana.
  • Izsauciet funkciju, kas palaiž robotu, nododot tam pilsētas un datumus, kas tiks izmantoti, meklējot biļetes.
  • Šī funkcija ņem pirmos meklēšanas rezultātus, kas sakārtoti pēc labākā, un noklikšķina uz pogas, lai ielādētu vairāk rezultātu.
  • Cita funkcija apkopo datus no visas lapas un atgriež datu rāmi.
  • Divas iepriekšējās darbības tiek veiktas, izmantojot šķirošanas veidus pēc biļetes cenas (lētās) un lidojuma ātruma (ātrākais).
  • Skripta lietotājam tiek nosūtīts e-pasts ar biļešu cenu kopsavilkumu (lētākās biļetes un vidējā cena), kā arī datu rāmis ar informāciju, kas sakārtota pēc trim augstākminētajiem rādītājiem, tiek saglabāts kā Excel fails.
  • Visas iepriekš minētās darbības tiek veiktas ciklā pēc noteikta laika perioda.

Jāatzīmē, ka katrs Selēna projekts sākas ar tīmekļa draiveri. ES izmantoju Chromedriver, Es strādāju ar Google Chrome, taču ir arī citas iespējas. Populāri ir arī PhantomJS un Firefox. Pēc draivera lejupielādes tas jāievieto attiecīgajā mapē, un tas pabeidz sagatavošanos tā lietošanai. Mūsu skripta pirmajās rindās tiek atvērta jauna Chrome cilne.

Paturiet prātā, ka savā stāstā es nemēģinu atvērt jaunus apvāršņus, lai atrastu lieliskus aviobiļešu piedāvājumus. Šādu piedāvājumu meklēšanai ir daudz progresīvākas metodes. Es tikai vēlos piedāvāt šī materiāla lasītājiem vienkāršu, bet praktisku veidu, kā atrisināt šo problēmu.

Šeit ir kods, par kuru mēs runājām iepriekš.

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)

Koda sākumā varat redzēt pakotņu importēšanas komandas, kas tiek izmantotas visā mūsu projektā. Tātad, randint izmanto, lai bots “aizmigtu” uz nejaušu sekunžu skaitu pirms jaunas meklēšanas darbības uzsākšanas. Parasti bez tā nevar iztikt neviens robots. Ja palaižat iepriekš minēto kodu, tiks atvērts Chrome logs, kuru robots izmantos darbam ar vietnēm.

Veiksim nelielu eksperimentu un atsevišķā logā atveram vietni kayak.com. Mēs izvēlēsimies pilsētu, no kuras lidosim, un pilsētu, kurā vēlamies nokļūt, kā arī lidojuma datumus. Izvēloties datumus, ņemiet vērā, ka tiek izmantots +-3 dienu diapazons. Es uzrakstīju kodu, ņemot vērā to, ko vietne ražo, reaģējot uz šādiem pieprasījumiem. Ja, piemēram, jāmeklē biļetes tikai uz noteiktiem datumiem, tad pastāv liela varbūtība, ka būs jāmaina bota kods. Kad es runāju par kodu, es sniedzu atbilstošus paskaidrojumus, bet, ja jūtaties apmulsis, dariet to man zināmu.

Tagad noklikšķiniet uz meklēšanas pogas un apskatiet saiti adreses joslā. Tai vajadzētu būt līdzīgai saitei, kuru izmantoju tālāk esošajā piemērā, kur tiek deklarēts mainīgais kayak, kurā tiek saglabāts URL, un tiek izmantota metode get tīmekļa draiveris. Pēc meklēšanas pogas noklikšķināšanas lapā vajadzētu parādīties rezultātiem.

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Kad es izmantoju komandu get vairāk nekā divas vai trīs reizes dažu minūšu laikā man tika lūgts pabeigt verifikāciju, izmantojot reCaptcha. Varat izturēt šo pārbaudi manuāli un turpināt eksperimentēt, līdz sistēma nolemj veikt jaunu pārbaudi. Kad es testēju skriptu, šķita, ka pirmā meklēšanas sesija vienmēr noritēja gludi, tāpēc, ja vēlaties eksperimentēt ar kodu, jums tikai periodiski manuāli jāpārbauda un jāļauj kodam palaist, izmantojot ilgus intervālus starp meklēšanas sesijām. Un, ja tā padomā, diez vai cilvēkam būs vajadzīga informācija par biļešu cenām, kas saņemta 10 minūšu intervālos starp meklēšanas darbībām.

Darbs ar lapu, izmantojot XPath

Tātad, mēs atvērām logu un ielādējām vietni. Lai iegūtu cenas un citu informāciju, mums ir jāizmanto XPath tehnoloģija vai CSS atlasītāji. Es nolēmu palikt pie XPath un nejutu vajadzību izmantot CSS atlasītājus, taču tas ir pilnīgi iespējams strādāt šādā veidā. Navigācija lapā, izmantojot XPath, var būt sarežģīta, pat ja izmantojat manis aprakstītās metodes šis rakstu, kas ietvēra atbilstošo identifikatoru kopēšanu no lapas koda, sapratu, ka patiesībā tas nav optimālais veids, kā piekļūt nepieciešamajiem elementiem. Starp citu, iekšā šis Grāmatā ir sniegts lielisks apraksts par pamatiem darbam ar lapām, izmantojot XPath un CSS atlasītājus. Šādi izskatās atbilstošā tīmekļa draivera metode.

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Tātad, turpināsim strādāt pie robotprogrammas. Izmantosim programmas iespējas, lai izvēlētos lētākās biļetes. Nākamajā attēlā XPath atlasītāja kods ir iezīmēts sarkanā krāsā. Lai skatītu kodu, ar peles labo pogu noklikšķiniet uz interesējošā lapas elementa un parādītajā izvēlnē atlasiet komandu Pārbaudīt. Šo komandu var izsaukt dažādiem lapas elementiem, kuru kods tiks parādīts un izcelts koda skatītājā.

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Skatīt lapas kodu

Lai atrastu apstiprinājumu maniem argumentiem par trūkumiem, kopējot atlasītājus no koda, pievērsiet uzmanību šādām funkcijām.

Šis ir tas, ko jūs saņemat, kopējot kodu:

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

Lai nokopētu kaut ko līdzīgu, ar peles labo pogu noklikšķiniet uz interesējošās koda sadaļas un parādītajā izvēlnē atlasiet komandu Kopēt > Kopēt XPath.

Lūk, ko izmantoju, lai definētu pogu Lētākā:

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

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Kopēt komandu > Kopēt XPath

Ir pilnīgi skaidrs, ka otrā iespēja izskatās daudz vienkāršāka. Kad tiek izmantots, tas meklē elementu a, kuram ir atribūts data-codevienāds ar price. Izmantojot pirmo opciju, elements tiek meklēts id kas ir vienāds ar wtKI-price_aTab, un XPath ceļš uz elementu izskatās šādi /div[1]/div/div/div[1]/div/span/span. Šāds XPath vaicājums lapai palīdzēs, taču tikai vienu reizi. To es varu teikt šobrīd id mainīsies nākamreiz, kad lapa tiks ielādēta. Rakstzīmju secība wtKI dinamiski mainās katru reizi, kad lapa tiek ielādēta, tāpēc kods, kas to izmanto, pēc nākamās lapas atkārtotas ielādes būs bezjēdzīgs. Tāpēc veltiet laiku, lai saprastu XPath. Šīs zināšanas jums noderēs.

Tomēr jāņem vērā, ka XPath atlasītāju kopēšana var būt noderīga, strādājot ar diezgan vienkāršām vietnēm, un, ja tas jums patīk, nav nekā slikta.

Tagad padomāsim, kā rīkoties, ja visi meklēšanas rezultāti jāiekļauj vairākās rindās sarakstā. Ļoti vienkārši. Katrs rezultāts atrodas objektā ar klasi resultWrapper. Visu rezultātu ielādi var veikt cilpā, kas ir līdzīga zemāk parādītajai.

Jāatzīmē, ka, ja jūs saprotat iepriekš minēto, jums vajadzētu viegli saprast lielāko daļu koda, kuru mēs analizēsim. Kad šis kods darbojas, mēs piekļūstam tam, kas mums nepieciešams (faktiski elementam, kurā tiek iesaiņots rezultāts), izmantojot sava veida ceļa noteikšanas mehānismu (XPath). Tas tiek darīts, lai iegūtu elementa tekstu un ievietotu to objektā, no kura var nolasīt datus (pirmo reizi flight_containers, tad - flights_list).

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Tiek parādītas pirmās trīs rindas, un mēs varam skaidri redzēt visu, kas mums nepieciešams. Tomēr mums ir interesantāki informācijas iegūšanas veidi. Mums ir jāņem dati no katra elementa atsevišķi.

Ķeries pie darba!

Vienkāršākais veids, kā rakstīt funkciju, ir ielādēt papildu rezultātus, tāpēc mēs sāksim ar to. Es vēlos maksimāli palielināt lidojumu skaitu, par kuriem programma saņem informāciju, neradot aizdomas par pakalpojumu, kas noved pie pārbaudes, tāpēc katru reizi, kad tiek parādīta lapa, noklikšķiniet uz pogas Ielādēt vairāk rezultātu. Šajā kodā jums vajadzētu pievērst uzmanību blokam try, ko pievienoju, jo dažreiz poga netiek pareizi ielādēta. Ja arī jūs ar to saskaraties, komentējiet šīs funkcijas izsaukumus funkcijas kodā start_kayak, ko mēs apskatīsim tālāk.

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

Tagad, pēc ilgstošas ​​šīs funkcijas analīzes (dažreiz es varu aizrauties), mēs esam gatavi deklarēt funkciju, kas nokasīs lapu.

Es jau esmu apkopojis lielāko daļu nepieciešamās funkcijas šādā funkcijā page_scrape. Dažreiz atgrieztie ceļa dati tiek apvienoti, tāpēc es izmantoju vienkāršu metodi, lai tos atdalītu. Piemēram, kad es pirmo reizi izmantoju mainīgos section_a_list и section_b_list. Mūsu funkcija atgriež datu rāmi flights_df, tas ļauj atdalīt no dažādām datu šķirošanas metodēm iegūtos rezultātus un vēlāk tos apvienot.

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

Mēģināju nosaukt mainīgos, lai kods būtu saprotams. Atcerieties, ka mainīgie sākas ar a pieder pie pirmā ceļa posma, un b - uz otro. Pāriesim pie nākamās funkcijas.

Atbalsta mehānismi

Tagad mums ir funkcija, kas ļauj ielādēt papildu meklēšanas rezultātus, un funkcija šo rezultātu apstrādei. Šis raksts varēja beigties šeit, jo šīs divas funkcijas nodrošina visu nepieciešamo, lai nokasītu lapas, kuras varat atvērt pats. Bet mēs vēl neesam apsvēruši dažus no iepriekš apspriestajiem palīgmehānismiem. Piemēram, šis ir kods e-pasta sūtīšanai un dažām citām lietām. To visu var atrast funkcijā start_kayak, ko mēs tagad apsvērsim.

Lai šī funkcija darbotos, nepieciešama informācija par pilsētām un datumiem. Izmantojot šo informāciju, tas veido saiti mainīgajā kayak, kas tiek izmantota, lai novirzītu jūs uz lapu, kurā būs meklēšanas rezultāti, kas sakārtoti pēc to vislabākās atbilstības vaicājumam. Pēc pirmās skrāpēšanas sesijas mēs strādāsim ar cenām tabulā lapas augšpusē. Proti, atradīsim minimālo biļetes cenu un vidējo cenu. Tas viss kopā ar vietnes sniegto prognozi tiks nosūtīts pa e-pastu. Lapas augšējā kreisajā stūrī ir jāatrodas atbilstošajai tabulai. Starp citu, strādājot ar šo tabulu, var rasties kļūda, meklējot, izmantojot precīzus datumus, jo šajā gadījumā tabula lapā netiek parādīta.

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

Es pārbaudīju šo skriptu, izmantojot Outlook kontu (hotmail.com). Neesmu pārbaudījis, lai tā pareizi darbotos ar Gmail kontu, šī e-pasta sistēma ir diezgan populāra, taču ir daudz iespēju. Ja izmantojat Hotmail kontu, tad, lai viss darbotos, kodā ir jāievada savi dati.

Ja vēlaties saprast, kas tieši tiek darīts konkrētās šīs funkcijas koda sadaļās, varat tās kopēt un eksperimentēt ar tām. Eksperimentēšana ar kodu ir vienīgais veids, kā to patiesi saprast.

Gatava sistēma

Tagad, kad esam paveikuši visu, par ko runājām, varam izveidot vienkāršu cilpu, kas izsauc mūsu funkcijas. Skripts pieprasa datus no lietotāja par pilsētām un datumiem. Veicot testēšanu ar pastāvīgu skripta restartēšanu, jūs, visticamāk, nevēlaties katru reizi ievadīt šos datus manuāli, tāpēc atbilstošās rindas testēšanas laikam var tikt komentētas, atņemot komentārus zem tām esošās, kurās ir nepieciešami dati skripts ir kodēts.

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

Šādi izskatās skripta testa palaišana.
Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot
Skripta testa palaišana

Rezultāti

Ja esat tik tālu tikuši, apsveicam! Tagad jums ir darbojošs tīmekļa skrāpis, lai gan es jau redzu daudzus veidus, kā to uzlabot. Piemēram, to var integrēt ar Twilio, lai tas sūtītu īsziņas, nevis e-pastus. Varat izmantot VPN vai kaut ko citu, lai vienlaikus saņemtu rezultātus no vairākiem serveriem. Periodiski rodas arī problēma, pārbaudot vietnes lietotāju, vai viņš ir persona, taču arī šo problēmu var atrisināt. Jebkurā gadījumā tagad jums ir bāze, kuru varat paplašināt, ja vēlaties. Piemēram, pārliecinieties, vai Excel fails tiek nosūtīts lietotājam kā e-pasta pielikums.

Python - palīgs lētu aviobiļešu atrašanā tiem, kam patīk ceļot

Aptaujā var piedalīties tikai reģistrēti lietotāji. Ielogoties, lūdzu.

Vai izmantojat tīmekļa skrāpēšanas tehnoloģijas?

Nobalsoja 8 lietotāji. 1 lietotājs atturējās.

Avots: www.habr.com

Pievieno komentāru