В этой главе вы научитесь писать специализированные программы для построения визуализаций на основании загруженных ими данных. Ваша программа будет использовать программный интерфейс (API) веб-приложения для автоматического запроса конкретной информации с сайта (вместо целых страниц). Полученная информация будет использоваться для построения визуализации. Так как программы, написанные по такой схеме, всегда используют самые свежие данные для построения визуализации, даже при быстро изменяющихся данных полученная диаграмма всегда будет оставаться актуальной.
API веб-приложения представляет собой часть веб-сайта, предназначенную для взаимодействия с программами, которые используют особым образом построенные URL-адреса для запроса информации. Подобные запросы называются вызовами API. Запрашиваемые данные возвращаются в удобном формате (например, JSON или CSV). Многие приложения, зависящие от внешних источников данных (как приложения, интегрирующиеся с сайтами социальных сетей), используют вызовы API.
Наша визуализация будет построена на базе информации с GitHub — сайта, организующего совместную работу программистов над проектами. Мы воспользуемся API GitHub для запроса информации о проектах Python и последующего построения интерактивной визуализации относительной популярности этих проектов в Pygal.
Имя GitHub (https://github.com/) происходит от Git — распределенной системы управления версиями, которая позволяет программистам совместно трудиться над проектами. Пользователи Git управляют своим индивидуальным вкладом в проект, чтобы изменения, вносимые одним человеком, не конфликтовали с изменениями, вносимыми другими людьми. Когда вы реализуете новую возможность в проекте, Git отслеживает изменения, внесенные в каждый файл. Если новый код успешно работает, вы закрепляете внесенные изменения, и Git записывает новое состояние проекта. Если же вы допустили ошибку и хотите отменить внесенные изменения, Git позволяет легко вернуться к любому из предыдущих рабочих состояний. (За дополнительной информацией об управлении версиями с использованием Git обращайтесь к приложению Г.) Проекты GitHub хранятся в репозиториях, содержащих все ресурсы, связанные с проектом: код, информацию о других участниках, все проблемы или отчеты об ошибках и т.д.
Если проект нравится пользователям GitHub, то пользователи могут «поставить звезду», чтобы продемонстрировать свою поддержку и следить за проектами, которые могут им пригодиться. В этой главе мы напишем программу для автоматической загрузки информации о проектах Python с наибольшим количеством звезд на GitHub, а затем построим содержательную визуализацию таких проектов.
GitHub поддерживает API (программный интерфейс) для запроса разнообразной информации посредством вызовов API. Чтобы понять, как выглядит вызов API, введите следующий адрес в адресной строке своего браузера и нажмите Enter:
https://api.github.com/search/repositories?q=language:python&sort=stars
Этот вызов возвращает количество проектов Python, размещенных на GitHub в настоящее время, а также информацию о самых популярных репозиториях Python. Рассмотрим вызов подробнее: первая часть https://api.github.com/ передает запрос части сайта GitHub, отвечающей на вызовы API. Следующая часть, search/repositories, приказывает API провести поиск по всем репозиториям в GitHub.
Вопросительный знак после repositories означает, что мы собираемся передать аргумент. Символ q обозначает запрос (Query), а знак равенства начинает определение запроса (q=). Выражение language:python указывает, что запрашивается информация только по репозиториям, для которых основным языком указан Python. Завершающая часть, &sort=stars, сортирует проекты по количеству присвоенных им звезд.
В следующем фрагменте приведены несколько начальных строк ответа. Вероятно, по виду ответа вы уже поняли, что этот URL-адрес не предназначен для обычных пользователей.
{
"total_count": 713062,
"incomplete_results": false,
"items": [
. .{
. . "id": 3544424,
. . "name": "httpie",
. . "full_name": "jkbrzt/httpie",
. . ...
Как видно из второй строки вывода, на момент написания книги на GitHub было найдено 713 062 проектов Python. Значение "incomplete_results" равно false, а значит, запрос был обработан успешно (информация не является неполной). Если бы у GitHub возникли проблемы с полной обработкой запроса API, то в этом поле было бы возвращено значение true. Возвращаемые данные отображаются в списке "items" с информацией о самых популярных проектах Python на GitHub.
Пакет requests предоставляет удобные средства для запроса информации с сайтов из программ Python и анализа полученных ответов. Чтобы установить requests, введите команду следующего вида:
$ pip install --user requests
Если вы еще не использовали pip, обратитесь к разделу «Установка пакетов Python с использованием pip» на с. 227. (Возможно, вам придется использовать другую версию команды в зависимости от конфигурации системы.)
Теперь мы напишем программу, которая выдает вызов API для поиска на Github проектов Python с наибольшим количеством звезд:
python_repos.py
(1) import requests
# Создание вызова API и сохранение ответа.
(2)url = 'https://api.github.com/search/repositories?q=language:python &sort=stars'
(3)r = requests.get(url)
(4)print("Status code:", r.status_code)
# Сохранение ответа API в переменной.
(5)response_dict = r.json()
# Обработка результатов.
print(response_dict.keys())
В точке (1) импортируется модуль requests. В точке (2) URL-адрес вызова API сохраняется в переменной, после чего модуль requests используется для вызова (3). Мы вызываем метод get() и передаем ему URL, а объект ответа сохраняется в переменной r. Объект ответа содержит атрибут status_code, в котором хранится признак успешного выполнения запроса. (Код 200 — признак успешного ответа.) В точке (4) программа выводит значение status_code, чтобы вы могли убедиться в том, что вызов был обработан успешно.
API возвращает информацию в формате JSON, поэтому в программе используется метод json() (5) для преобразования информации в словарь Python. Полученный словарь сохраняется в переменной response_dict.
Наконец, программа выводит ключи словаря response_dict, и мы видим следующее:
Status code: 200
dict_keys(['items', 'total_count', 'incomplete_results'])
Код статуса 200 означает, что запрос был обработан успешно. Словарь ответа содержит всего три ключа: ‘items’, 'total_count' и 'incomplete_results'.
Примечание
Подобные простые вызовы должны возвращать полный набор результатов, поэтому значение, связанное с 'incomplete_results’, можно достаточно безопасно игнорировать. Но, если ваша программа выдает более сложные вызовы API, обязательно проверяйте это значение.
Итак, полученная при вызове API информация хранится в словаре, и мы можем заняться работой с данными. Для начала построим сводку с обобщенными сведениями — это позволит убедиться в том, что вызов вернул ожидаемую информацию, и перейти к анализу интересующих данных.
python_repos.py
import requests
# Создание вызова API и сохранение ответа.
url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
r = requests.get(url)
print("Status code:", r.status_code)
# Сохранение ответа API в переменной.
response_dict = r.json()
(1) print("Total repositories:", response_dict['total_count'])
# Анализ информации о репозиториях.
(2)repo_dicts = response_dict['items']
print("Repositories returned:", len(repo_dicts))
# Анализ первого репозитория.
(3)repo_dict = repo_dicts[0]
(4)print("\nKeys:", len(repo_dict))
(5)for key in sorted(repo_dict.keys()):
. .print(key)
В точке (1) выводится значение, связанное с 'total_count', которое представляет общее количество репозиториев Python в GitHub.
Значение, связанное с 'items', представляет собой список со словарями, каждый из которых содержит данные об одном репозитории Python. В точке (2) этот список словарей сохраняется в repo_dicts. Затем программа выводит длину repo_dicts, чтобы пользователь видел, по какому количеству репозиториев имеется информация.
Чтобы получить первое представление об информации, возвращенной по каждому репозиторию, программа извлекает первый элемент из repo_dicts и сохраняет его в repo_dict (3). Затем программа выводит количество ключей в словаре — это значение определяет объем доступной информации (4). В точке (5) выводятся все ключи словаря; по ним можно понять, какая информация включена в ответ.
Из сводки начинает вырисовываться более четкая картина полученных данных:
Status code: 200
Total repositories: 713062
Repositories returned: 30
(1) Keys: 68
archive_url
assignees_url
blobs_url
...
url
watchers
watchers_count
API GitHub возвращает подробную информацию о каждом репозитории: в repo_dict 68 ключей (1) . Просмотр ключей дает представление о том, какую информацию можно извлечь о проекте. (Чтобы узнать, какую информацию можно получить через API, следует либо прочитать документацию, либо проанализировать информацию в коде, как мы и поступаем.)
Прочитаем значения некоторых ключей repo_dict:
python_repos.py
...
# Анализ информации о репозиториях.
repo_dicts = response_dict['items']
print("Repositories returned:", len(repo_dicts))
# Анализ первого репозитория.
repo_dict = repo_dicts[0]
print("\nSelected information about first repository:")
(1) print('Name:', repo_dict['name'])
(2)print('Owner:', repo_dict['owner']['login'])
(3)print('Stars:', repo_dict['stargazers_count'])
print('Repository:', repo_dict['html_url'])
(4)print('Created:', repo_dict['created_at'])
(5)print('Updated:', repo_dict['updated_at'])
print('Description:', repo_dict['description'])
В программе выводятся значения, связанные с некоторыми ключами словаря первого репозитория. В точке (1) выводится имя проекта. Владельца проекта представляет целый словарь, поэтому в точке (2) ключ owner используется для обращения к словарю, представляющему владельца, после чего ключ login используется для получения регистрационного имени владельца. В точке (3) выводится количество звезд, заработанных проектом, и URL репозитория GitHub проекта. Далее выводится дата создания (4) и последнего обновления репозитория (5). В завершение выводится описание репозитория; вывод должен выглядеть примерно так:
Status code: 200
Total repositories: 713065
Repositories returned: 30
Selected information about first repository:
Name: httpie
Owner: jkbrzt
Stars: 16101
Repository: https://github.com/jkbrzt/httpie
Created: 2012-02-25T12:39:13Z
Updated: 2015-07-13T14:56:41Z
Description: CLI HTTP client; user-friendly cURL replacement featuring
intuitive UI, JSON support, syntax highlighting, wget-like downloads,
extensions, etc.
Из вывода видно, что на момент написания книги самым «звездным» проектом Python на GitHub был проект HTTPie, владельцем которого является пользователь jkbrzt, и звезды этот проект получил более чем от 16 000 пользователей GitHub. Мы видим URL репозитория проекта, дату создания (февраль 2012 г.) и то, что проект недавно обновлялся. Наконец, из описания следует, что HTTPie помогает выдавать вызовы HTTP из терминала (CLI — сокращение от «Command Line Interface», то есть «интерфейс командной строки»).
При построении визуализации этих данных на диаграмму необходимо нанести более одного репозитория. Напишем цикл для вывода информации о каждом репозитории, возвращаемом вызовом API, чтобы все эти репозитории можно было включить в визуализацию:
python_repos.py
...
# Анализ информации о репозиториях.
repo_dicts = response_dict['items']
print("Repositories returned:", len(repo_dicts))
(1) print("\nSelected information about each repository:")
(2)for repo_dict in repo_dicts:
. .print('\nName:', repo_dict['name'])
. .print('Owner:', repo_dict['owner']['login'])
. .print('Stars:', repo_dict['stargazers_count'])
. .print('Repository:', repo_dict['html_url'])
. .print('Description:', repo_dict['description'])
В точке (1) выводится приветственное сообщение. В точке (2) перебираются все словари в repo_dicts. Внутри цикла выводится имя каждого проекта, его владелец, количество звезд, URL на GitHub и краткое описание проекта:
Status code: 200
Total repositories: 713067
Repositories returned: 30
Selected information about each repository:
Name: httpie
Owner: jkbrzt
Stars: 16101
Repository: https://github.com/jkbrzt/httpie
Description: CLI HTTP client; user-friendly cURL replacement featuring
intuitive UI, JSON support, syntax highlighting, wget-like downloads,
extensions, etc.
Name: django
Owner: django
Stars: 15028
Repository: https://github.com/django/django
Description: The Web framework for perfectionists with deadlines.
...
Name: powerline
Owner: powerline
Stars: 4315
Repository: https://github.com/powerline/powerline
Description: Powerline is a statusline plugin for vim, and provides
statuslines and prompts for several other applications, including zsh, bash,
tmux, IPython, Awesome and Qtile.
В этих результатах встречаются интересные проекты; возможно, вам стоит присмотреться к некоторым из них… Но не увлекайтесь, потому что мы собираемся создать визуализацию, которая существенно упростит чтение результатов.
Многие API ограничивают частоту обращений; иначе говоря, существует предел для количества запросов в определенный промежуток времени. Чтобы узнать, не приближаетесь ли вы к ограничениям GitHub, введите в браузере адрес https://api.github.com/rate_limit. Вы получите ответ, который выглядит примерно так:
{
"resources": {
. ."core": {
. . "limit": 60,
. . "remaining": 58,
. . "reset": 1426082320
. .},
(1) . ."search": {
(2) . . "limit": 10,
(3) . . "remaining": 8,
(4) . . "reset": 1426078803
. .}
},
"rate": {
. ."limit": 60,
. ."remaining": 58,
. ."reset": 1426082320
}
}
В этих данных нас интересует частота обращений для поискового API (1) . В точке (2) видно, что предельная частота составляет 10 запросов в минуту и что на текущую минуту осталось еще 8 запросов (3). Значение reset представляет Unix-время, или эпохальное время (число секунд, прошедших с полуночи 1 января 1970 года) момента, когда произойдет сброс квоты (4). При достижении предельного количества обращений вы получите короткий ответ, уведомляющий о достижении предела API. Если вы достигнете предела, просто подождите, пока квота будет сброшена.
Примечание
Многие API требуют регистрации и получения ключа API для совершения вызовов. На момент написания для GitHub такого требования не было, но, если вы получите ключ API, предельная частота обращений для ваших программ значительно увеличится.
Теперь, с появлением интересных данных, мы построим визуализацию, демонстрирующую относительную популярность проектов Python в GitHub. Мы построим интерактивную столбцовую диаграмму: высота каждого столбца будет представлять количество звезд у проекта. Щелчок на столбце будет открывать домашнюю страницу проекта на GitHub. Первая попытка выглядит так:
python_repos.py
import requests
import pygal
from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS
# Создание вызова API и сохранение ответа.
URL = 'https://api.github.com/search/repositories?q=language:python&sort=star'
r = requests.get(URL)
print("Status code:", r.status_code)
# Сохранение ответа API в переменной.
response_dict = r.json()
print("Total repositories:", response_dict['total_count'])
# Анализ информации о репозиториях.
repo_dicts = response_dict['items']
(1) names, stars = [], []
for repo_dict in repo_dicts:
(2) . .names.append(repo_dict['name'])
. .stars.append(repo_dict['stargazers_count'])
# Построение визуализации.
(3)my_style = LS('#333366', base_style=LCS)
(4)chart = pygal.Bar(style=my_style, x_label_rotation=45, show_ legend=False)
chart.title = 'Most-Starred Python Projects on GitHub'
chart.x_labels = names
(5)chart.add('', stars)
chart.render_to_file('python_repos.svg')
Сначала импортируется модуль pygal и стили Pygal, необходимые для оформления диаграммы. Затем выводится статус ответа на вызов API и общее количество обнаруженных репозиториев, чтобы мы сразу узнали о возможной проблеме с вызовом API. Информация о конкретных возвращенных проектах уже не выводится, потому что эта информация будет включена в визуализацию.
В точке (1) создаются два пустых списка для хранения данных, включаемых в диаграмму. Нам понадобится имя каждого проекта (для пометки столбцов) и количество звезд, определяющее высоту столбцов. В цикле имя каждого проекта и количество звезд присоединяются к соответствующему списку (2).
Затем программа определяет стиль с использованием класса LightenStyle (псевдоним LS) на базе темного оттенка синего цвета (3). Также передается аргумент base_style для использования класса LightColorizedStyle (псевдоним LCS). Метод Bar() создает простую столбцовую диаграмму, которой передается стиль my_style (4). Также передаются еще два стилевых аргумента: метки поворачиваются под 45 градусов к оси x (x_label_rotation=45), а условные обозначения скрываются, потому что на диаграмму наносится только одна серия данных (show_legend=False). Затем назначается заголовок диаграммы, а атрибуту x_labels присваивается список names.
Так как метки для этой серии данных не нужны, при добавлении данных (5) вместо метки передается пустая строка. Полученная диаграмма изображена на рис. 17.1. Мы видим, что несколько первых проектов существенно популярнее остальных, но все эти проекты занимают важное место в экосистеме Python.
Рис. 17.1. Проекты Python на GitHub с наибольшим количеством звезд
Немного доработаем стилевое оформление диаграммы. Мы собираемся внести несколько разных настроек, поэтому для начала слегка изменим структуру кода: создадим конфигурационный объект, который содержит все настройки, передаваемые Bar():
python_repos.py
...
# Построение визуализации.
my_style = LS('#333366', base_style=LCS)
(1) my_config = pygal.Config()
(2)my_config.x_label_rotation = 45
my_config.show_legend = False
(3)my_config.title_font_size = 24
my_config.label_font_size = 14
my_config.major_label_font_size = 18
(4)my_config.truncate_label = 15
(5)my_config.show_y_guides = False
? my_config.width = 1000
? chart = pygal.Bar(my_config, style=my_style)
chart.title = 'Most-Starred Python Projects on GitHub'
chart.x_labels = names
chart.add('', stars)
chart.render_to_file('python_repos.svg')
В точке (1) создается экземпляр класса Pygal Config с именем my_config; изменение атрибутов my_config приведет к изменению внешнего вида диаграммы. Мы задаем два атрибута x_label_rotation и show_legend (2), которые ранее передавались в виде именованных аргументов при создании экземпляра Bar. В точке (3) назначаются размеры шрифта для заголовка диаграммы, дополнительных и основных меток. К дополнительным меткам на этой диаграмме относятся имена проектов по оси x и большинство чисел по оси y. Основными метками являются только метки оси y для делений, кратных 5000 звездам. Эти метки должны быть более крупными, поэтому мы назначаем им другой размер шрифта. В точке (4) атрибут truncate_label используется для сокращения длинных имен проектов до 15 символов. (Если навести указатель мыши на сокращенное имя проекта, на экране появляется подсказка с полным именем.) Затем мы скрываем горизонтальные линии на графике, задавая атрибуту show_y_guides значение False (5). Наконец, в точке ? назначается ширина диаграммы для более эффективного использования доступного места в браузере.
Теперь при создании экземпляра Bar ? мы передаем my_config в первом аргументе; таким образом, один аргумент передает сразу все настройки конфигурации.
Рис. 17.2. Диаграмма с улучшенным оформлением
При желании в my_config можно внести сколько угодно стилевых и конфигурационных изменений; строка ? от этого не изменится. На рис. 17.2 изображена диаграмма с измененным оформлением.
В Pygal при наведении указателя мыши на отдельный столбец отображается информация, которую этот столбец представляет. В текущей версии экранная подсказка (tooltip) отображает количество звезд проекта. Давайте создадим нестандартную подсказку, которая также будет выводить описание каждого проекта.
Рассмотрим короткий пример, в котором каждый из первых трех проектов выводится отдельно, с передачей нестандартных меток для каждого столбца. Для этого методу add() следует передавать список словарей вместо списка значений:
bar_descriptions.py
import pygal
from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS
my_style = LS('#333366', base_style=LCS)
chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False)
chart.title = 'Python Projects'
chart.x_labels = ['httpie', 'django', 'flask']
(1) plot_dicts = [
(2) . .{'value': 16101, 'label': 'Description of httpie.'},
. .{'value': 15028, 'label': 'Description of django.'},
. .{'value': 14798, 'label': 'Description of flask.'},
. .]
(3)chart.add('', plot_dicts)
chart.render_to_file('bar_descriptions.svg')
Рис. 17.3. С каждым столбцом связывается нестандартная подсказка
В точке (1) определяется список с именем plot_dicts, который содержит три словаря: для проекта HTTPie, для проекта Django и для проекта Flask. Каждый словарь содержит два ключа: 'value' и 'label'. Pygal использует число, связанное с 'value', для определения высоты каждого столбца, а строка, связанная с 'label', используется для создания подсказки столбца. Например, первый словарь (2) создаст столбец, представляющий проект с 16 101 звездой, а в его подсказке будет содержаться текст Description of httpie.
Метод add() должен получать строку и список. При вызове add() передается список словарей, представляющих столбцы (plot_dicts) (3). Одна из подсказок изображена на рис. 17.3. Pygal включает количество звезд как подсказку по умолчанию в дополнение к нестандартной подсказке, которую мы определили в программе.
Чтобы построить диаграмму с данными, мы сгенерируем plot_dicts автоматически для 30 проектов, возвращенных вызовом API.
python_repos.py
...
# Анализ информации о репозиториях.
repo_dicts = response_dict['items']
print("Number of items:", len(repo_dicts))
(1) names, plot_dicts = [], []
for repo_dict in repo_dicts:
names.append(repo_dict['name'])
. .
(2) . .plot_dict = {
. . . .'value': repo_dict['stargazers_count'],
. . . .'label': repo_dict['description'],
. . . .}
(3) . .plot_dicts.append(plot_dict)
# Построение визуализации.
my_style = LS('#333366', base_style=LCS)
...
(4)chart.add('', plot_dicts)
chart.render_to_file('python_repos.svg')
В точке (1) создаются два пустых списка, names и plot_dicts. Список names все еще необходим для построения меток оси x.
Внутри цикла для каждого проекта создается словарь plot_dict (2). В нем количество звезд сохраняется с ключом 'value', а описание проекта — с ключом 'label'. Затем словарь plot_dict каждого проекта присоединяется к plot_dicts (3). В точке (4) список plot_dicts передается add(). Полученная диаграмма изображена на рис. 17.4.
Рис. 17.4. При наведении указателя мыши на столбец появляется описание проекта
Pygal также позволяет использовать каждый столбец диаграммы как ссылку на сайт. Чтобы реализовать эту возможность, достаточно добавить всего одну строку кода, в которой используется словарь, создаваемый для каждого проекта. В словарь plot_dict каждого проекта добавляется новая пара «ключ—значение» с ключом 'xlink':
python_repos.py
...
names, plot_dicts = [], []
for repo_dict in repo_dicts:
names.append(repo_dict['name'])
plot_dict = {
'value': repo_dict['stargazers_count'],
'label': repo_dict['description'],
. . . .'xlink': repo_dict['html_url'],
}
plot_dicts.append(plot_dict)
...
Pygal использует URL-адрес, связанный с 'xlink', для преобразования каждого столбца в активную ссылку. Если щелкнуть на любом столбце диаграммы, в браузере автоматически открывается новая вкладка со страницей GitHub соответствующего проекта. Итак, мы создали интерактивную, содержательную визуализацию данных, загруженных через API!
Чтобы познакомиться с использованием вызовов API для других сайтов, мы обратимся к сайту Hacker News (http://news.ycombinator.com/). На этом сайте пользователи делятся друг с другом статьями, посвященными программированию и технологиям, а также активно обсуждают эти статьи. API сайта Hacker News предоставляет доступ ко всем статьям и комментариям на сайте, а для его использования не требуется регистрация с получением ключа.
Следующий вызов возвращает информацию о текущей самой популярной статье (на момент написания книги):
https://hacker-news.firebaseio.com/v0/item/9884165.json
Ответ представляет собой словарь с информацией о статье с идентификатором 9884165:
{
(1) . .'url': 'http://www.bbc.co.uk/news/science-environment-33524589',
. .'type': 'story',
(2) . .'title': 'New Horizons: Nasa spacecraft speeds past Pluto',
(3) . .'descendants': 141,
. .'score': 230,
. .'time': 1436875181,
. .'text': '',
. .'by': 'nns',
. .'id': 9884165,
(4) . .'kids': [9884723, 9885099, 9884789, 9885604, 9885844]
}
Словарь содержит ряд ключей, которые могут нам пригодиться, — например, 'url' (1) и 'title' (2). Ключ 'descendants' содержит количество комментариев, полученных статьей (3). Ключ 'kids' предоставляет идентификаторы всех дочерних комментариев, сделанных непосредственно в ответ на эту статью (4). У каждого из этих комментариев могут быть свои дочерние комментарии, так что количество потомков у статьи может быть больше количества дочерних комментариев.
Создадим вызов API для получения идентификаторов статей, наиболее популярных на Hacker News, а затем рассмотрим каждую из этих статей:
hn_submissions.py
import requests
from operator import itemgetter
# Создание вызова API и сохранение ответа.
(1) url = 'https://hacker-news.firebaseio.com/v0/topstories.json'
r = requests.get(url)
print("Status code:", r.status_code)
# Обработка информации о каждой статье.
(2)submission_ids = r.json()
(3)submission_dicts = []
for submission_id in submission_ids[:30]:
. .# Создание отдельного вызова API для каждой статьи.
(4) . .url = ('https://hacker-news.firebaseio.com/v0/item/' +
. . . . . .str(submission_id) + '.json')
. .submission_r = requests.get(url)
. .print(submission_r.status_code)
. .response_dict = submission_r.json()
. .
(5) . .submission_dict = {
. . . .'title': response_dict['title'],
. . . .'link': 'http://news.ycombinator.com/item?id=' + str(submission_id),
? . . . .'comments': response_dict.get('descendants', 0)
. . . .}
. .submission_dicts.append(submission_dict)
. .
? submission_dicts = sorted(submission_dicts, key=itemgetter('comments'),
. . . . . . . . . . . . . .reverse=True)
? for submission_dict in submission_dicts:
. .print("\nTitle:", submission_dict['title'])
. .print("Discussion link:", submission_dict['link'])
. .print("Comments:", submission_dict['comments'])
Сначала программа создает вызов API и выводит статус ответа (1) . Этот вызов API возвращает список идентификаторов 500 самых популярных статей на Hacker News на момент выдачи вызова. Текст ответа преобразуется в список Python (2), который сохраняется в переменной submission_ids. Идентификаторы будут использованы для построения набора словарей, каждый из которых содержит информацию об одной из текущих статей.
В точке (3) создается пустой список с именем submission_dicts для хранения словарей. Далее программа перебирает идентификаторы 30 самых популярных статей и выдает новый вызов API для каждой статьи, генерируя URL с текущим значением submission_id (4). Также выводится статус каждого запроса, чтобы мы могли проверить, успешно ли он был обработан.
В точке (5) создается словарь для текущей обрабатываемой статьи, в котором сохраняется заголовок статьи и ссылка на страницу с ее обсуждением. В точке ? сохраняется количество комментариев в словаре. Если статья еще не имеет комментариев, ключ 'descendants' отсутствует. Если вы не уверены, существует ли ключ в словаре, используйте метод dict.get(), который возвращает значение, связанное с ключом (если он существует), или значение, заданное вами (если ключ не существует), — 0 в данном примере. Наконец, словарь submission_dict присоединяется к списку submission_dicts.
Статьи Hacker News ранжируются по общей системе, основанной на нескольких факторах: сколько раз за статью голосовали, сколько комментариев она получила и давно ли была опубликована. Требуется отсортировать список словарей по количеству комментариев. Для этого мы используем функцию itemgetter() ? из модуля operator. Мы передаем этой функции ключ 'comments', а она извлекает значение, связанное с данным ключом, из каждого словаря в списке. Функция sorted() затем использует это значение для сортировки списка. Мы сортируем список в обратном порядке, чтобы публикации с наибольшим количеством комментариев оказались на первом месте.
После того как список будет отсортирован, мы перебираем элементы ? и выводим для каждой из самых популярных статей три атрибута: заголовок, ссылку на страницу обсуждения и текущее количество комментариев:
Status code: 200
200
200
200
..
Title: Firefox deactivates Flash by default
Discussion link: http://news.ycombinator.com/item?id=9883246
Comments: 231
Title: New Horizons: Nasa spacecraft speeds past Pluto
Discussion link: http://news.ycombinator.com/item?id=9884165
Comments: 142
Title: Iran Nuclear Deal Is Reached With World Powers
Discussion link: http://news.ycombinator.com/item?id=9884005
Comments: 141
Title: Match Group Buys PlentyOfFish for $575M
Discussion link: http://news.ycombinator.com/item?id=9884417
Comments: 75
Title: Our Nexus 4 devices are about to explode
Discussion link: http://news.ycombinator.com/item?id=9885625
Comments: 14
...
Аналогичный процесс применяется для обращения и анализа информации из любого API. С такими данными вы сможете построить визуализацию, показывающую, какие публикации вызывали наиболее активные обсуждения в последнее время.
Упражнения
17-1. Другие языки: измените вызов API в программе python_repos.py так, чтобы на диаграмме отображались самые популярные проекты на других языках. Попробуйте такие языки, как JavaScript, Ruby, C, Java, Perl, Haskell и Go.
17-2. Активные обсуждения: на основании данных из hn_submissions.py постройте столбцовую диаграмму самых активных обсуждений, проходящих на Hacker News. Высота каждого столбца должна соответствовать количеству комментариев к каждой статье. Метка столбца должна включать заголовок статьи, а сам столбец должен служить ссылкой на страницу обсуждения этой публикации.
17-3. Тестирование python_repos.py: в python_repos.py для проверки успешности вызова API выводится значение status_code. Напишите программу test_python_repos.py, которая использует модуль unittest для проверки того, что значение status_code равно 200. Придумайте другие условия, которые могут проверяться при тестировании, — например, что количество возвращаемых элементов совпадает с ожидаемым, а общее количество репозиториев превышает некоторый порог.
В этой главе вы узнали, как использовать API для написания программ, автоматически собирающих необходимые данные и использующих полученную информацию для создания визуализации. Мы использовали GitHub API для получения информации о самых популярных проектах Python на GitHub, а также в общих чертах рассмотрели API Hacker News. Вы узнали, как с помощью пакета requests автоматически выдать вызов API к GitHub и как обработать результаты этого вызова. Также были описаны некоторые средства конфигурации Pygal, позволяющие выполнить дополнительную настройку внешнего вида создаваемых диаграмм.
В последнем проекте мы используем Django для построения веб-приложения.