
Hola habrozhiteli! Este libro de regalos de Noah es para cualquier persona interesada en inteligencia artificial, aprendizaje automático, computación en la nube y cualquier combinación de temas de datos. Tanto los programadores como los técnicos más interesados encontrarán aquí información útil. Se dan ejemplos de código en Python. Cubre muchos temas avanzados como el uso de plataformas en la nube (como AWS, GCP y Azure), así como técnicas de aprendizaje automático y la implementación de IA. Los Jedi que dominan Python, la computación en la nube y ML también encontrarán muchas ideas útiles para sí mismos que pueden aplicar de inmediato en su trabajo actual.
Te invitamos a leer un extracto del libro "Creación de un Bot inteligente inteligente en AWS"
La gente siempre ha soñado con crear una "vida artificial". Muy a menudo, si bien esto es posible mediante la creación de bots. Los bots se están convirtiendo en una parte cada vez más integral de nuestra vida diaria, especialmente desde la llegada de Siri de Apple y Alexa de Amazon. En este capítulo, revelaremos todos los secretos de la creación de bots.
Creación de bot
Para crear el bot, utilizaremos la biblioteca Slack para el lenguaje Python (https://github.com/slackapi/python-slackclient). Para comenzar con Slack, debe generar un token de identificación. En general, tiene sentido cuando se trabaja con dichos marcadores para exportar una variable de entorno. A menudo hago esto en virtualenv, obteniendo así acceso automáticamente cuando se ejecuta en el entorno actual. Para hacer esto, debe "descifrar" un poco la utilidad virtualenv editando el script de activación.
Al exportar la variable Slack en la secuencia de comandos ~ / .env / bin / enable, se verá como la siguiente.
Y solo para información, si desea mantenerse al día con las últimas innovaciones, se recomienda utilizar la nueva utilidad oficial de Python para administrar el entorno: pipenv (https://github.com/pypa/pipenv):
_OLD_VIRTUAL_PATH="$PATH" PATH="$VIRTUAL_ENV/bin:$PATH" export PATH SLACK_API_TOKEN=<Your Token Here> export SLACK_API_TOKEN
Es conveniente utilizar el comando printenv de los sistemas operativos OS X y Linux para verificar si la variable de entorno está configurada. Después de eso, puede usar el siguiente script corto para verificar el envío de un mensaje:
import os from slackclient import SlackClient slack_token = os.environ["SLACK_API_TOKEN"] sc = SlackClient(slack_token) sc.api_call( "chat.postMessage", channel="#general", text="Hello from my bot! :tada:" )
También vale la pena señalar que la utilidad pipenv es la solución recomendada que combina las capacidades de las utilidades pip y virtualenv en un componente. Se ha convertido en un nuevo estándar, por lo que tiene sentido mirarlo desde el punto de vista de la gestión de paquetes.
Convertir biblioteca a utilidad de línea de comando
Al igual que con los otros ejemplos en este libro, es una buena idea convertir nuestro código en una utilidad de línea de comandos para que sea más fácil probar nuevas ideas. Vale la pena señalar que muchos desarrolladores novatos a menudo prefieren no herramientas de línea de comandos, sino otras soluciones, por ejemplo, simplemente funcionan en las computadoras portátiles Jupiter. Jugaré por un corto tiempo el papel del abogado del diablo y haré una pregunta que los lectores bien podrían tener: “¿Por qué necesitamos utilidades de línea de comandos en un proyecto basado en las libretas de Júpiter? ¿No es el objetivo de los blocs de notas de Júpiter hacer innecesario el shell de comando y la línea de comando? " Agregar una utilidad de línea de comandos a un proyecto es bueno porque le permite probar rápidamente diferentes opciones de entrada. Los bloques de código de la notebook Jupiter no aceptan entradas; en cierto sentido, estos son scripts con datos cableados.
Muchas utilidades de línea de comandos en plataformas GCP y AWS no existen por casualidad: proporcionan flexibilidad y características que no están disponibles en las interfaces gráficas. Una maravillosa colección de ensayos sobre este tema del escritor de ciencia ficción Neal Stephenson se llama "Al principio ... había una línea de comando". En él, Stevenson dice: "Las GUI implican una sobrecarga adicional significativa para cada componente de software, incluso el más pequeño, que cambia completamente el entorno de programación". Termina la colección con las palabras: “... la vida es una cosa muy difícil y difícil; ninguna interfaz cambiará eso; y cualquiera que piense lo contrario es un tonto ... "Bastante difícil, pero mi experiencia sugiere que es lo suficientemente cierto. La vida con la línea de comando está mejorando. Pruébelo y no querrá volver a la GUI.
Para hacer esto, utilizaremos el paquete de clic, como se muestra a continuación. Enviar mensajes usando la nueva interfaz es muy simple.
./clibot.py send --message "from cli" sending message from cli to #general
La Figura 7.1 muestra los valores predeterminados, así como el mensaje personalizado de la utilidad cli.
#!/usr/bin/env python import os import click from slackclient import SlackClient SLACK_TOKEN = os.environ["SLACK_API_TOKEN"] def send_message(channel="#general", message="Hello from my bot!"): """ """ slack_client = SlackClient(SLACK_TOKEN) res = slack_client.api_call( "chat.postMessage", channel=channel, text=message ) return res @click.group() @click.version_option("0.1") def cli(): """ """ @cli.command("send") @click.option("--message", default="Hello from my bot!", help="text of message") @click.option("--channel", default="#general", help="general channel") def send(channel, message): click.echo(f"sending message {message} to {channel}") send_message(channel, message=message) if __name__ == '__main__': cli()
Llevamos el bot a un nuevo nivel usando el servicio AWS Step Functions
Después de crear canales de comunicación para enviar mensajes a Slack, puede mejorar nuestro código, a saber: ejecutarlo en un horario y usarlo para cualquier acción útil. El servicio AWS Step Functions es excelente para esto. En la siguiente sección, nuestro bot Slack aprenderá a eliminar las páginas de deportes de Yahoo! Jugadores de la NBA, recuperen su lugar de nacimiento y luego envíen estos datos a Slack.
La Figura 7.2 muestra una función paso a paso lista para usar en acción. El primer paso es extraer las URL del perfil del jugador de la NBA, y el segundo es usar la biblioteca Beautiful Soup para encontrar el lugar de nacimiento de cada jugador. Una vez completada la función paso a paso, los resultados se enviarán de vuelta a Slack.
AWS Lambda y Chalice se pueden usar para coordinar las partes individuales del trabajo dentro de una función de paso. Lambda (https://aws.amazon.com/lambda/) permite al usuario realizar funciones en AWS, y el marco Chalice (http://chalice.readthedocs.io/en/latest/) le permite crear aplicaciones sin servidor en Python. Aquí hay algunos requisitos previos:
- El usuario debe tener una cuenta de AWS.
- el usuario necesita credenciales para usar la API;
- el rol Lambda (creado por Chalice) debe tener una política con los privilegios necesarios para invocar los servicios de AWS correspondientes, como S3.
Configurar credenciales de IAM
Se pueden encontrar instrucciones detalladas para configurar las credenciales de AWS en
boto3.readthedocs.io/en/latest/guide/configuration.html .
Puede encontrar información sobre la exportación de variables de AWS en los sistemas operativos Windows y Linux
aquí . Hay muchas formas de configurar las credenciales, pero los usuarios virtualenv pueden colocar las credenciales de AWS en un entorno virtual local en el script / bin / generate:
# AWS AWS_DEFAULT_REGION=us-east-1 AWS_ACCESS_KEY_ID=xxxxxxxx AWS_SESSION_TOKEN=xxxxxxxx
# export AWS_DEFAULT_REGION export AWS_ACCESS_KEY_ID export AWS_DEFAULT_REGION
Trabaja con cáliz. Chalice tiene una utilidad de línea de comandos con muchos comandos disponibles:
Usage: chalice [OPTIONS] COMMAND [ARGS]... Options: --version Show the version and exit. --project-dir TEXT The project directory. Defaults to CWD --debug / --no-debug Print debug logs to stderr. --help Show this message and exit. Commands: delete deploy gen-policy generate-pipeline Generate a cloudformation template for a... generate-sdk local logs new-project package url
El código dentro de la plantilla app.py se puede reemplazar con las funciones del servicio Lambda. AWS Chalice es conveniente porque permite crear, además de servicios web, funciones Lambda "independientes". Gracias a esta funcionalidad, puede crear varias funciones Lambda, asociarlas con una función paso a paso y unirlas como cubos Lego.
Por ejemplo, puede crear fácilmente una función Lambda programada que realizará cualquier acción:
@app.schedule(Rate(1, unit=Rate.MINUTES)) def every_minute(event): """, """ # Slack
Para establecer la interacción con el bot para el raspado web, debe crear varias funciones. Al comienzo del archivo se importan y se declaran varias variables:
import logging import csv from io import StringIO import boto3 from bs4 import BeautifulSoup import requests from chalice import (Chalice, Rate) APP_NAME = 'scrape-yahoo' app = Chalice(app_name=APP_NAME) app.log.setLevel(logging.DEBUG)
El bot puede necesitar almacenar algunos de los datos en S3. La siguiente función usa Boto para guardar los resultados en un archivo CSV:
def create_s3_file(data, name="birthplaces.csv"): csv_buffer = StringIO() app.log.info(f"Creating file with {data} for name") writer = csv.writer(csv_buffer) for key, value in data.items(): writer.writerow([key,value]) s3 = boto3.resource('s3') res = s3.Bucket('aiwebscraping').\ put_object(Key=name, Body=csv_buffer.getvalue()) return res
La función fetch_page utiliza
la biblioteca Beautiful Soup para analizar una página HTML ubicada de acuerdo con la URL de estadísticas de la NBA y devuelve un objeto de sopa:
def fetch_page(url="https://sports.yahoo.com/nba/stats/"): """ URL Yahoo""" # # Beautiful Soup app.log.info(f"Fetching urls from {url}") res = requests.get(url) soup = BeautifulSoup(res.content, 'html.parser') return soup
Las funciones get_player_links y fetch_player_urls obtienen enlaces a las URL del perfil del jugador:
def get_player_links(soup): """ URL URL 'a' 'nba/players' """ nba_player_urls = [] for link in soup.find_all('a'): link_url = link.get('href') # if link_url: if "nba/players" in link_url: print(link_url) nba_player_urls.append(link_url) return nba_player_urls def fetch_player_urls(): """ URL """ soup = fetch_page() urls = get_player_links(soup) return urls
A continuación, en la función find_birthplaces, extraemos los lugares de nacimiento de los jugadores de las URL ubicadas en estas páginas:
def find_birthplaces(urls): """ NBA Yahoo""" birthplaces = {} for url in urls: profile = requests.get(url) profile_url = BeautifulSoup(profile.content, 'html.parser') lines = profile_url.text res2 = lines.split(",") key_line = [] for line in res2: if "Birth" in line: #print(line) key_line.append(line) try: birth_place = key_line[0].split(":")[-1].strip() app.log.info(f"birth_place: {birth_place}") except IndexError: app.log.info(f"skipping {url}") continue birthplaces[url] = birth_place app.log.info(birth_place) return birthplaces
Ahora pasaremos a las funciones de Chalice. Tenga en cuenta: para el marco Chalice, se debe crear la ruta predeterminada:
# HTTP- @app.route('/') def index(): """ URL""" app.log.info(f"/ Route: for {APP_NAME}") return {'app_name': APP_NAME}
La siguiente función Lambda es una ruta que conecta una URL HTTP a una función escrita anteriormente:
@app.route('/player_urls') def player_urls(): """ URL """ app.log.info(f"/player_urls Route: for {APP_NAME}") urls = fetch_player_urls() return {"nba_player_urls": urls}
Las siguientes funciones de Lambda son independientes, se pueden llamar dentro de una función paso a paso:
# Lambda @app.lambda_function() def return_player_urls(event, context): """ Lambda, URL """ app.log.info(f"standalone lambda 'return_players_urls'\ {APP_NAME} with {event} and {context}") urls = fetch_player_urls() return {"urls": urls} # Lambda @app.lambda_function() def birthplace_from_urls(event, context): """ """ app.log.info(f"standalone lambda 'birthplace_from_urls'\ {APP_NAME} with {event} and {context}") payload = event["urls"] birthplaces = find_birthplaces(payload) return birthplaces # Lambda @app.lambda_function() def create_s3_file_from_json(event, context): """ S3 JSON""" app.log.info(f"Creating s3 file with event data {event}\ and context {context}") print(type(event)) res = create_s3_file(data=event) app.log.info(f"response of putting file: {res}") return True
Si ejecuta la aplicación Chalice resultante localmente, se mostrarán los siguientes resultados:
→ scrape-yahoo git:(master) chalice local Serving on 127.0.0.1:8000 scrape-yahoo - INFO - / Route: for scrape-yahoo 127.0.0.1 - - [12/Dec/2017 03:25:42] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [12/Dec/2017 03:25:42] "GET /favicon.ico" scrape-yahoo - INFO - / Route: for scrape-yahoo 127.0.0.1 - - [12/Dec/2017 03:25:45] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [12/Dec/2017 03:25:45] "GET /favicon.ico" scrape-yahoo - INFO - /player_urls Route: for scrape-yahoo scrape-yahoo - INFO - https://sports.yahoo.com/nba/stats/ https://sports.yahoo.com/nba/players/4563/ https://sports.yahoo.com/nba/players/5185/ https://sports.yahoo.com/nba/players/3704/ https://sports.yahoo.com/nba/players/5012/ https://sports.yahoo.com/nba/players/4612/ https://sports.yahoo.com/nba/players/5015/ https://sports.yahoo.com/nba/players/4497/ https://sports.yahoo.com/nba/players/4720/ https://sports.yahoo.com/nba/players/3818/ https://sports.yahoo.com/nba/players/5432/ https://sports.yahoo.com/nba/players/5471/ https://sports.yahoo.com/nba/players/4244/ https://sports.yahoo.com/nba/players/5464/ https://sports.yahoo.com/nba/players/5294/ https://sports.yahoo.com/nba/players/5336/ https://sports.yahoo.com/nba/players/4390/ https://sports.yahoo.com/nba/players/4563/ https://sports.yahoo.com/nba/players/3704/ https://sports.yahoo.com/nba/players/5600/ https://sports.yahoo.com/nba/players/4624/ 127.0.0.1 - - [12/Dec/2017 03:25:53] "GET /player_urls" 127.0.0.1 - - [12/Dec/2017 03:25:53] "GET /favicon.ico"
Para implementar la aplicación, ejecute el comando de implementación de cáliz:
→ scrape-yahoo git:(master) chalice deploy Creating role: scrape-yahoo-dev Creating deployment package. Creating lambda function: scrape-yahoo-dev Initiating first time deployment. Deploying to API Gateway stage: api https://bt98uzs1cc.execute-api.us-east-1.amazonaws.com/api/
Gracias a la interfaz de línea de comandos para HTTP (https://github.com/jakubroztocil/httpie) llamamos a la ruta HTTP desde AWS y extraemos los enlaces disponibles en / api / player_urls:
→ scrape-yahoo git:(master) http \ https://<a lambda route>.amazonaws.com/api/player_urls HTTP/1.1 200 OK Connection: keep-alive Content-Length: 941 Content-Type: application/json Date: Tue, 12 Dec 2017 11:48:41 GMT Via: 1.1 ba90f9bd20de9ac04075a8309c165ab1.cloudfront.net (CloudFront) X-Amz-Cf-Id: ViZswjo4UeHYwrc9e-5vMVTDhV_Ic0dhVIG0BrDdtYqd5KWcAuZKKQ== X-Amzn-Trace-Id: sampled=0;root=1-5a2fc217-07cc12d50a4d38a59a688f5c X-Cache: Miss from cloudfront x-amzn-RequestId: 64f24fcd-df32-11e7-a81a-2b511652b4f6 { "nba_player_urls": [ "https://sports.yahoo.com/nba/players/4563/", "https://sports.yahoo.com/nba/players/5185/", "https://sports.yahoo.com/nba/players/3704/", "https://sports.yahoo.com/nba/players/5012/", "https://sports.yahoo.com/nba/players/4612/", "https://sports.yahoo.com/nba/players/5015/", "https://sports.yahoo.com/nba/players/4497/", "https://sports.yahoo.com/nba/players/4720/", "https://sports.yahoo.com/nba/players/3818/", "https://sports.yahoo.com/nba/players/5432/", "https://sports.yahoo.com/nba/players/5471/", "https://sports.yahoo.com/nba/players/4244/", "https://sports.yahoo.com/nba/players/5464/", "https://sports.yahoo.com/nba/players/5294/", "https://sports.yahoo.com/nba/players/5336/", "https://sports.yahoo.com/nba/players/4390/", "https://sports.yahoo.com/nba/players/4563/", "https://sports.yahoo.com/nba/players/3704/", "https://sports.yahoo.com/nba/players/5600/", "https://sports.yahoo.com/nba/players/4624/" ] }
Otra forma conveniente de trabajar con las funciones de Lambda es llamarlas directamente usando el paquete click y la biblioteca Python Boto.
Podemos crear una nueva utilidad de línea de comando llamada wscli.py (abreviatura de interfaz de línea de comando de raspado web - "interfaz de línea de comando para raspado web"). En la primera parte del código, configuramos el registro e importamos las bibliotecas:
#!/usr/bin/env python import logging import json import boto3 import click from pythonjsonlogger import jsonlogger # log = logging.getLogger(__name__) log.setLevel(logging.INFO) LOGHANDLER = logging.StreamHandler() FORMMATTER = jsonlogger.JsonFormatter() LOGHANDLER.setFormatter(FORMMATTER) log.addHandler(LOGHANDLER)
Las siguientes tres funciones están diseñadas para conectarse a la función Lambda a través de invoke_lambda:
### API Boto Lambda def lambda_connection(region_name="us-east-1"): """ Lambda""" lambda_conn = boto3.client("lambda", region_name=region_name) extra_msg = {"region_name": region_name, "aws_service": "lambda"} log.info("instantiate lambda client", extra=extra_msg) return lambda_conn def parse_lambda_result(response): """ Boto JSON""" body = response['Payload'] json_result = body.read() lambda_return_value = json.loads(json_result) return lambda_return_value def invoke_lambda(func_name, lambda_conn, payload=None, invocation_type="RequestResponse"): """ Lambda""" extra_msg = {"function_name": func_name, "aws_service": "lambda", "payload":payload} log.info("Calling lambda function", extra=extra_msg) if not payload: payload = json.dumps({"payload":"None"}) response = lambda_conn.invoke(FunctionName=func_name, InvocationType=invocation_type, Payload=payload ) log.info(response, extra=extra_msg) lambda_return_value = parse_lambda_result(response) return lambda_return_value
Envolvemos la función invoke_lambda con el paquete Python para crear utilidades de línea de comandos Click. Tenga en cuenta que establecemos el valor predeterminado para la opción --func, que utiliza la función Lambda que implementamos anteriormente:
@click.group() @click.version_option("1.0") def cli(): """ -""" @cli.command("lambda") @click.option("--func", default="scrape-yahoo-dev-return_player_urls", help="name of execution") @click.option("--payload", default='{"cli":"invoke"}', help="name of payload") def call_lambda(func, payload): """ Lambda ./wscli.py lambda """ click.echo(click.style("Lambda Function invoked from cli:", bg='blue', fg='white')) conn = lambda_connection() lambda_return_value = invoke_lambda(func_name=func, lambda_conn=conn, payload=payload) formatted_json = json.dumps(lambda_return_value, sort_keys=True, indent=4) click.echo(click.style( "Lambda Return Value Below:", bg='blue', fg='white')) click.echo(click.style(formatted_json,fg="red")) if __name__ == "__main__": cli()
Los resultados que muestra esta utilidad son similares a llamar a la interfaz HTTP:
→ X ./wscli.py lambda \ --func=scrape-yahoo-dev-birthplace_from_urls\ --payload '{"url":["https://sports.yahoo.com/nba/players/4624/",\ "https://sports.yahoo.com/nba/players/5185/"]}' Lambda Function invoked from cli: {"message": "instantiate lambda client", "region_name": "us-east-1", "aws_service": "lambda"} {"message": "Calling lambda function", "function_name": "scrape-yahoo-dev-birthplace_from_urls", "aws_service": "lambda", "payload": "{\"url\":[\"https://sports.yahoo.com/nba/players/4624/\", \"https://sports.yahoo.com/nba/players/5185/\"]}"} {"message": null, "ResponseMetadata": {"RequestId": "a6049115-df59-11e7-935d-bb1de9c0649d", "HTTPStatusCode": 200, "HTTPHeaders": {"date": "Tue, 12 Dec 2017 16:29:43 GMT", "content-type": "application/json", "content-length": "118", "connection": "keep-alive", "x-amzn-requestid": "a6049115-df59-11e7-935d-bb1de9c0649d", "x-amzn-remapped-content-length": "0", "x-amz-executed-version": "$LATEST", "x-amzn-trace-id": "root=1-5a3003f2-2583679b2456022568ed0682;sampled=0"}, "RetryAttempts": 0}, "StatusCode": 200, "ExecutedVersion": "$LATEST", "Payload": "<botocore.response.StreamingBody object at 0x10ee37dd8>", "function_name": "scrape-yahoo-dev-birthplace_from_urls", "aws_service": "lambda", "payload": "{\"url\":[\"https://sports.yahoo.com/nba/players/4624/\", \"https://sports.yahoo.com/nba/players/5185/\"]}"} Lambda Return Value Below: { "https://sports.yahoo.com/nba/players/4624/": "Indianapolis", "https://sports.yahoo.com/nba/players/5185/": "Athens" }
Termine de crear una función paso a paso
El paso final para crear una función paso a paso, como se describe en la documentación de AWS (https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-creating-activity-state-machine.html), es crear usando interfaz web de la estructura de la máquina de estado en formato JavaScript Object Notation (JSON). El siguiente código demuestra esta canalización, comenzando por las funciones originales de Lambda para raspar Yahoo !, almacenando datos en un archivo S3 y finalmente enviando contenido a Slack:
{ "Comment": "Fetch Player Urls", "StartAt": "FetchUrls", "States": { "FetchUrls": { "Type": "Task", "Resource": \ "arn:aws:lambda:us-east-1:561744971673:\ function:scrape-yahoo-dev-return_player_urls", "Next": "FetchBirthplaces" }, "FetchBirthplaces": { "Type": "Task", "Resource": \ "arn:aws:lambda:us-east-1:561744971673:\ function:scrape-yahoo-dev-birthplace_from_urls", "Next": "WriteToS3" }, "WriteToS3": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:\ 561744971673:function:scrape-yahoo-dev-create_s3_file_from_json", "Next": "SendToSlack" }, "SendToSlack": { "Type": "Task", "Resource": "arn:aws:lambda:us-east-1:561744971673:\ function:send_message", "Next": "Finish" }, "Finish": { "Type": "Pass", "Result": "Finished", "End": true } } }
En la fig. 7.2 se mostró la ejecución de la primera parte de esta tubería. Es extremadamente útil ver los resultados intermedios de la máquina de estados. Además, la posibilidad de monitoreo en tiempo real de cada parte de la máquina de estado es muy conveniente para la depuración.
La Figura 7.3 muestra una tubería completa con la adición de los pasos de escribir en un archivo S3 y enviar contenido a Slack. Solo queda decidir cómo ejecutar esta utilidad de raspado, en un determinado intervalo de tiempo o en respuesta a un evento.
Resumen
En este capítulo, se le presentaron muchos conceptos sorprendentes para crear aplicaciones de inteligencia artificial. Creó el bot Slack y la utilidad de raspado web, que luego se conectaron mediante servicios sin servidor de AWS. Puede agregar mucho más a dicho marco inicial, por ejemplo, la función Lambda para procesar textos escritos en lenguajes naturales para leer páginas web y obtener sus breves contenidos o el algoritmo de agrupamiento sin un maestro, que agruparía a los nuevos jugadores de la NBA por atributos arbitrarios.
»Se puede encontrar más información sobre el libro en
el sitio web del editor»
Contenidos»
Extracto20% de descuento en cupones para mensajeros -
RegaloPD: el 7% del costo del libro se destinará a la traducción de nuevos libros de computadora, la lista de libros entregados a la imprenta está
aquí .