Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească

Autorul articolului, a cărui traducere o publicăm astăzi, spune că scopul său este să vorbească despre dezvoltarea unui web scraper în Python folosind Selenium, care caută prețurile biletelor de avion. La căutarea biletelor se folosesc date flexibile (+- 3 zile față de datele specificate). Scraperul salvează rezultatele căutării într-un fișier Excel și îi trimite persoanei care a efectuat căutarea un e-mail cu un rezumat a ceea ce a găsit. Scopul acestui proiect este de a ajuta călătorii să găsească cele mai bune oferte.

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească

Dacă, în timp ce înțelegi materialul, te simți pierdut, aruncă o privire la acest articol.

Ce căutăm?

Sunteți liber să utilizați sistemul descris aici după cum doriți. De exemplu, l-am folosit pentru a căuta tururi de weekend și bilete în orașul meu natal. Dacă sunteți serios să găsiți bilete profitabile, puteți rula scriptul pe server (simplu serverului, pentru 130 de ruble pe lună, este destul de potrivit pentru aceasta) și asigurați-vă că rulează o dată sau de două ori pe zi. Rezultatele căutării vă vor fi trimise prin e-mail. În plus, recomand să setați totul astfel încât scriptul să salveze un fișier Excel cu rezultatele căutării într-un folder Dropbox, ceea ce vă va permite să vizualizați astfel de fișiere de oriunde și oricând.

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Nu am gasit inca tarife cu erori, dar cred ca se poate

La căutare, așa cum sa menționat deja, se folosește o „dată flexibilă”; scriptul găsește oferte care sunt în termen de trei zile de la datele date. Deși atunci când rulează scriptul, acesta caută oferte într-o singură direcție, este ușor să îl modifici, astfel încât să poată colecta date pe mai multe direcții de zbor. Cu ajutorul acestuia, puteți chiar să căutați tarife eronate; astfel de descoperiri pot fi foarte interesante.

De ce ai nevoie de un alt răzuitor web?

Când am început pentru prima dată web scraping, sincer nu eram deosebit de interesat de asta. Mi-am dorit să fac mai multe proiecte în domeniul modelării predictive, al analizei financiare și, eventual, în domeniul analizării colorării emoționale a textelor. Dar s-a dovedit că a fost foarte interesant să ne dăm seama cum să creăm un program care colectează date de pe site-uri web. Pe măsură ce am aprofundat în acest subiect, mi-am dat seama că web scraping este „motorul” internetului.

S-ar putea să credeți că aceasta este o afirmație prea îndrăzneață. Dar luați în considerare că Google a început cu un web scraper pe care Larry Page l-a creat folosind Java și Python. Roboții Google au explorat internetul, încercând să ofere utilizatorilor săi cele mai bune răspunsuri la întrebările lor. Web scraping are utilizări nesfârșite și, chiar dacă sunteți interesat de altceva în Data Science, veți avea nevoie de niște abilități de scraping pentru a obține datele pe care trebuie să le analizați.

Am găsit câteva dintre tehnicile folosite aici într-un mod minunat cartea despre web scraping, pe care l-am achiziționat recent. Conține multe exemple și idei simple pentru aplicarea practică a ceea ce ați învățat. În plus, există un capitol foarte interesant despre ocolirea verificărilor reCaptcha. Acest lucru a venit ca o veste pentru mine, deoarece nici nu știam că există instrumente speciale și chiar servicii întregi pentru rezolvarea unor astfel de probleme.

Îți place să călătorești?!

La întrebarea simplă și destul de inofensivă pusă în titlul acestei secțiuni, puteți auzi adesea un răspuns pozitiv, însoțit de câteva povești din călătoriile persoanei căreia i s-a cerut. Cei mai mulți dintre noi ar fi de acord că călătoriile sunt o modalitate excelentă de a te cufunda în noi medii culturale și de a-ți lărgi orizonturile. Cu toate acestea, dacă întrebi pe cineva dacă îi place să caute bilete de avion, sunt sigur că răspunsul nu va fi atât de pozitiv. De fapt, Python ne vine în ajutor aici.

Prima sarcină pe care trebuie să o rezolvăm în drumul spre crearea unui sistem de căutare a informațiilor despre biletele de avion va fi selectarea unei platforme adecvate de pe care vom prelua informații. Rezolvarea acestei probleme nu mi-a fost ușoară, dar până la urmă am ales serviciul Kayak. Am încercat serviciile Momondo, Skyscanner, Expedia și alte câteva, dar mecanismele de protecție a roboților de pe aceste resurse erau impenetrabile. După mai multe încercări, în timpul cărora am avut de-a face cu semafoare, treceri de pietoni și biciclete, încercând să conving sistemele că sunt om, am decis că Kayak-ul este cel mai potrivit pentru mine, în ciuda faptului că, chiar dacă sunt încărcate Prea multe pagini în scurt timp și încep și verificările. Am reușit să fac botul să trimită cereri către site la intervale de 4 până la 6 ore și totul a funcționat bine. Din când în când, apar dificultăți atunci când lucrați cu Kayak, dar dacă încep să vă deranjeze cu verificări, atunci trebuie fie să vă ocupați de ele manual și apoi să lansați botul, fie să așteptați câteva ore și verificările ar trebui să înceteze. Dacă este necesar, puteți adapta cu ușurință codul pentru o altă platformă, iar dacă faceți acest lucru, îl puteți raporta în comentarii.

Если вы только начинаете знакомство с веб-скрапингом, и не знаете о том, почему некоторые веб-сайты всеми силами с ним борются, то, прежде чем приступать к своему первому проекту в этой области — окажите себе услугу и поищите в Google материалы по словам «web scraping etiquette». Ваши эксперименты могут завершиться скорее, чем вы думаете, в том случае, если вы будете заниматься веб-скрапингом неразумно.

Noțiuni de bază

Iată o prezentare generală a ceea ce se va întâmpla în codul nostru web scraper:

  • Importați bibliotecile necesare.
  • Deschiderea unei file Google Chrome.
  • Вызов функции, которая запускает бота, передавая ему города и даты, которые будут использоваться при поиске билетов.
  • Эта функция получает первые результаты поиска, отсортированные по критерию наибольшей привлекательности (best), и нажимает на кнопку для загрузки дополнительных результатов.
  • O altă funcție colectează date de pe întreaga pagină și returnează un cadru de date.
  • Два предыдущих шага выполняются с использованием типов сортировки по цене билетов (cheap) и по скорости перелёта (fastest).
  • Utilizatorului scriptului i se trimite un e-mail care conține un rezumat al prețurilor biletelor (biletele cele mai ieftine și prețul mediu), iar un cadru de date cu informații sortate după cei trei indicatori menționați mai sus este salvat ca fișier Excel.
  • Toate acțiunile de mai sus sunt efectuate într-un ciclu după o anumită perioadă de timp.

Trebuie remarcat faptul că fiecare proiect Selenium începe cu un driver web. eu folosesc Chromedriver, lucrez cu Google Chrome, dar există și alte opțiuni. PhantomJS și Firefox sunt, de asemenea, populare. După descărcarea driverului, trebuie să îl plasați în folderul corespunzător, iar acest lucru completează pregătirea pentru utilizarea acestuia. Primele rânduri ale scriptului nostru deschid o nouă filă Chrome.

Rețineți că în povestea mea nu încerc să deschid noi orizonturi pentru a găsi oferte grozave la biletele de avion. Există metode mult mai avansate de căutare a unor astfel de oferte. Vreau doar să ofer cititorilor acestui material o modalitate simplă, dar practică de a rezolva această problemă.

Iată codul despre care am vorbit mai sus.

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)

В начале кода можно видеть команды импорта пакетов, которые используются во всём нашем проекте. Так, randint folosit pentru a face botul „să adoarmă” pentru un număr aleatoriu de secunde înainte de a începe o nouă operațiune de căutare. De obicei, niciun bot nu se poate descurca fără acest lucru. Dacă rulați codul de mai sus, se va deschide o fereastră Chrome, pe care botul o va folosi pentru a lucra cu site-urile.

Să facem un mic experiment și să deschidem site-ul kayak.com într-o fereastră separată. Vom selecta orașul din care vom zbura și orașul în care dorim să ajungem, precum și datele zborului. Atunci când alegeți datele, asigurați-vă că este utilizat intervalul de +-3 zile. Am scris codul ținând cont de ceea ce produce site-ul ca răspuns la astfel de solicitări. Dacă, de exemplu, trebuie să căutați bilete numai pentru datele specificate, atunci există o mare probabilitate ca va trebui să modificați codul bot. Când vorbesc despre cod, ofer explicații adecvate, dar dacă te simți confuz, anunțați-mă.

Acum faceți clic pe butonul de căutare și priviți linkul din bara de adrese. Ar trebui să fie similar cu linkul pe care îl folosesc în exemplul de mai jos în care este declarată variabila kayak, care stochează adresa URL și este folosită metoda get driver web. După ce faceți clic pe butonul de căutare, rezultatele ar trebui să apară pe pagină.

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Când am folosit comanda get de mai mult de două sau trei ori în câteva minute, mi s-a cerut să finalizez verificarea folosind reCaptcha. Puteți trece manual această verificare și puteți continua experimentarea până când sistemul decide să execute o nouă verificare. Când am testat scriptul, mi s-a părut că prima sesiune de căutare a mers întotdeauna fără probleme, așa că, dacă ai vrea să experimentezi cu codul, ar trebui doar să verifici manual periodic și să lași codul să ruleze, folosind intervale lungi între sesiunile de căutare. Și, dacă vă gândiți bine, este puțin probabil ca o persoană să aibă nevoie de informații despre prețurile biletelor primite la intervale de 10 minute între operațiunile de căutare.

Lucrul cu o pagină folosind XPath

Deci, am deschis o fereastră și am încărcat site-ul. Pentru a obține prețuri și alte informații, trebuie să folosim tehnologia XPath sau selectoare CSS. Am decis să rămân cu XPath și nu am simțit nevoia să folosesc selectoare CSS, dar este foarte posibil să funcționeze așa. Navigarea într-o pagină folosind XPath poate fi dificilă și chiar dacă utilizați tehnicile pe care le-am descris acest articol, care presupunea copierea identificatorilor corespunzători din codul paginii, mi-am dat seama că aceasta nu este, de fapt, modalitatea optimă de a accesa elementele necesare. Apropo, în acest книге можно найти отличное описание основ работы со страницами с использованием XPath и CSS-селекторов. Вот как выглядит соответствующий метод веб-драйвера.

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Deci, să continuăm să lucrăm la bot. Să folosim capacitățile programului pentru a selecta cele mai ieftine bilete. În imaginea următoare, codul selector XPath este evidențiat cu roșu. Pentru a vizualiza codul, trebuie să faceți clic dreapta pe elementul de pagină care vă interesează și să selectați comanda Inspectare din meniul care apare. Această comandă poate fi apelată pentru diferite elemente de pagină, al căror cod va fi afișat și evidențiat în vizualizatorul de cod.

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Vizualizați codul paginii

Для того чтобы найти подтверждение моим рассуждениям о недостатках копирования селекторов из кода, обратите внимание на следующие особенности.

Iată ce obțineți când copiați codul:

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

Для того чтобы скопировать нечто подобное, нужно щёлкнуть правой кнопкой мыши по интересующему вас участку кода и выбрать в появившемся меню команду Copy > Copy XPath.

Iată ce am folosit pentru a defini butonul Cele mai ieftine:

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

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Copiați comanda > Copiați XPath

Совершенно очевидно то, что второй вариант выглядит гораздо проще. При его использовании выполняется поиск элемента a, у которого есть атрибут data-code, egal price. Când utilizați prima opțiune, elementul este căutat id care este egal cu wtKI-price_aTab, iar calea XPath către element arată ca /div[1]/div/div/div[1]/div/span/span. Подобный XPath-запрос к странице сделает своё дело, но — лишь один раз. Я прямо сейчас могу сказать, что id se va schimba data viitoare când pagina este încărcată. Secvența de caractere wtKI se modifică dinamic de fiecare dată când pagina este încărcată, astfel încât codul care o folosește va fi inutil după următoarea reîncărcare a paginii. Așa că fă-ți puțin timp pentru a înțelege XPath. Aceste cunoștințe vă vor fi de folos.

Cu toate acestea, trebuie remarcat faptul că copierea selectoarelor XPath poate fi utilă atunci când lucrați cu site-uri destul de simple și, dacă vă simțiți confortabil cu acest lucru, nu este nimic în neregulă.

Acum să ne gândim ce să facem dacă trebuie să obțineți toate rezultatele căutării pe mai multe rânduri, în interiorul unei liste. Foarte simplu. Fiecare rezultat este în interiorul unui obiect cu o clasă resultWrapper. Încărcarea tuturor rezultatelor se poate face într-o buclă similară cu cea prezentată mai jos.

Trebuie remarcat faptul că, dacă înțelegeți cele de mai sus, atunci ar trebui să înțelegeți cu ușurință majoritatea codului pe care îl vom analiza. Pe măsură ce rulează acest cod, accesăm ceea ce avem nevoie (de fapt, elementul în care este înfășurat rezultatul) folosind un fel de mecanism de specificare a căii (XPath). Acest lucru se face pentru a obține textul elementului și a-l plasa într-un obiect din care pot fi citite date (utilizat prima dată flight_containers, atunci - flights_list).

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Sunt afișate primele trei rânduri și putem vedea clar tot ce avem nevoie. Cu toate acestea, avem modalități mai interesante de a obține informații. Trebuie să luăm datele de la fiecare element separat.

Treci la treabă!

Cel mai simplu mod de a scrie o funcție este să încărcați rezultate suplimentare, așa că de aici vom începe. Aș dori să maximizez numărul de zboruri despre care programul primește informații, fără a ridica suspiciuni în serviciul care duce la o inspecție, așa că dau clic pe butonul Încarcă mai multe rezultate o dată de fiecare dată când pagina este afișată. În acest cod, ar trebui să acordați atenție blocului try, который я добавил из-за того, что иногда кнопка нормально не загружается. Если вы тоже с этим столкнётесь — закомментируйте вызовы этой функции в коде функции start_kayak, pe care ne vom uita mai jos.

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

Теперь, после долгого разбора этой функции (иногда я могу и увлечься), мы готовы к тому, чтобы объявить функцию, которая будет заниматься скрапингом страницы.

Am colectat deja cea mai mare parte a ceea ce este necesar în următoarea funcție numită page_scrape. Иногда возвращаемые данные об этапах пути оказываются объединёнными, для их разделения я использую простой метод. Например, когда в первый раз пользуюсь переменными section_a_list и section_b_list. Funcția noastră returnează un cadru de date flights_df, это позволяет нам разделить результаты, полученные при использовании разных методов сортировки данных, а позже — объединить их.

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

Я постарался называть переменные так, чтобы код оказался бы понятным. Помните о том, что переменные, начинающиеся с a aparțin primei etape a drumului și b - la al doilea. Să trecem la următoarea funcție.

Mecanisme de sprijin

Acum avem o funcție care ne permite să încărcăm rezultate de căutare suplimentare și o funcție pentru a procesa acele rezultate. Acest articol s-ar fi putut încheia aici, deoarece aceste două funcții oferă tot ce aveți nevoie pentru a răzui paginile pe care le puteți deschide singur. Dar nu am luat în considerare încă unele dintre mecanismele auxiliare discutate mai sus. De exemplu, acesta este codul pentru trimiterea de e-mailuri și alte lucruri. Toate acestea pot fi găsite în funcție start_kayak, pe care acum îl vom lua în considerare.

Для работы этой функции нужны сведения о городах и датах. Она, используя эти сведения, формирует ссылку в переменной kayak, care este folosit pentru a vă duce la o pagină care va conține rezultate de căutare sortate după cea mai bună potrivire la interogare. După prima sesiune de scraping, vom lucra cu prețurile din tabelul din partea de sus a paginii. Și anume, vom găsi prețul minim al biletului și prețul mediu. Toate acestea, alături de predicția emisă de site, vor fi trimise prin email. Pe pagină, tabelul corespunzător ar trebui să fie în colțul din stânga sus. Lucrul cu acest tabel, apropo, poate provoca o eroare la căutarea folosind datele exacte, deoarece în acest caz tabelul nu este afișat pe pagină.

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

Am testat acest script folosind un cont Outlook (hotmail.com). Nu l-am testat să funcționeze corect cu un cont Gmail, acest sistem de e-mail este destul de popular, dar există multe opțiuni posibile. Dacă utilizați un cont Hotmail, atunci pentru ca totul să funcționeze, trebuie doar să introduceți datele în cod.

Dacă doriți să înțelegeți exact ce se face în anumite secțiuni ale codului pentru această funcție, le puteți copia și experimenta cu ele. Experimentarea codului este singura modalitate de a-l înțelege cu adevărat.

Sistem gata

Acum că am făcut tot ce am vorbit, putem crea o buclă simplă care ne apelează funcțiile. Scriptul solicită utilizatorului date despre orașe și date. Când testați cu repornirea constantă a scriptului, este puțin probabil să doriți să introduceți aceste date manual de fiecare dată, astfel încât rândurile corespunzătoare, pentru momentul testării, pot fi comentate prin decomentarea celor de sub ele, în care datele necesare scriptul este codificat.

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

Așa arată o rulare de probă a scriptului.
Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească
Executare de testare a scriptului

Rezultatele

Dacă ai ajuns până aici, felicitări! Acum aveți un web scraper care funcționează, deși văd deja multe modalități de a-l îmbunătăți. De exemplu, poate fi integrat cu Twilio, astfel încât să trimită mesaje text în loc de e-mailuri. Puteți folosi un VPN sau altceva pentru a primi simultan rezultate de la mai multe servere. Există, de asemenea, o problemă care apare periodic cu verificarea utilizatorului site-ului pentru a vedea dacă este o persoană, dar și această problemă poate fi rezolvată. În orice caz, acum ai o bază pe care o poți extinde dacă dorești. De exemplu, asigurați-vă că un fișier Excel este trimis utilizatorului ca atașament la un e-mail.

Python - un asistent în găsirea de bilete de avion ieftine pentru cei cărora le place să călătorească

Numai utilizatorii înregistrați pot participa la sondaj. Loghează-te, Vă rog.

Folosiți tehnologii web scraping?

  • Da

  • Nu

Au votat 8 utilizatori. 1 utilizator s-a abținut.

Sursa: www.habr.com

Adauga un comentariu