Привет, Хаброжители! Эта книга Ноя Гифта предназначена для всех, кого интересуют ИИ, машинное обучение, облачные вычисления, а также любое сочетание данных тем. Как программисты, так и просто неравнодушные технари найдут тут для себя полезную информацию. Примеры кода даны на Python. Здесь рассматривается множество столь продвинутых тем, как использование облачных платформ (например, AWS, GCP и Azure), а также приемы машинного обучения и реализация ИИ. Джедаи, свободно ориентирующиеся в Python, облачных вычислениях и ML, также найдут для себя много полезных идей, которые смогут сразу применить в своей текущей работе.
Предлагаем ознакомиться с отрывком из книги «Создание интеллектуального бота Slack в AWS»
Люди давно мечтают создать «искусственную жизнь». Чаще всего пока это возможно путем создания ботов. Боты становятся все более неотъемлемой частью нашей повседневной жизни, особенно после появления Siri от компании Apple и Alexa от Amazon. В этой главе мы раскроем все тайны создания ботов.
Для создания бота мы воспользуемся библиотекой Slack для языка Python (https://github.com/slackapi/python-slackclient). Для начала работы со Slack необходимо сгенерировать идентификационный маркер. В целом имеет смысл при работе с подобными маркерами экспортировать переменную среды. Я часто делаю это в virtualenv, получая, таким образом, автоматически доступ к ней при выполнении в текущей среде. Для этого необходимо немного «взломать» утилиту virtualenv, отредактировав сценарий activate.
При экспорте переменной Slack в сценарии ~/.env/bin/activate он будет иметь нижеприведенный вид.
И просто для информации, если вы хотите идти в ногу с последними новинками, рекомендуется использовать появившуюся на рынке новую, официальную утилиту Python для управления средой — pipenv (https://github.com/pypa/pipenv):
Для проверки того, задано ли значение переменной среды, удобно использовать команду printenv операционных систем OS X и Linux. После этого для проверки отправки сообщения можно воспользоваться следующим коротким сценарием:
Стоит также отметить, что утилита pipenv — рекомендуемое решение, объединяющее в одном компоненте возможности утилит pip и virtualenv. Она стала новым стандартом, так что имеет смысл взглянуть на нее с точки зрения управления пакетами.
Как и в других примерах из этой книги, удачной идеей будет преобразовать наш код в утилиту командной строки, чтобы облегчить проверку новых идей. Стоит отметить, что многие разработчики-новички часто отдают предпочтение не утилитам командной строки, а другим решениям, например, просто работают в блокнотах Jupiter. Сыграю ненадолго роль адвоката дьявола и задам вопрос, который вполне может возникнуть у читателей: «А зачем нам утилиты командной строки в проекте, основанном на блокнотах Jupiter? Разве смысл блокнотов Jupiter состоит не в том, чтобы сделать ненужными командную оболочку и командную строку?» Добавление утилиты командной строки в проект хорошо тем, что позволяет быстро пробовать различные варианты входных данных. Блоки кода блокнотов Jupiter не принимают входные данные, в некотором смысле это сценарии с жестко «зашитыми» данными.
Множество утилит командной строки на платформах как GCP, так и AWS существует не случайно: они обеспечивают гибкость и возможности, недоступные для графических интерфейсов. Замечательный сборник эссе на эту тему фантаста Нила Стивенсона (Neal Stephenson) называется «В начале… была командная строка». В нем Стивенсон говорит: «GUI приводят к значительным дополнительным накладным расходам на каждый, даже самый маленький компонент программного обеспечения, которые полностью меняют среду программирования». Он заканчивает сборник словами: «… жизнь — штука очень тяжелая и сложная; никакой интерфейс это не изменит; и всякий, кто считает иначе, — простофиля...» Достаточно жестко, но мой опыт подсказывает, что и достаточно правдиво. Жизнь с командной строкой становится лучше. Попробуйте ее — и вы не захотите возвращаться обратно к GUI.
Для этого мы воспользуемся пакетом click, как показано ниже. Отправка сообщений с помощью нового интерфейса оказывается очень простым делом.
Рисунок 7.1 демонстрирует значения по умолчанию, а также настраиваемое сообщение от утилиты cli.
После создания каналов связи для отправки сообщений в Slack можно усовершенствовать наш код, а именно: запускать его по расписанию и использовать для каких-либо полезных действий. Сервис пошаговых функций AWS (AWS Step Functions) замечательно подходит для этой цели. В следующем разделе наш бот Slack научится производить скрапинг спортивных страниц Yahoo! игроков НБА, извлекать их места рождения, а затем отправлять эти данные в Slack.
Рисунок 7.2 демонстрирует готовую пошаговую функцию в действии. Первый шаг состоит в извлечении URL профилей игроков НБА, а второй — в использовании библиотеки Beautiful Soup для поиска места рождения каждого из игроков. По завершении выполнения пошаговой функции результаты будут отправлены обратно в Slack.
Для координации отдельных частей работы внутри пошаговой функции можно применить AWS Lambda и Chalice. Lambda (https://aws.amazon.com/lambda/) позволяет пользователю выполнять функции в AWS, а фреймворк Chalice (http://chalice.readthedocs.io/en/latest/) дает возможность создания бессерверных приложений на языке Python. Вот некоторые предварительные требования:
Подробные инструкции по настройке учетных данных AWS можно найти по адресу boto3.readthedocs.io/en/latest/guide/configuration.html. Информацию об экспорте переменных AWS в операционных системах Windows и Linux можно найти здесь. Существует множество способов настройки учетных данных, но пользователи virtualenv могут поместить учетные данные AWS в локальную виртуальную среду, в сценарий /bin/activate:
Работа с Chalice. У Chalice есть утилита командной строки с множеством доступных команд:
Код внутри шаблона app.py можно заменить на функции сервиса Lambda. В AWS Chalice удобно то, что он дает возможность создавать, помимо веб-сервисов, «автономные» функции Lambda. Благодаря этой функциональности можно создать несколько функций Lambda, связать их с пошаговой функцией и свести воедино, как кубики «Лего».
Например, можно легко создать запускаемую по расписанию функцию Lambda, которая будет выполнять какие-либо действия:
Для налаживания взаимодействия с ботом для веб-скрапинга необходимо создать несколько функций. В начале файла находятся импорты и объявлено некоторое количество переменных:
Боту может понадобиться хранить часть данных в S3. Следующая функция использует Boto для сохранения результатов в CSV-файле:
Функция fetch_page использует библиотеку Beautiful Soup для синтаксического разбора HTML-страницы, расположенной в соответствии с URL статистики НБА, и возвращает объект soup:
Функции get_player_links и fetch_player_urls получают ссылки на URL профилей игроков:
Далее в функции find_birthplaces мы извлекаем с расположенных по этим URL страниц места рождения игроков:
Теперь мы перейдем к функциям Chalice. Обратите внимание: для фреймворка Chalice необходимо, чтобы был создан путь по умолчанию:
Следующая функция Lambda представляет собой маршрут, связывающий HTTP URL с написанной ранее функцией:
Следующие функции Lambda — автономные, их можно вызвать внутри пошаговой функции:
Если запустить получившееся приложение Chalice локально, будут выведены следующие результаты:
Для развертывания приложения выполните команду chalice deploy:
Благодаря интерфейсу командной строки для HTTP (https://github.com/jakubroztocil/httpie) мы вызываем маршрут HTTP из AWS и извлекаем доступные в /api/player_urls ссылки:
Еще один удобный способ работы с функциями Lambda — непосредственный их вызов с помощью пакета click и библиотеки Boto языка Python.
Мы можем создать новую утилиту командной строки с названием wscli.py (сокращение от web-scraping command-line interface — «интерфейс командной строки для веб-скрапинга»). В первой части кода мы настраиваем журналирование и импортируем библиотеки:
Следующие три функции предназначены для подключения к функции Lambda через invoke_lambda:
Обертываем функцию invoke_lambda с помощью пакета Python для создания утилит командной строки Click. Обратите внимание, что мы задали значение по умолчанию для опции --func, при котором используется развернутая нами ранее функция Lambda:
Выводимые этой утилитой результаты аналогичны вызову HTTP-интерфейса:
Последний этап создания пошаговой функции, как описывается в документации от AWS (https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-creating-activity-state-machine.html), — создание с помощью веб-интерфейса структуры конечного автомата в формате нотации объектов JavaScript (JavaScript Object Notation, JSON). Следующий код демонстрирует этот конвейер, начиная от исходных функций Lambda для скрапинга Yahoo!, сохранения данных в файле S3 и, наконец, отправки содержимого в Slack:
На рис. 7.2 было показано выполнение первой части этого конвейера. Чрезвычайно полезна возможность видеть промежуточные результаты работы конечного автомата. Кроме того, возможность мониторинга в режиме реального времени каждой части конечного автомата очень удобна для отладки.
Рисунок 7.3 демонстрирует полный конвейер с добавлением шагов записи в S3-файл и отправки содержимого в Slack. Осталось только решить, как запускать эту утилиту скрапинга — через определенный интервал времени или в ответ на какое-либо событие.
В этой главе вы познакомились с множеством потрясающих концепций построения приложений ИИ. В ней были созданы бот Slack и утилита веб-скрапинга, соединенные затем с помощью бессерверных сервисов от AWS. В такой начальный каркас можно добавить еще много всего — например, Lambda-функцию обработки написанных на естественных языках текстов для чтения веб-страниц и получения их краткого содержимого или алгоритм кластеризации без учителя, который бы кластеризовал новых игроков НБА по произвольным атрибутам.
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 20% по купону — Гифт
P.S.: 7% от стоимости книги пойдет на перевод новых компьютерных книг, список сданных в типографию книг здесь.
Предлагаем ознакомиться с отрывком из книги «Создание интеллектуального бота Slack в AWS»
Люди давно мечтают создать «искусственную жизнь». Чаще всего пока это возможно путем создания ботов. Боты становятся все более неотъемлемой частью нашей повседневной жизни, особенно после появления Siri от компании Apple и Alexa от Amazon. В этой главе мы раскроем все тайны создания ботов.
Создание бота
Для создания бота мы воспользуемся библиотекой Slack для языка Python (https://github.com/slackapi/python-slackclient). Для начала работы со Slack необходимо сгенерировать идентификационный маркер. В целом имеет смысл при работе с подобными маркерами экспортировать переменную среды. Я часто делаю это в virtualenv, получая, таким образом, автоматически доступ к ней при выполнении в текущей среде. Для этого необходимо немного «взломать» утилиту virtualenv, отредактировав сценарий activate.
При экспорте переменной Slack в сценарии ~/.env/bin/activate он будет иметь нижеприведенный вид.
И просто для информации, если вы хотите идти в ногу с последними новинками, рекомендуется использовать появившуюся на рынке новую, официальную утилиту Python для управления средой — 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
Для проверки того, задано ли значение переменной среды, удобно использовать команду printenv операционных систем OS X и Linux. После этого для проверки отправки сообщения можно воспользоваться следующим коротким сценарием:
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:"
)
Стоит также отметить, что утилита pipenv — рекомендуемое решение, объединяющее в одном компоненте возможности утилит pip и virtualenv. Она стала новым стандартом, так что имеет смысл взглянуть на нее с точки зрения управления пакетами.
Преобразование библиотеки в утилиту командной строки
Как и в других примерах из этой книги, удачной идеей будет преобразовать наш код в утилиту командной строки, чтобы облегчить проверку новых идей. Стоит отметить, что многие разработчики-новички часто отдают предпочтение не утилитам командной строки, а другим решениям, например, просто работают в блокнотах Jupiter. Сыграю ненадолго роль адвоката дьявола и задам вопрос, который вполне может возникнуть у читателей: «А зачем нам утилиты командной строки в проекте, основанном на блокнотах Jupiter? Разве смысл блокнотов Jupiter состоит не в том, чтобы сделать ненужными командную оболочку и командную строку?» Добавление утилиты командной строки в проект хорошо тем, что позволяет быстро пробовать различные варианты входных данных. Блоки кода блокнотов Jupiter не принимают входные данные, в некотором смысле это сценарии с жестко «зашитыми» данными.
Множество утилит командной строки на платформах как GCP, так и AWS существует не случайно: они обеспечивают гибкость и возможности, недоступные для графических интерфейсов. Замечательный сборник эссе на эту тему фантаста Нила Стивенсона (Neal Stephenson) называется «В начале… была командная строка». В нем Стивенсон говорит: «GUI приводят к значительным дополнительным накладным расходам на каждый, даже самый маленький компонент программного обеспечения, которые полностью меняют среду программирования». Он заканчивает сборник словами: «… жизнь — штука очень тяжелая и сложная; никакой интерфейс это не изменит; и всякий, кто считает иначе, — простофиля...» Достаточно жестко, но мой опыт подсказывает, что и достаточно правдиво. Жизнь с командной строкой становится лучше. Попробуйте ее — и вы не захотите возвращаться обратно к GUI.
Для этого мы воспользуемся пакетом click, как показано ниже. Отправка сообщений с помощью нового интерфейса оказывается очень простым делом.
./clibot.py send --message "from cli"
sending message from cli to #general
Рисунок 7.1 демонстрирует значения по умолчанию, а также настраиваемое сообщение от утилиты 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()
Выводим бот на новый уровень с помощью сервиса AWS Step Functions
После создания каналов связи для отправки сообщений в Slack можно усовершенствовать наш код, а именно: запускать его по расписанию и использовать для каких-либо полезных действий. Сервис пошаговых функций AWS (AWS Step Functions) замечательно подходит для этой цели. В следующем разделе наш бот Slack научится производить скрапинг спортивных страниц Yahoo! игроков НБА, извлекать их места рождения, а затем отправлять эти данные в Slack.
Рисунок 7.2 демонстрирует готовую пошаговую функцию в действии. Первый шаг состоит в извлечении URL профилей игроков НБА, а второй — в использовании библиотеки Beautiful Soup для поиска места рождения каждого из игроков. По завершении выполнения пошаговой функции результаты будут отправлены обратно в Slack.
Для координации отдельных частей работы внутри пошаговой функции можно применить AWS Lambda и Chalice. Lambda (https://aws.amazon.com/lambda/) позволяет пользователю выполнять функции в AWS, а фреймворк Chalice (http://chalice.readthedocs.io/en/latest/) дает возможность создания бессерверных приложений на языке Python. Вот некоторые предварительные требования:
- у пользователя должна быть учетная запись AWS;
- пользователю необходимы учетные данные для использования API;
- у роли Lambda (создаваемой Chalice) должна быть политика с привилегиями, необходимыми для вызова соответствующих сервисов AWS, например S3.
Настройка учетных данных IAM
Подробные инструкции по настройке учетных данных AWS можно найти по адресу boto3.readthedocs.io/en/latest/guide/configuration.html. Информацию об экспорте переменных AWS в операционных системах Windows и Linux можно найти здесь. Существует множество способов настройки учетных данных, но пользователи virtualenv могут поместить учетные данные AWS в локальную виртуальную среду, в сценарий /bin/activate:
#Добавляем ключи 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
Работа с Chalice. У Chalice есть утилита командной строки с множеством доступных команд:
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
Код внутри шаблона app.py можно заменить на функции сервиса Lambda. В AWS Chalice удобно то, что он дает возможность создавать, помимо веб-сервисов, «автономные» функции Lambda. Благодаря этой функциональности можно создать несколько функций Lambda, связать их с пошаговой функцией и свести воедино, как кубики «Лего».
Например, можно легко создать запускаемую по расписанию функцию Lambda, которая будет выполнять какие-либо действия:
@app.schedule(Rate(1, unit=Rate.MINUTES))
def every_minute(event):
"""Событие, запланированное для ежеминутного выполнения"""
#Отправка сообщения боту Slack
Для налаживания взаимодействия с ботом для веб-скрапинга необходимо создать несколько функций. В начале файла находятся импорты и объявлено некоторое количество переменных:
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)
Боту может понадобиться хранить часть данных в S3. Следующая функция использует Boto для сохранения результатов в 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
Функция fetch_page использует библиотеку Beautiful Soup для синтаксического разбора HTML-страницы, расположенной в соответствии с URL статистики НБА, и возвращает объект soup:
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
Функции get_player_links и fetch_player_urls получают ссылки на URL профилей игроков:
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
Далее в функции find_birthplaces мы извлекаем с расположенных по этим URL страниц места рождения игроков:
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
Теперь мы перейдем к функциям Chalice. Обратите внимание: для фреймворка Chalice необходимо, чтобы был создан путь по умолчанию:
#Их можно вызвать с помощью HTTP-запросов
@app.route('/')
def index():
"""Корневой URL"""
app.log.info(f"/ Route: for {APP_NAME}")
return {'app_name': APP_NAME}
Следующая функция Lambda представляет собой маршрут, связывающий HTTP URL с написанной ранее функцией:
@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}
Следующие функции Lambda — автономные, их можно вызвать внутри пошаговой функции:
#Это автономная функция 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
Если запустить получившееся приложение Chalice локально, будут выведены следующие результаты:
→ 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"
Для развертывания приложения выполните команду chalice deploy:
→ 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/
Благодаря интерфейсу командной строки для HTTP (https://github.com/jakubroztocil/httpie) мы вызываем маршрут HTTP из AWS и извлекаем доступные в /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/"
]
}
Еще один удобный способ работы с функциями Lambda — непосредственный их вызов с помощью пакета click и библиотеки Boto языка Python.
Мы можем создать новую утилиту командной строки с названием wscli.py (сокращение от web-scraping command-line interface — «интерфейс командной строки для веб-скрапинга»). В первой части кода мы настраиваем журналирование и импортируем библиотеки:
#!/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)
Следующие три функции предназначены для подключения к функции Lambda через 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
Обертываем функцию invoke_lambda с помощью пакета Python для создания утилит командной строки Click. Обратите внимание, что мы задали значение по умолчанию для опции --func, при котором используется развернутая нами ранее функция Lambda:
@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()
Выводимые этой утилитой результаты аналогичны вызову 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"
}
Завершение создания пошаговой функции
Последний этап создания пошаговой функции, как описывается в документации от AWS (https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-creating-activity-state-machine.html), — создание с помощью веб-интерфейса структуры конечного автомата в формате нотации объектов JavaScript (JavaScript Object Notation, JSON). Следующий код демонстрирует этот конвейер, начиная от исходных функций Lambda для скрапинга Yahoo!, сохранения данных в файле S3 и, наконец, отправки содержимого в 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
}
}
}
На рис. 7.2 было показано выполнение первой части этого конвейера. Чрезвычайно полезна возможность видеть промежуточные результаты работы конечного автомата. Кроме того, возможность мониторинга в режиме реального времени каждой части конечного автомата очень удобна для отладки.
Рисунок 7.3 демонстрирует полный конвейер с добавлением шагов записи в S3-файл и отправки содержимого в Slack. Осталось только решить, как запускать эту утилиту скрапинга — через определенный интервал времени или в ответ на какое-либо событие.
Резюме
В этой главе вы познакомились с множеством потрясающих концепций построения приложений ИИ. В ней были созданы бот Slack и утилита веб-скрапинга, соединенные затем с помощью бессерверных сервисов от AWS. В такой начальный каркас можно добавить еще много всего — например, Lambda-функцию обработки написанных на естественных языках текстов для чтения веб-страниц и получения их краткого содержимого или алгоритм кластеризации без учителя, который бы кластеризовал новых игроков НБА по произвольным атрибутам.
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 20% по купону — Гифт
P.S.: 7% от стоимости книги пойдет на перевод новых компьютерных книг, список сданных в типографию книг здесь.