Python es un asistente para encontrar vuelos baratos para quienes aman viajar.

El autor del artículo cuya traducción publicamos hoy explica que su objetivo es contar sobre el desarrollo de un web scraper en Python utilizando Selenium, que realiza búsquedas de precios de billetes de avión. Al buscar billetes, se utilizan fechas flexibles (+- 3 días respecto a las fechas indicadas). El scraper guarda los resultados de la búsqueda en un archivo de Excel y envía un correo electrónico a quien lo ejecutó con un resumen de lo que logró encontrar. La tarea de este proyecto es ayudar a los viajeros a encontrar las mejores ofertas.

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.

Si al revisar el material te sientes perdido, echa un vistazo a esto el artículo.

¿Qué buscaremos?

Eres libre de utilizar el sistema descrito aquí como desees. Por ejemplo, yo lo he utilizado para buscar escapadas de fin de semana y billetes a mi ciudad natal. Si realmente estás decidido a buscar billetes económicos, puedes ejecutar el script en un servidor (simplemente servidor, por 130 rublos al mes, es bastante adecuado para esto) y configurar que se ejecute una o dos veces al día. Los resultados de la búsqueda te llegarán por correo electrónico. Además, recomiendo configurar todo de tal manera que el script guarde un archivo de Excel con los resultados de la búsqueda en una carpeta de Dropbox, lo que te permitirá ver esos archivos desde cualquier lugar y en cualquier momento.

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Hasta ahora no he encontrado tarifas erróneas, pero supongo que esto es posible.

Al buscar, como ya se mencionó, se utiliza 'fecha flexible', el script encuentra ofertas dentro de un rango de tres días desde las fechas establecidas. Aunque al ejecutar el script se busca solo en una dirección, no es difícil modificarlo para que pueda recopilar datos de múltiples rutas de vuelo. Incluso se pueden buscar tarifas erróneas, y esos hallazgos pueden resultar muy interesantes.

¿Para qué necesitamos otro web scraper?

Cuando comencé a hacer web scraping, honestamente, no me parecía muy interesante. Quería realizar más proyectos en el ámbito de la modelización predictiva, el análisis financiero y, quizás, en el análisis del sentimiento en los textos. Pero resultó ser muy interesante entender cómo crear un programa que recolecte datos de sitios web. A medida que profundizaba en el tema, me di cuenta de que el web scraping es el "motor" de Internet.

Quizás consideres que esta es una afirmación demasiado atrevida. Pero piensa en que la empresa Google comenzó con un web scraper que Larry Page creó utilizando Java y Python. Los robots de Google exploran y continúan explorando Internet, tratando de ofrecer a sus usuarios las mejores respuestas a sus preguntas. El web scraping tiene un sinfín de aplicaciones, y aunque en el campo de la Ciencia de Datos estés interesado en otra cosa, necesitarás algunas habilidades de scraping para obtener datos para tu análisis.

Algunos de los métodos utilizados aquí los encontré en una fantástica el libro sobre web scraping, que adquirí recientemente. Allí se pueden encontrar muchos ejemplos sencillos e ideas sobre cómo aplicar lo aprendido en la práctica. Además, hay un capítulo muy interesante sobre cómo eludir verificaciones de reCaptcha. Para mí fue una revelación, ya que no sabía que existen herramientas específicas e incluso servicios completos para resolver tales problemas.

¿Te gusta viajar?!

A la sencilla y bastante inofensiva pregunta que lleva el título de esta sección, a menudo se puede escuchar una respuesta positiva, acompañada de un par de historias de viajes de la persona a la que se le formuló. La mayoría de nosotros está de acuerdo en que viajar es una maravillosa manera de sumergirse en nuevas culturas y ampliar nuestros horizontes. Sin embargo, si se le pregunta a alguien si disfruta buscar vuelos, estoy seguro de que la respuesta no será tan positiva. De hecho, aquí es donde Python nos puede ayudar.

La primera tarea que debemos resolver en nuestro camino para crear un sistema de búsqueda de información sobre boletos de avión es seleccionar la plataforma adecuada de la que obtendremos la información. Esta decisión no fue fácil para mí, pero al final elegí el servicio Kayak. Probé con servicios como Momondo, Skyscanner, Expedia y algunos más, pero los mecanismos de protección contra bots en esos recursos eran infranqueables. Después de varios intentos, en los que, al intentar convencer a los sistemas de que era humano, tuve que lidiar con semáforos, pasos peatonales y bicicletas, decidí que Kayak era el que mejor se adaptaba a mis necesidades, a pesar de que aquí también, si se cargan demasiadas páginas en poco tiempo, comienzan las verificaciones. Logré configurar el bot para que enviara solicitudes al sitio en intervalos de 4 a 6 horas, y funcionó correctamente. Ocasionalmente surgen dificultades al trabajar con Kayak, pero si comienzan a agobiarte con verificaciones, debes ya sea resolverlas manualmente y luego lanzar el bot, o esperar unas horas y las verificaciones deberían cesar. Si es necesario, puedes adaptar el código para otra plataforma, y si lo haces, puedes mencionarlo en los comentarios.

Si estás comenzando a conocer el web scraping y no sabes por qué algunos sitios web luchan contra él, simplemente hazte un favor y busca en Google materiales con las palabras "etiqueta de web scraping" antes de iniciar tu primer proyecto en este campo. Tus experimentos pueden terminar más rápido de lo que piensas si realizas web scraping de forma irresponsable.

Inicio

Aquí hay un resumen general de lo que sucederá en el código de nuestro web scraper:

  • Importar las bibliotecas necesarias.
  • Abrir una pestaña de Google Chrome.
  • Llamar a la función que inicia el bot, pasando las ciudades y las fechas que se usarán en la búsqueda de boletos.
  • Esta función obtiene los primeros resultados de búsqueda, ordenados por el criterio de mayor atractivo (best), y hace clic en el botón para cargar resultados adicionales.
  • Otra función recopila los datos de toda la página y devuelve un marco de datos.
  • Los dos pasos anteriores se realizan utilizando tipos de ordenación por el precio de los boletos (cheap) y por la velocidad de vuelo (fastest).
  • Al usuario del script se le envía un correo electrónico que contiene un resumen breve sobre los precios de los boletos (los boletos más baratos y el precio promedio), y el marco de datos con la información, ordenada por los tres indicadores mencionados anteriormente, se guarda en un archivo de Excel.
  • Todas las acciones descritas anteriormente se realizan en un ciclo a intervalos de tiempo determinados.

Es importante señalar que cada proyecto de Selenium comienza con un controlador web. Yo utilizo Chromedriver, trabajo con Google Chrome, pero hay otras opciones. PhantomJS y Firefox también son populares. Después de descargar el controlador, debe colocarse en la carpeta correspondiente, y aquí termina la preparación para su uso. En las primeras líneas de nuestro script se abre una nueva pestaña de Chrome.

Tenga en cuenta que no intento abrir nuevos horizontes en la búsqueda de ofertas ventajosas de boletos de avión en mi relato. Existen métodos mucho más avanzados para buscar tales ofertas. Solo deseo ofrecer a los lectores de este material una manera simple, pero aplicable en la práctica, de resolver esta tarea.

Aquí está el código del que hablamos anteriormente.

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

# ¡Utiliza aquí tu ruta al chromedriver!
chromedriver_path = 'C:/{YOUR PATH HERE}/chromedriver_win32/chromedriver.exe'

driver = webdriver.Chrome(executable_path=chromedriver_path) # Este comando abre una ventana de Chrome
sleep(2)

Al principio del código, se pueden ver los comandos para importar paquetes que se utilizan en todo nuestro proyecto. Así, randint se utiliza para que el bot "dormir" un número aleatorio de segundos antes de comenzar una nueva operación de búsqueda. Generalmente, ningún bot puede prescindir de esto. Si se ejecuta el código anterior, se abrirá una ventana de Chrome que el bot utilizará para trabajar con los sitios web.

Realizaremos un pequeño experimento y abriremos en una ventana separada el sitio kayak.com. Elegiremos la ciudad desde la que planeamos volar y la ciudad a la que queremos llegar, así como las fechas de los vuelos. Al seleccionar las fechas, verificaremos que se utilice el rango de +-3 días. He escrito el código teniendo en cuenta lo que devuelve el sitio como respuesta a solicitudes similares. Si, por ejemplo, necesitas buscar boletos solo en las fechas específicas, es probable que debas ajustar el código del bot. Al hablar sobre el código, haré las aclaraciones pertinentes, pero si sientes que te has confundido, házmelo saber.

Ahora hacemos clic en el botón para iniciar la búsqueda y observamos el enlace en la barra de direcciones. Debe parecerse al enlace que utilizo en el ejemplo a continuación, donde se declara la variable kayak, que almacena la URL, y se utiliza el método get del controlador web. Después de hacer clic en el botón de búsqueda, deberían aparecer los resultados en la página.

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Cuando utilicé el comando get más de dos o tres veces en unos minutos, me ofrecieron pasar un verificación con reCaptcha. Esta verificación se puede realizar manualmente y continuar experimentando hasta que el sistema decida hacer una nueva verificación. Cuando probé el script, tuve la sensación de que la primera sesión de búsqueda siempre se realizaba sin problemas, así que, si deseas experimentar con el código, solo tendrás que pasar la verificación manualmente de vez en cuando y dejar que el código se ejecute, usando intervalos largos entre las sesiones de búsqueda. Además, si lo piensas, a una persona difícilmente le servirán los datos de precios de los boletos obtenidos con intervalos de 10 minutos entre operaciones de búsqueda.

Trabajando con la página usando XPath

Así que abrimos una ventana y cargamos el sitio. Para obtener información sobre precios y otros datos, necesitamos utilizar la tecnología XPath o selectores CSS. Decidí optar por XPath y no sentí la necesidad de usar selectores CSS, pero es perfectamente posible trabajar de esa manera también. Navegar por la página utilizando XPath puede resultar complicado, y incluso si usas los métodos que describí en esta En el artículo donde se utilizó la copia de los identificadores correspondientes del código de la página, comprendí que, en realidad, esta no es la forma óptima de acceder a los elementos necesarios. Por cierto, en esta el libro se puede encontrar una excelente descripción de los fundamentos del trabajo con páginas utilizando XPath y selectores CSS. Así es como se ve el método correspondiente del controlador web.

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Así que continuamos trabajando en el bot. Aprovecharemos las capacidades del programa para elegir los boletos más baratos. En la siguiente imagen, el código del selector XPath está resaltado en rojo. Para visualizar el código, debes hacer clic derecho en el elemento de la página que te interese y seleccionar la opción Inspeccionar (Inspect) en el menú que aparece. Esta opción se puede invocar para diferentes elementos de la página, cuyo código aparecerá y se resaltará en la ventana de visualización de código.

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Inspeccionar el código de la página

Para encontrar confirmación de mis razonamientos sobre las desventajas de copiar selectores del código, presta atención a las siguientes características.

Esto es lo que obtenemos al copiar el código:

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

Para copiar algo similar, debes hacer clic derecho en la parte del código que te interese y seleccionar la opción Copiar > Copiar XPath en el menú que aparece.

Esto es lo que utilicé para identificar el botón Cheapest:

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

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Opción Copiar > Copiar XPath

Es completamente evidente que la segunda opción parece mucho más simple. Al utilizarla, se busca un elemento a que tiene el atributo data-code, igual a precio. Al usar la primera opción, se busca un elemento id cuyo valor es wtKI-price_aTab, mientras que la ruta XPath hacia el elemento se ve como /div[1]/div/div/div[1]/div/span/span. Tal consulta XPath a la página hará su trabajo, pero solo una vez. Puedo decirte ahora mismo que id cambiará en la siguiente carga de la página. La secuencia de caracteres wtKI cambia dinámicamente en cada carga de la página, lo que resulta en que el código en el que se utiliza será inútil tras la siguiente recarga de la página. Por lo tanto, tómate un tiempo para familiarizarte con XPath. Este conocimiento te será útil.

Sin embargo, hay que señalar que copiar selectores XPath puede ser útil al trabajar con sitios web relativamente simples, y si esto te parece bien, no hay nada de malo en ello.

Ahora pensemos en qué hacer si necesitamos obtener todos los resultados de búsqueda en varias líneas, dentro de una lista. Es muy sencillo. Cada resultado se encuentra dentro de un objeto con la clase resultWrapper. La carga de todos los resultados se puede realizar en un ciclo, similar al que se mostrará a continuación.

Cabe señalar que si has comprendido lo anterior, no deberías tener problemas para entender la mayor parte del código que vamos a analizar. En el transcurso de este código, accedemos a lo que necesitamos (de hecho, es el elemento que envuelve el resultado) utilizando algún mecanismo para especificar la ruta (XPath). Esto se hace para obtener el texto del elemento y colocarlo en un objeto del que se pueden leer los datos (primero se utiliza flight_containers, después — flights_list).

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Se muestran las primeras tres líneas y podemos ver claramente todo lo que necesitamos. Sin embargo, tenemos formas más interesantes de obtener información. Necesitamos extraer los datos de cada elemento individualmente.

¡A trabajar!

Es más fácil escribir una función para cargar resultados adicionales, así que comencemos por ahí. Me gustaría maximizar el número de vuelos cuyo información obtiene el programa, y al mismo tiempo no despertar sospechas en el servicio que lleven a una verificación, así que hago clic una vez en el botón 'Cargar más resultados' cada vez que aparece la página. En este código, debes prestar atención al bloque intentar, que añadí porque a veces el botón no se carga correctamente. Si te encuentras también con esto, comenta las llamadas a esta función en el código de la función start_kayak, que discutiremos a continuación.

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

Ahora, después de un largo análisis de esta función (a veces puedo emocionarme), estamos listos para declarar una función que se encargará de hacer scraping de la página.

Ya he reunido gran parte de lo necesario en la siguiente función, llamada page_scrape. A veces, los datos devueltos sobre los tramos del viaje resultan estar combinados, para separarlos uso un método simple. Por ejemplo, cuando uso por primera vez las variables section_a_list y section_b_list. Nuestra función devuelve un marco de datos flights_df, lo que nos permite dividir los resultados obtenidos al usar diferentes métodos de ordenación de datos y, posteriormente, unirlos.

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

He tratado de nombrar las variables de tal manera que el código sea comprensible. Recuerden que las variables que comienzan con a se refieren a la primera etapa del camino, mientras que b se refiere a la segunda. Pasemos a la siguiente función.

Mecanismos auxiliares

Ahora tenemos una función que permite cargar resultados adicionales de búsqueda y una función para procesar esos resultados. Este artículo podría terminar aquí, ya que estas dos funciones proporcionan todo lo necesario para raspar páginas que se pueden abrir por sí mismas. Pero aún no hemos considerado algunos mecanismos auxiliares que mencionamos anteriormente. Por ejemplo, este es el código para enviar correos electrónicos y algunas otras cosas. Todo esto se encuentra en la función start_kayak, que ahora vamos a examinar.

Para que esta función funcione, se necesita información sobre las ciudades y las fechas. Utilizando esta información, forma un enlace en la variable kayak, que se utiliza para acceder a la página donde se ubicarán los resultados de búsqueda, ordenados por su mejor coincidencia con la consulta. Después de la primera sesión de raspado, trabajaremos con los precios que se encuentran en la tabla, en la parte superior de la página. Específicamente, encontraremos el precio mínimo del boleto y el precio promedio. Todo esto, junto con la predicción proporcionada por el sitio, será enviado por correo electrónico. En la página, la tabla correspondiente debe estar en la esquina superior izquierda. Trabajar con esta tabla, por cierto, puede provocar un error al buscar utilizando fechas exactas, ya que en ese caso la tabla no se muestra en la página.

def start_kayak(city_from, city_to, date_start, date_end):
    """Códigos de ciudad - ¡son los códigos IATA!
    Formato de fecha -  AAAA-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))
    
    # a veces aparece una ventana emergente, para verificar esto y cerrarla se puede usar el bloque try
    try:
        xp_popup_close = '//*[@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('cargando más.....')
    
#     load_more()
    
    print('comenzando el primer raspado.....')
    df_flights_best = page_scrape()
    df_flights_best['sort'] = 'best'
    sleep(randint(60,80))
    
    # Tomaremos el precio más bajo de la tabla, ubicada en la parte superior de la página
    matrix = driver.find_elements_by_xpath('//*[@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('cambiando a los resultados más baratos.....')
    cheap_results = '//*[@data-code = "price"]'
    driver.find_element_by_xpath(cheap_results).click()
    sleep(randint(60,90))
    print('cargando más.....')
    
#     load_more()
    
    print('comenzando el segundo raspado.....')
    df_flights_cheap = page_scrape()
    df_flights_cheap['sort'] = 'cheap'
    sleep(randint(60,80))
    
    print('cambiando a los resultados más rápidos.....')
    quick_results = '//*[@data-code = "duration"]'
    driver.find_element_by_xpath(quick_results).click()  
    sleep(randint(60,90))
    print('cargando más.....')
    
#     load_more()
    
    print('comenzando el tercer raspado.....')
    df_flights_fast = page_scrape()
    df_flights_fast['sort'] = 'fast'
    sleep(randint(60,80))
    
    # Guardando el nuevo marco en un archivo Excel, cuyo nombre refleja las ciudades y las fechas
    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('df guardado.....')
    
    # Se puede seguir cómo la predicción dada por el sitio se relaciona con la realidad
    xp_loading = '//*[@id="advice"]'
    loading = driver.find_element_by_xpath(xp_loading).text
    xp_prediction = '//*[@class="info-text"]'
    prediction = driver.find_element_by_xpath(xp_prediction).text
    print(loading+'n'+prediction)
    
    # a veces, en la variable loading aparece esta cadena, que más tarde causa problemas con el envío de correos
    # si esto sucede, cambiamos a "No estoy seguro"
    weird = '¯_(ツ)_/¯'
    if loading == weird:
        loading = 'No estoy seguro'
    
    username = 'TUEMAIL@hotmail.com'
    password = 'TU CONTRASEÑA'

    server = smtplib.SMTP('smtp.outlook.com', 587)
    server.ehlo()
    server.starttls()
    server.login(username, password)
    msg = ('Subject: Flight Scrapernn
Vuelo más barato: {}nPrecio promedio: {}nnRecomendación: {}nnFin del mensaje'.format(matrix_min, matrix_avg, (loading+'n'+prediction)))
    message = MIMEMultipart()
    message['From'] = 'TUEMAIL@hotmail.com'
    message['to'] = 'TUOTROEMAIL@domain.com'
    server.sendmail('TUEMAIL@hotmail.com', 'TUOTROEMAIL@domain.com', msg)
    print('correo enviado.....')

He probado este script utilizando una cuenta de Outlook (hotmail.com). No lo he probado con una cuenta de Gmail, que es un servicio de correo muy popular, pero hay muchas otras opciones. Si estás usando una cuenta de Hotmail, para que todo funcione, solo necesitas ingresar tus datos en el código.

Si quieres entender lo que se está ejecutando en ciertas partes del código de esta función, puedes copiarlas y experimentar con ellas. Experimentar con el código es la única forma de comprenderlo adecuadamente.

Sistema listo

Ahora que hemos hecho todo lo que mencionamos, podemos crear un ciclo simple que llame a nuestras funciones. El script pedirá al usuario datos sobre ciudades y fechas. Al probar con la ejecución continua del script, probablemente no querrás ingresar estos datos manualmente cada vez, por lo que puedes comentar las líneas correspondientes, descomentando las que se encuentran debajo, donde se establecen los datos necesarios para el script.

city_from = input('¿Desde qué ciudad? ')
city_to = input('¿A dónde? ')
date_start = input('¿Alrededor de qué fecha de salida? Por favor, usa solo el formato YYYY-MM-DD ')
date_end = input('¿Cuándo regresar? Por favor, usa solo el formato YYYY-MM-DD ')

# 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('la iteración {} se completó @ {}'.format(n, strftime("%Y%m%d-%H%M")))
    
    # Esperamos 4 horas
    sleep(60*60*4)
    print('se acabó el sueño.....')

Así es como se ve la ejecución de prueba del script.
Python es un asistente para encontrar vuelos baratos para quienes aman viajar.
Ejecución de prueba del script

Resultados

Si has llegado hasta aquí, ¡felicitaciones! Ahora tienes un web scraper funcional, aunque ya veo muchas maneras de mejorarlo. Por ejemplo, se podría integrar con Twilio para que, en lugar de correos electrónicos, enviara mensajes de texto. Se podría usar una VPN o algo similar para obtener resultados de varios servidores simultáneamente. También hay un problema ocasional relacionado con la verificación del usuario del sitio para saber si es un ser humano, pero este problema también se puede solucionar. De todos modos, ahora tienes una base que puedes expandir si lo deseas. Por ejemplo, hacer que el archivo de Excel se envíe al usuario como un archivo adjunto en un correo electrónico.

Python es un asistente para encontrar vuelos baratos para quienes aman viajar.

Solo los usuarios registrados pueden participar en la encuesta. Inicie sesión, por favor.

¿Utilizas tecnologías de web scraping?

  • Sí

  • No

8 usuarios votaron. 1 usuario se abstuvo.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster