Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar

L'autor de l'article, la traducció del qual publiquem avui, diu que el seu objectiu és parlar del desenvolupament d'un web scraper en Python amb Selenium, que cerca preus de bitllets d'avió. En cercar entrades, s'utilitzen dates flexibles (+- 3 dies en relació amb les dates especificades). El rascador desa els resultats de la cerca en un fitxer Excel i envia a la persona que va fer la cerca un correu electrònic amb un resum del que va trobar. L'objectiu d'aquest projecte és ajudar els viatgers a trobar les millors ofertes.

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar

Si, mentre entens el material, et sents perdut, fes-hi una ullada això article.

Què anem a buscar?

Podeu utilitzar el sistema que es descriu aquí com vulgueu. Per exemple, el vaig fer servir per buscar visites de cap de setmana i entrades a la meva ciutat natal. Si voleu trobar entrades rendibles, podeu executar l'script al servidor (simple servidor, per 130 rubles al mes, és molt adequat per a això) i assegureu-vos que funcioni una o dues vegades al dia. Els resultats de la cerca s'enviaran per correu electrònic. A més, us recomano configurar-ho tot perquè l'script desi un fitxer Excel amb els resultats de la cerca en una carpeta de Dropbox, que us permetrà visualitzar aquests fitxers des de qualsevol lloc i en qualsevol moment.

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Encara no he trobat tarifes amb errors, però crec que és possible

Quan es cerca, com ja s'ha dit, s'utilitza una "data flexible"; l'script troba ofertes que es troben dins dels tres dies següents a les dates indicades. Encara que quan s'executa l'script, cerca ofertes només en una direcció, és fàcil modificar-lo perquè pugui recollir dades en diverses direccions de vol. Amb la seva ajuda, fins i tot podeu buscar tarifes errònies; aquestes troballes poden ser molt interessants.

Per què necessiteu un altre rascador web?

Quan vaig començar a rascar web, sincerament, no m'interessava especialment. Volia fer més projectes en l'àmbit de la modelització predictiva, l'anàlisi financera i, possiblement, en l'àmbit de l'anàlisi de la coloració emocional dels textos. Però va resultar que era molt interessant esbrinar com crear un programa que reculli dades de llocs web. Quan vaig aprofundir en aquest tema, em vaig adonar que el web scraping és el "motor" d'Internet.

Potser penseu que aquesta és una afirmació massa agosarada. Però tingueu en compte que Google va començar amb un rascador web que Larry Page va crear amb Java i Python. Els robots de Google han estat explorant Internet, intentant oferir als seus usuaris les millors respostes a les seves preguntes. El raspat web té infinitat d'usos i, fins i tot si esteu interessats en alguna cosa més a Data Science, necessitareu algunes habilitats de raspat per obtenir les dades que necessiteu analitzar.

He trobat algunes de les tècniques utilitzades aquí en un meravellós el llibre sobre el raspat web, que vaig adquirir recentment. Conté molts exemples i idees senzills per a l'aplicació pràctica del que has après. A més, hi ha un capítol molt interessant sobre eludir les comprovacions de reCaptcha. Això va ser una notícia per a mi, ja que ni tan sols sabia que hi havia eines especials i fins i tot serveis sencers per resoldre aquests problemes.

T'agrada viatjar?!

A la pregunta senzilla i més aviat inofensiva que es planteja al títol d'aquesta secció, sovint es pot escoltar una resposta positiva, acompanyada d'un parell d'històries dels viatges de la persona a qui se li va preguntar. La majoria de nosaltres estaríem d'acord que viatjar és una bona manera de submergir-se en nous entorns culturals i ampliar els vostres horitzons. Tanmateix, si pregunteu a algú si li agrada buscar bitllets d'avió, estic segur que la resposta no serà tan positiva. De fet, Python ens ajuda aquí.

La primera tasca que hem de resoldre en el camí per crear un sistema de cerca d'informació sobre bitllets d'avió serà seleccionar una plataforma adequada de la qual agafarem la informació. Solucionar aquest problema no em va ser fàcil, però al final vaig triar el servei de Kayak. Vaig provar els serveis de Momondo, Skyscanner, Expedia i uns quants més, però els mecanismes de protecció del robot d'aquests recursos eren impenetrables. Després de diversos intents, durant els quals vaig haver de fer front a semàfors, passos de vianants i bicicletes, intentant convèncer els sistemes que jo era humà, vaig decidir que Kayak era el més adequat per a mi, malgrat que encara que es carreguessin massa pàgines. en poc temps, i també comencen els controls. Vaig aconseguir que el bot enviés sol·licituds al lloc a intervals de 4 a 6 hores i tot va funcionar bé. De tant en tant, sorgeixen dificultats quan es treballa amb Kayak, però si comencen a molestar-vos amb les comprovacions, haureu de tractar-les manualment i després iniciar el bot, o esperar unes hores i les comprovacions haurien d'aturar-se. Si cal, podeu adaptar fàcilment el codi a una altra plataforma i, si ho feu, ho podeu informar als comentaris.

Si acabeu de començar amb el web scraping i no saps per què alguns llocs web lluiten, abans d'iniciar el teu primer projecte en aquesta àrea, fes-te un favor i fes una cerca a Google amb les paraules "etiqueta del web scraping". . Els vostres experiments poden acabar abans del que penseu si feu un raspat web de manera imprudent.

primers passos

Aquí teniu una visió general del que passarà al nostre codi web scraper:

  • Importa les biblioteques necessàries.
  • Obrint una pestanya de Google Chrome.
  • Truqueu a una funció que iniciï el bot, passant-li les ciutats i les dates que s'utilitzaran a l'hora de cercar entrades.
  • Aquesta funció agafa els primers resultats de la cerca, ordenats per millor, i fa clic en un botó per carregar més resultats.
  • Una altra funció recull dades de tota la pàgina i retorna un marc de dades.
  • Els dos passos anteriors es realitzen utilitzant tipus d'ordenació per preu del bitllet (barat) i per velocitat de vol (més ràpid).
  • A l'usuari de l'script s'envia un correu electrònic amb un resum dels preus de les entrades (entrades més barates i preu mitjà) i es desa com a fitxer Excel un marc de dades amb la informació ordenada pels tres indicadors esmentats anteriorment.
  • Totes les accions anteriors es realitzen en un cicle després d'un període de temps determinat.

Cal tenir en compte que cada projecte de Selenium comença amb un controlador web. jo utilitzo Chromedriver, treballo amb Google Chrome, però hi ha altres opcions. PhantomJS i Firefox també són populars. Després de descarregar el controlador, cal col·locar-lo a la carpeta adequada, i això completa la preparació per al seu ús. Les primeres línies del nostre script obren una nova pestanya de Chrome.

Tingueu en compte que a la meva història no estic intentant obrir nous horitzons per trobar grans ofertes en bitllets d'avió. Hi ha mètodes molt més avançats per cercar aquestes ofertes. Només vull oferir als lectors d'aquest material una manera senzilla però pràctica de resoldre aquest problema.

Aquí teniu el codi del qual hem parlat anteriorment.

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)

Al principi del codi podeu veure les ordres d'importació de paquets que s'utilitzen al llarg del nostre projecte. Tan, randint s'utilitza per fer que el bot "s'adormi" durant un nombre aleatori de segons abans d'iniciar una nova operació de cerca. Normalment, cap bot pot prescindir d'això. Si executeu el codi anterior, s'obrirà una finestra de Chrome, que el bot utilitzarà per treballar amb els llocs.

Fem un petit experiment i obrim el lloc web kayak.com en una finestra independent. Seleccionarem la ciutat des de la qual volem i la ciutat a la qual volem arribar, així com les dates de vol. Quan trieu les dates, assegureu-vos que s'utilitza l'interval de +-3 dies. Vaig escriure el codi tenint en compte el que produeix el lloc en resposta a aquestes peticions. Si, per exemple, necessiteu cercar entrades només per a dates especificades, hi ha una gran probabilitat que hàgiu de modificar el codi del bot. Quan parlo del codi, dono les explicacions adequades, però si us sentiu confós, feu-m'ho saber.

Ara feu clic al botó de cerca i mireu l'enllaç a la barra d'adreces. Hauria de ser similar a l'enllaç que faig servir a l'exemple següent on es declara la variable kayak, que emmagatzema l'URL i s'utilitza el mètode get controlador web. Després de fer clic al botó de cerca, els resultats haurien d'aparèixer a la pàgina.

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Quan vaig utilitzar l'ordre get més de dues o tres vegades en pocs minuts, em van demanar que completes la verificació mitjançant reCaptcha. Podeu passar aquesta comprovació manualment i continuar experimentant fins que el sistema decideixi executar una nova comprovació. Quan vaig provar l'script, semblava que la primera sessió de cerca sempre anava sense problemes, de manera que si volíeu experimentar amb el codi, només haureu de comprovar manualment periòdicament i deixar que el codi s'executi, utilitzant intervals llargs entre sessions de cerca. I, si hi penseu bé, és poc probable que una persona necessiti informació sobre els preus dels bitllets rebuts a intervals de 10 minuts entre les operacions de cerca.

Treballant amb una pàgina amb XPath

Per tant, vam obrir una finestra i vam carregar el lloc. Per obtenir preus i altra informació, hem d'utilitzar la tecnologia XPath o els selectors CSS. Vaig decidir quedar-me amb XPath i no vaig sentir la necessitat d'utilitzar selectors CSS, però és molt possible que funcioni així. Navegar per una pàgina amb XPath pot ser complicat, i fins i tot si feu servir les tècniques que he descrit això article, que implicava copiar els identificadors corresponents del codi de la pàgina, em vaig adonar que aquesta no és, de fet, la manera òptima d'accedir als elements necessaris. Per cert, en això El llibre ofereix una descripció excel·lent dels fonaments bàsics de treballar amb pàgines amb selectors XPath i CSS. Així és el mètode de controlador web corresponent.

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Per tant, continuem treballant en el bot. Utilitzem les capacitats del programa per seleccionar les entrades més barates. A la imatge següent, el codi del selector XPath està ressaltat en vermell. Per veure el codi, heu de fer clic amb el botó dret a l'element de la pàgina que us interessa i seleccionar l'ordre Inspeccionar al menú que apareix. Aquesta ordre es pot cridar per a diferents elements de la pàgina, el codi dels quals es mostrarà i es destacarà al visualitzador de codi.

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Veure el codi de la pàgina

Per trobar la confirmació del meu raonament sobre els desavantatges de copiar selectors del codi, presteu atenció a les funcions següents.

Això és el que obteniu quan copieu el codi:

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

Per copiar alguna cosa com això, heu de fer clic amb el botó dret a la secció de codi que us interessa i seleccionar l'ordre Copia > Copia XPath al menú que apareix.

Això és el que he utilitzat per definir el botó Més barat:

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

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Copia l'ordre > Copia XPath

És bastant obvi que la segona opció sembla molt més senzilla. Quan s'utilitza, cerca un element a que tingui l'atribut data-codeigual a price. Quan s'utilitza la primera opció, es cerca l'element id que és igual a wtKI-price_aTab, i el camí XPath a l'element sembla /div[1]/div/div/div[1]/div/span/span. Una consulta XPath com aquesta a una pàgina farà el truc, però només una vegada. Ho puc dir ara mateix id canviarà la propera vegada que es carregui la pàgina. Seqüència de caràcters wtKI canvia dinàmicament cada vegada que es carrega la pàgina, de manera que el codi que l'utilitza serà inútil després de la propera recàrrega de la pàgina. Així que preneu-vos una estona per entendre XPath. Aquest coneixement et servirà molt.

Tanmateix, cal tenir en compte que copiar els selectors XPath pot ser útil quan es treballa amb llocs bastant senzills, i si us trobeu còmode amb això, no hi ha res de dolent.

Ara pensem què fer si necessiteu obtenir tots els resultats de la cerca en diverses línies, dins d'una llista. Molt simple. Cada resultat es troba dins d'un objecte amb una classe resultWrapper. La càrrega de tots els resultats es pot fer en un bucle similar al que es mostra a continuació.

Cal tenir en compte que si enteneu l'anterior, haureu d'entendre fàcilment la major part del codi que analitzarem. A mesura que s'executa aquest codi, accedim al que necessitem (de fet, l'element en què s'embolica el resultat) mitjançant algun tipus de mecanisme que especifica el camí (XPath). Això es fa per tal d'aconseguir el text de l'element i col·locar-lo en un objecte des del qual es puguin llegir dades (utilitzat per primera vegada flight_containers, llavors - flights_list).

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Es mostren les tres primeres línies i podem veure clarament tot el que necessitem. Tanmateix, tenim maneres més interessants d'obtenir informació. Hem de prendre les dades de cada element per separat.

Posa't a treballar!

La manera més senzilla d'escriure una funció és carregar resultats addicionals, de manera que és per on començarem. M'agradaria maximitzar el nombre de vols dels quals el programa rep informació, sense aixecar sospites en el servei que condueixi a una inspecció, així que faig clic al botó Carrega més resultats una vegada cada vegada que es mostra la pàgina. En aquest codi, hauríeu de prestar atenció al bloc try, que he afegit perquè de vegades el botó no es carrega correctament. Si també trobeu això, comenta les trucades a aquesta funció al codi de funció start_kayak, que veurem a continuació.

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

Ara, després d'una llarga anàlisi d'aquesta funció (de vegades em puc deixar portar), estem preparats per declarar una funció que rasparà la pàgina.

Ja he recollit la major part del que es necessita a la següent funció anomenada page_scrape. De vegades, les dades del camí retornades es combinen, de manera que faig servir un mètode senzill per separar-les. Per exemple, quan faig servir variables per primera vegada section_a_list и section_b_list. La nostra funció retorna un marc de dades flights_df, això ens permet separar els resultats obtinguts de diferents mètodes d'ordenació de dades i, posteriorment, combinar-los.

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

Vaig intentar anomenar les variables perquè el codi fos comprensible. Recordeu que les variables comencen per a pertanyen a la primera etapa del camí, i b - al segon. Passem a la següent funció.

Mecanismes de suport

Ara tenim una funció que ens permet carregar resultats de cerca addicionals i una funció per processar aquests resultats. Aquest article podria haver acabat aquí, ja que aquestes dues funcions proporcionen tot el que necessiteu per raspar pàgines que podeu obrir vosaltres mateixos. Però encara no hem considerat alguns dels mecanismes auxiliars comentats anteriorment. Per exemple, aquest és el codi per enviar correus electrònics i algunes altres coses. Tot això es pot trobar a la funció start_kayak, que ara analitzarem.

Perquè aquesta funció funcioni, necessiteu informació sobre ciutats i dates. Amb aquesta informació, forma un enllaç en una variable kayak, que s'utilitza per portar-vos a una pàgina que contindrà resultats de cerca ordenats segons la millor coincidència amb la consulta. Després de la primera sessió de raspat, treballarem amb els preus de la taula de la part superior de la pàgina. És a dir, trobarem el preu mínim del bitllet i el preu mitjà. Tot això, juntament amb la predicció emesa pel lloc, s'enviarà per correu electrònic. A la pàgina, la taula corresponent hauria d'estar a la cantonada superior esquerra. Treballar amb aquesta taula, per cert, pot provocar un error a l'hora de cercar amb dates exactes, ja que en aquest cas la taula no es mostra a la pàgina.

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

Vaig provar aquest script amb un compte d'Outlook (hotmail.com). No l'he provat perquè funcioni correctament amb un compte de Gmail, aquest sistema de correu electrònic és força popular, però hi ha moltes opcions possibles. Si utilitzeu un compte de Hotmail, per tal que tot funcioni, només cal que introduïu les vostres dades al codi.

Si voleu entendre què s'està fent exactament en seccions específiques del codi d'aquesta funció, podeu copiar-les i experimentar amb elles. Experimentar amb el codi és l'única manera d'entendre'l realment.

Sistema llest

Ara que hem fet tot el que hem parlat, podem crear un bucle senzill que cridi les nostres funcions. L'script sol·licita dades a l'usuari sobre ciutats i dates. Quan feu proves amb un reinici constant de l'script, és poc probable que vulgueu introduir aquestes dades manualment cada vegada, de manera que les línies corresponents, per al moment de la prova, es poden comentar descommentant les de sota, en les quals les dades necessàries l'script està 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.....')

Així és l'execució de prova de l'script.
Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar
Prova d'execució de l'script

Resultats de

Si has arribat fins aquí, enhorabona! Ara teniu un rascador web que funciona, tot i que ja veig moltes maneres de millorar-lo. Per exemple, es pot integrar amb Twilio perquè enviï missatges de text en lloc de correus electrònics. Podeu utilitzar una VPN o una altra cosa per rebre resultats simultàniament de diversos servidors. També hi ha un problema periòdic en comprovar l'usuari del lloc per veure si és una persona, però aquest problema també es pot resoldre. En qualsevol cas, ara tens una base que pots ampliar si vols. Per exemple, assegureu-vos que s'enviï un fitxer Excel a l'usuari com a fitxer adjunt a un correu electrònic.

Python: un assistent per trobar bitllets d'avió barats per als amants de viatjar

Només els usuaris registrats poden participar en l'enquesta. Inicia sessiósi us plau.

Utilitzeu tecnologies de raspat web?

  • No

Han votat 8 usuaris. 1 usuari es va abstenir.

Font: www.habr.com

Afegeix comentari