Книга: Изучаем Python: программирование игр, визуализация данных, веб-приложения. 3-е изд. дополненное и переработанное
Назад: 16. Загрузка данных
Дальше: 18. Знакомство с Django

17. Работа с API

29062.png

 

В этой главе вы научитесь писать специализированные программы для создания визуализаций на основании загруженных ими данных. Ваша программа будет использовать программный интерфейс (application programming interface, API) веб-приложения для автоматического запроса конкретной информации с сайта (вместо целых страниц), на базе которой будет создаваться визуализация. Программы, написанные по такой схеме, всегда используют самые свежие данные для создания визуализации, поэтому даже при быстро изменяющихся данных полученная диаграмма будет оставаться актуальной.

Использование API веб-приложений

API веб-приложения представляет собой часть сайта, предназначенную для взаимодействия с программами, которые используют особым образом созданные URL для запроса информации. Подобные запросы называются вызовами API (API call). Запра­шиваемые данные возвращаются в удобном формате (например, JSON или CSV). Многие приложения, зависящие от внешних источников данных (такие как приложения, интегрирующиеся с сайтами социальных сетей), используют вызовы API.

Git и GitHub

Наша визуализация будет создана на базе информации с GitHub (https://github.com/) — сайта, организующего совместную работу программистов над проектами. С помощью API GitHub мы запросим информацию о проектах Python, а затем, используя Plotly, сгенерируем интерактивную визуализацию относительной популярности этих проектов.

Имя GitHub происходит от Git — распределенной системы управления версиями, которая позволяет программистам совместно трудиться над проектами. Пользователи Git управляют своим кодом, который является их вкладом в проект, чтобы изменения, вносимые одним человеком, не конфликтовали с изменениями, вносимыми другими людьми. Когда вы реализуете новую возможность в проекте, Git отслеживает изменения, внесенные в каждый файл. Если новый код работает успешно, то вы закрепляете (commit) внесенные изменения и Git записывает новое состояние проекта. Если же вы допустили ошибку и захотите отменить внесенные изменения, то Git позволяет легко вернуться к любому из предыдущих рабочих состояний. (Дополнительную информацию об управлении версиями с использованием Git см. в приложении Г.) Проекты GitHub хранятся в репозиториях (repositories), содержащих все ресурсы, связанные с проектом: код, информацию о других участниках, все проблемы или отчеты об ошибках и т.д.

Если проект нравится пользователям GitHub, то они могут отметить его звездочкой, чтобы продемонстрировать свою поддержку и следить за проектами, которые могут им пригодиться. В этой главе мы напишем программу для автоматического скачивания информации о проектах Python, отмеченных наибольшим количеством звезд на GitHub, а затем создадим информативную визуализацию таких проектов.

Запрос данных с помощью вызовов API

Программный интерфейс GitHub позволяет запрашивать разнообразную информацию с помощью вызовов 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, сортирует проекты по количеству присвоенных им звезд.

В следующем фрагменте приведены несколько начальных строк ответа:

{

❶     "total_count": 8961993,

❷     "incomplete_results": true,

❸     "items": [

     {

       "id": 54346799,

       "node_id": "MDEwOlJlcG9zaXRvcnk1NDM0Njc5OQ==",

       "name": "public-apis",

       "full_name": "public-apis/public-apis",

      --пропуск--

Вероятно, по виду ответа вы уже поняли, что этот URL не предназначен для обычных пользователей, поскольку ответ закодирован в формате, рассчитанном на машинную обработку. На момент написания книги на GitHub было найдено чуть менее девяти миллионов проектов Python . Значение "incomplete_results" равно true, а значит, у GitHub возникли проблемы с полной обработкой запроса . Алгоритмы GitHub ограничивают продолжительность выполнения каждого запроса, чтобы сохранить доступность API для всех пользователей. В данном случае алгоритмы нашли несколько самых популярных репозиториев Python, но не все; сейчас мы это исправим. Возвращаемые данные отображаются в списке "items", содержащем информацию о самых популярных проектах Python на GitHub .

Установка пакета requests

Пакет requests предоставляет удобные средства, позволяющие запрашивать информацию с сайтов из программ Python и анализировать полученные ответы. Для установки requests используется модуль pip:

$ python -m pip install --user requests

Если для запуска программ или установки пакетов вы используете python3 или другую команду, то проследите за тем, чтобы здесь использовалась та же команда:

$ python3 -m pip install --user requests

Обработка ответа API

Теперь мы напишем программу для автоматического вызова API и обработки результатов:

python_repos.py

import requests

 

# Создание вызова API и сохранение ответа.

❶ url = "https://api.github.com/search/repositories"

url += "?q=language:python+sort:stars+stars:>10000"

 

❷ headers = {"Accept": "application/vnd.github.v3+json"}

❸ r = requests.get(url, headers=headers)

❹ print(f"Status code: {r.status_code}")

 

# Преобразование объекта ответа в словарь.

❺ response_dict = r.json()

 

# Обработка результатов.

print(response_dict.keys())

Сначала мы импортируем модуль requests. Затем присваиваем URL вызова API переменной url . URL длинный, поэтому мы разбиваем его на две строки. Первая строка — это основная часть URL, а вторая — сам запрос. Мы добавили еще одно условие в строку запроса: stars:>10000, которое дает GitHub указание искать только репозитории Python, отмеченные более чем 10 000 звезд. Теперь GitHub сможет возвращать полный, последовательный набор результатов.

В настоящее время GitHub использует третью версию API, поэтому для вызова API определяются заголовки, которые явно требуют использовать эту версию API и возвращают результаты в формате JSON . После этого модуль requests задействуется для вызова API . Мы вызываем метод get() и передаем ему URL и заголовок, а объект ответа сохраняется в переменной r.

Объект ответа содержит атрибут status_code, в котором хранится признак успешного выполнения запроса. (Код 200 — признак успешного ответа.) Программа выводит значение status_code, чтобы вы могли убедиться в том, что вызов был обработан успешно . API должен возвращать информацию в формате JSON, поэтому в программе используется метод json() для преобразования информации в словарь Python . Полученный словарь сохраняется в переменной response_dict.

Наконец, программа выводит ключи словаря response_dict, и мы видим следующее:

Status code: 200

dict_keys(['total_count', 'incomplete_results', 'items'])

Код статуса 200 означает, что запрос был обработан успешно. Словарь ответа содержит всего три ключа: 'total_count', 'incomplete_results' и 'items'. Поговорим о том, как взаимодействовать с таким словарем.

Работа со словарем ответа

Итак, полученная при вызове API информация хранится в словаре, и мы можем заняться работой с данными. Для начала создадим сводку с обобщенными сведениями — это позволит убедиться в том, что вызов вернул ожидаемую информацию, и перейти к анализу интересующих данных:

python_repos.py

import requests

 

# Создание вызова API и сохранение ответа.

--пропуск--

 

# Преобразование объекта ответа в словарь.

response_dict = r.json()

❶ print(f"Total repositories: {response_dict['total_count']}")

print(f"Complete results: {not response_dict['incomplete_results']}")

 

# Анализ информации о репозиториях.

❷ repo_dicts = response_dict['items']

print(f"Repositories returned: {len(repo_dicts)}")

 

# Анализ первого репозитория.

❸ repo_dict = repo_dicts[0]

❹ pprint(f"\nKeys: {len(repo_dict)}")

❺ for key in sorted(repo_dict.keys()):

    print(key)

Рассмотрим словарь ответа, начиная со значения 'total_count', которое представляет собой общее количество репозиториев Python, возвращенных этим вызовом API . Мы также используем значение 'incomplete_results', позволяющее узнать, смог ли GitHub полностью обработать запрос. Вместо того чтобы выдавать это значение напрямую, мы выводим его противоположность: значение True будет означать, что мы получили полный набор результатов.

Значение, связанное с 'items', представляет собой список со словарями, каждый из которых содержит данные об одном репозитории Python. Этот список словарей сохраняется в repo_dicts . Затем программа выводит длину repo_dicts, чтобы пользователь видел, по какому количеству репозиториев имеется информация.

Чтобы дать нам первое представление об информации, возвращенной по каждому репозиторию, программа извлекает первый элемент из repo_dicts и сохраняет его в repo_dict . Затем программа выдает количество ключей в словаре — это значение определяет объем доступной информации . И наконец, выводятся все ключи словаря; по ним можно понять, какая информация добавлена в ответ .

Из сводки начинает вырисовываться более четкая картина полученных данных:

Status code: 200

❶ Total repositories: 248

❷ Complete results: True

Repositories returned: 30

 

❸ Keys: 78

allow_forking

archive_url

archived

--пропуск--

url

visiblity

watchers

watchers_count

На момент написания книги было найдено 248 репозиториев Python, отмеченных 10 000 звезд и более . Судя по результату, GitHub смог полностью обработать вызов API . В ответе GitHub вернул информацию о первых 30 репозиториях, которые соответствуют условиям нашего запроса. Если нужно больше репозиториев, то мы можем запросить дополнительные страницы данных.

API GitHub возвращает подробную информацию о каждом репозитории: в repo_dict содержится 78 ключей . Просмотр ключей дает представление о том, какие сведения о проекте можно извлечь. (Какую информацию можно получить через API, мы узнаем, либо прочитав документацию, либо проанализировав информацию в коде.)

Прочитаем значения некоторых ключей repo_dict:

python_repos.py

--пропуск--

# Анализ первого репозитория.

repo_dict = repo_dicts[0]

 

print("\nSelected information about first repository:")

❶ print(f"Name: {repo_dict['name']}")

❷ print(f"Owner: {repo_dict['owner']['login']}")

❸ print(f"Stars: {repo_dict['stargazers_count']}")

print(f"Repository: {repo_dict['html_url']}")

❹ print(f"Created: {repo_dict['created_at']}")

❺ print(f"Updated: {repo_dict['updated_at']}")

print(f"Description: {repo_dict['description']}")

В программе выводятся значения, связанные с некоторыми ключами словаря первого репозитория. Сначала выводится имя проекта . Владельца проекта представляет целый словарь, поэтому ключ owner используется для обращения к словарю, представляющему владельца, после чего ключ login используется для получения регистрационного имени владельца . Далее выводится количество звезд, заработанных проектом , и URL репозитория GitHub проекта. Затем выводится дата создания и последнего обновления репозитория . В завершение выводится описание репозитория; результат должен выглядеть примерно так:

Status code: 200

Total repositories: 248

Complete results: True

Repositories returned: 30

 

Selected information about first repository:

Name: public-apis

Owner: public-apis

Stars: 191493

Repository: https://github.com/public-apis/public-apis

Created: 2016-03-20T23:49:42Z

Updated: 2022-05-12T06:37:11Z

Description: A collective list of free APIs

Из вывода видно, что на момент написания книги самым «звездным» проектом Python на GitHub был проект public-apis, владельцем которого является одно­именная организация, и звезды этот проект получил от почти 200 тыс. пользователей GitHub. Мы видим URL репозитория проекта, дату создания (март 2016 года) и то, что проект недавно обновлялся. Наконец, из описания следует, что public-apis содержит список бесплатных API, которые могут заинтересовать программистов.

Сводка самых популярных репозиториев

При создании визуализации этих данных в диаграмму необходимо внести несколько репозиториев. Напишем цикл для вывода информации о каждом репозитории, возвращаемом вызовом API, чтобы всех их можно было добавить в визуализацию:

python_repos.py

--пропуск--

# Анализ информации о репозиториях.

repo_dicts = response_dict['items']

print(f"Repositories returned: {len(repo_dicts)}")

 

❶ print("\nSelected information about each repository:")

❷ for repo_dict in repo_dicts:

    print(f"\nName: {repo_dict['name']}")

    print(f"Owner: {repo_dict['owner']['login']}")

    print(f"Stars: {repo_dict['stargazers_count']}")

    print(f"Repository: {repo_dict['html_url']}")

    print(f"Description: {repo_dict['description']}")

Сначала выдается приветственное сообщение . Затем перебираются все словари в repo_dicts . Внутри цикла выводятся имя каждого проекта, его владелец, количество звезд, URL на GitHub и краткое описание проекта:

Status code: 200

Total repositories: 248

Complete results: True

Repositories returned: 30

 

Selected information about each repository:

 

Name: public-apis

Owner: public-apis

Stars: 191494

Repository: https://github.com/public-apis/public-apis

Description: A collective list of free APIs

 

Name: system-design-primer

Owner: donnemartin

Stars: 179952

Repository: https://github.com/donnemartin/system-design-primer

Description: Learn how to design large-scale systems. Prep for the system

  design interview.

  Includes Anki flashcards.

--пропуск--

 

Name: PayloadsAllTheThings

Owner: swisskyrepo

Stars: 37227

Repository: https://github.com/swisskyrepo/PayloadsAllTheThings

Description: A list of useful payloads and bypass for Web Application

  Security and Pentest/CTF

В этих результатах встречаются интересные проекты; возможно, вам стоит присмотреться к некоторым из них… Но не увлекайтесь, поскольку мы собираемся создать визуализацию, которая существенно упростит чтение результатов.

Проверка ограничений частоты обращений API

Многие API ограничивают частоту обращений (rate limits); иначе говоря, существует предел для количества запросов в определенный промежуток времени. Чтобы узнать, не приближаетесь ли вы к ограничениям GitHub, введите в брау­зере адрес https://api.github.com/rate_limit. Вы получите ответ, который выглядит примерно так:

{

  "resources": {

    --пропуск--

❶     "search": {

❷       "limit": 10,

❸       "remaining": 9,

❹       "reset": 1652338832,

      "used": 1,

      "resource": "search"

    }

  },

--пропуск--

В этих данных нас интересует частота обращений для поискового API . Видно, что предельная частота составляет 10 запросов в минуту и что на текущую минуту осталось еще 9 запросов . Значение "reset" представляет Unix-время, или эпохальное время (epoch time) (количество секунд, прошедших с полуночи 1 января 1970 года), когда произойдет сброс квоты . При достижении предельного количества обращений вы получите короткий ответ, уведомляющий об этом. Тогда просто подождите, пока квота не будет сброшена.

ПРИМЕЧАНИЕ

Многие API требуют регистрации и получения API-ключа (токена доступа) для совершения API-вызовов. На момент написания книги для GitHub такого требования не было, но если вы получите токен доступа, то предельная частота обращений для ваших программ значительно увеличится.

Визуализация репозиториев с помощью Plotly

Теперь, имея данные о проектах Python, мы создадим визуализацию, демонстрирующую их относительную популярность в GitHub, — интерактивную столбцовую диаграмму: высота каждого столбца будет представлять количество звезд, полученных проектом. Щелчок на столбце будет открывать главную страницу проекта на GitHub.

Сохраните копию программы, над которой вы работаете, под именем python_repos_visual.py, а затем приведите ее к следующему виду:

python_repos_visual.py

import requests

import plotly.express as px

 

# Создание вызова API и сохранение ответа.

url = "https://api.github.com/search/repositories"

url += "?q=language:python+sort:stars+stars:>10000"

 

headers = {"Accept": "application/vnd.github.v3+json"}

r = requests.get(url, headers=headers)

print(f"Status code: {r.status_code}")

 

# Обработка результатов.

response_dict = r.json()

print(f"Complete results: {not response_dict['incomplete_results']}")

 

# Обработка информации о репозитории.

repo_dicts = response_dict['items']

❸ repo_names, stars = [], []

for repo_dict in repo_dicts:

    repo_names.append(repo_dict['name'])

    stars.append(repo_dict['stargazers_count'])

 

# Создание визуализации.

fig = px.bar(x=repo_names, y=stars)

fig.show()

Мы импортируем функционал Plotly Express, а затем выполняем вызов API, как и ранее. Затем выводится статус ответа на вызов API, чтобы мы сразу узнали о возможной проблеме с вызовом API . Обрабатывая общие результаты, интерпретатор выдает сообщение, подтверждающее, что мы получили полный набор результатов . Остальные вызовы функции print() удалены, поскольку фаза исследования данных осталась позади; мы знаем, что получены именно те данные, которые нам нужны.

Затем создаются два пустых списка для хранения данных, добавляемых в диаграмму. Нам понадобятся имя каждого проекта для пометки столбцов (repo_names) и количество звезд, определяющее их высоту (stars). В цикле имя каждого проекта и количество звезд присоединяются к соответствующему списку.

Мы создаем первичную визуализацию, используя всего пару строк кода в соответствии с философией Plotly Express, согласно которой вы должны иметь возможность быстро увидеть диаграмму, прежде чем дорабатывать ее внешний вид. Здесь мы используем функцию px.bar() для создания диаграммы. В качестве аргумента x передается список repo_names, а в качестве аргумента y — список stars.

Полученная диаграмма изображена на рис. 17.1. Мы видим, что несколько первых проектов существенно популярнее остальных, но все они занимают важное место в экосистеме Python.

17_01.tif 

Рис. 17.1. Проекты Python на GitHub, имеющие наибольшее количество звезд

Форматирование диаграммы

С помощью Plotly доступно множество способов форматирования и настройки диаграмм после того, как все данные отображены на них корректно. Мы внесем некоторые изменения в первоначальный вызов функции px.bar(), а затем слегка изменим объект fig после его создания.

Форматирование диаграммы мы начнем с добавления заголовка и меток к каждой оси:

python_repos_visual.py

--пропуск--

# Создание визуализации.

title = "Most-Starred Python Projects on GitHub"

labels = {'x': 'Repository', 'y': 'Stars'}

fig = px.bar(x=repo_names, y=stars, title=title, labels=labels)

 

❶ fig.update_layout(title_font_size=28, xaxis_title_font_size=20,

        yaxis_title_font_size=20)

 

fig.show()

Сначала мы добавляем заголовок диаграммы и метки к каждой оси, как делали это в главах 15 и 16. Затем используем метод fig.update_layout() для изменения некоторых элементов диаграммы . Согласно рекомендациям Plotly, слова в именах параметров диаграммы соединяются с помощью подчеркивания. По мере знакомства с документацией Plotly вы начнете видеть закономерности в том, как называются и настраиваются различные элементы диаграммы. В нашем примере мы установили размер шрифта заголовка равным 28, а размер шрифта меток осей — равным 20. Результат показан на рис. 17.2.

17_02.tif 

Рис. 17.2. Диаграмма с заголовком и метками осей

Добавление подсказок

В Plotly при наведении указателя мыши на отдельный столбец выводится информация, которую он представляет. В текущей версии экранная подсказка (tooltip) отображает количество звезд проекта. Создадим нестандартную подсказку, которая будет выводить описание каждого проекта, а также его владельца.

Чтобы сгенерировать подсказки, необходимо извлечь дополнительные данные:

python_repos_visual.py

--пропуск--

# Обработка результатов.

repo_dicts = response_dict['items']

❶ repo_names, stars, hover_texts = [], [], []

for repo_dict in repo_dicts:

    repo_names.append(repo_dict['name'])

    stars.append(repo_dict['stargazers_count'])

 

    # Создание всплывающих текстов.

❷     owner = repo_dict['owner']['login']

    description = repo_dict['description']

❸     hover_text = f"{owner}<br />{description}"

    hover_texts.append(hover_text)

 

# Создание визуализации.

title = "Most-Starred Python Projects on GitHub"

labels = {'x': 'Repository', 'y': 'Stars'}

❹ fig = px.bar(x=repo_names, y=stars, title=title, labels=labels,

        hover_name=hover_texts)

fig.update_layout(title_font_size=28, xaxis_title_font_size=20,

        yaxis_title_font_size=20)

fig.show()

Сначала определяется новый пустой список hover_texts для хранения текста, который должен выводиться для каждого проекта . В цикле, где происходит обработка данных, мы извлекаем данные о владельце и описание для каждого проекта . Plotly позволяет использовать разметку HTML в текстовых элементах, поэтому мы сгенерируем для метки текст с разрывом строки (<br />) между именем владельца проекта и описанием . Затем метка сохраняется в списке hover_texts.

В вызов функции px.bar() мы добавляем аргумент hover_name и передаем ему значение переменной hover_texts . С помощью такого же подхода мы настраивали метки для точек на карте активности землетрясений. При создании каждого столбца Plotly извлекает метки из списка и выводит их только в тот момент, когда пользователь наводит указатель мыши на столбец. На рис. 17.3 показана одна из таких пользовательских всплывающих подсказок.

17_03.tif 

Рис. 17.3. При наведении указателя мыши на столбец выводятся информация о владельце и описание проекта

Добавление ссылок в диаграмму

Plotly позволяет использовать HTML-элементы в текстовых элементах, поэтому диаграмму можно легко дополнить ссылками. Используем метки оси X для того, чтобы пользователь мог открыть главную страницу проекта на GitHub. Необходимо извлечь URL из данных и использовать их при генерировании меток для оси X.

python_repos_visual.py

--пропуск--

# Обработка результатов.

repo_dicts = response_dict['items']

❶ repo_links, stars, hover_texts = [], [], []

for repo_dict in repo_dicts:

    # Создание ссылок из названий репозиториев.

    repo_name = repo_dict['name']

❷     repo_url = repo_dict['html_url']

    repo_link = f"<a href='{repo_url}'>{repo_name}</a>"

    repo_links.append(repo_link)

 

    stars.append(repo_dict['stargazers_count'])

    --пропуск--

 

# Создание визуализации.

title = "Most-Starred Python Projects on GitHub"

labels = {'x': 'Repository', 'y': 'Stars'}

fig = px.bar(x=repo_links, y=stars, title=title, labels=labels,

        hover_name=hover_texts)

 

fig.update_layout(title_font_size=28, xaxis_title_font_size=20,

        yaxis_title_font_size=20)

 

fig.show()

Список, создаваемый на базе repo_names, переименован в repo_links, чтобы он более точно отражал свойства информации, объединяемой для диаграммы . Затем мы извлекаем URL проекта из repo_dict и присваиваем его временной переменной repo_url . Далее генерируется ссылка на проект . Для этого используется HTML-тег привязки вида <a href='URL'>текст ссылки</a>. Затем ссылка присоединяется к списку repo_links.

Вызывая функцию px.bar(), мы используем список repo_links для получения значений оси X диаграммы. Результат выглядит так же, как прежде, но теперь пользователь может щелкнуть на любом из имен проектов в нижней части диаграммы, чтобы посетить главную страницу этого проекта на GitHub. Нам удалось создать интерактивную, информативную визуализацию данных, полученных через API!

Настройка цвета маркеров

Когда диаграмма создана, практически любой ее аспект можно настроить с помощью метода update. Ранее мы использовали метод update_layout(). Другой метод, update_traces(), применяется для настройки данных, представленных на диаграмме.

Изменим цвет столбцов на темно-синий, чуть прозрачный:

--пропуск--

fig.update_layout(title_font_size=28, xaxis_title_font_size=20,

        yaxis_title_font_size=20)

 

fig.update_traces(marker_color='SteelBlue', marker_opacity=0.6)

 

fig.show()

В контексте Plotly под трассировкой (trace) понимается набор данных на графике. Метод update_traces() принимает несколько аргументов; те из них, которые начинаются со слова marker_, форматируют маркеры на диаграмме. В примере мы задаем для всех маркеров цвет 'SteelBlue' (можно использовать любые имена из спецификации CSS). Мы также настроили непрозрачность каждого маркера и сделали ее равной 0.6. При непрозрачности 1.0 маркер будет полностью непрозрачным, а при непрозрачности 0 — полностью невидимым.

Дополнительная информация о Plotly и GitHub API

Документация Plotly объемна и хорошо структурирована, однако бывает трудно понять, с чего начать чтение. Прочтите статью Plotly Express in Python на сайте https://plotly.com/python/plotly-express. В ней приведен обзор всех диаграмм, которые вы можете создать с помощью Plotly Express, а также ссылки на более подробные публикации о каждом виде диаграммы.

Если вы хотите основательно разобраться в настройках диаграмм Plotly, то благодаря статье Styling Plotly Express Figures in Python расширите ваши познания, полученные в главах 15–17. Вы найдете ее по адресу https://plotly.com/python/styling-plotly-express.

За дополнительной информацией о GitHub API обращайтесь к документации, размещенной на https://docs.github.com/en/rest. Из нее вы узнаете, как извлекать разнообразную информацию с сайта GitHub. Чтобы расширить знания, полученные в этом проекте, воспользуйтесь системой поиска на боковой панели сайта с документацией. Если у вас уже есть учетная запись GitHub, то вы можете работать как со своими данными, так и с общедоступными из репозиториев других пользователей.

API Hacker News

Поговорим о том, как использовать вызовы API для других сайтов, и для этого обратимся к сайту Hacker News (http://news.ycombinator.com/). На нем пользователи делятся друг с другом статьями, посвященными программированию и технологиям, а также активно обсуждают материалы этих статей. API сайта Hacker News предоставляет доступ ко всем статьям и комментариям на сайте, а его использование не требует регистрации с получением ключа.

Следующий вызов возвращает информацию о текущей самой популярной статье (на момент написания книги):

https://hacker-news.firebaseio.com/v0/item/31353677.json

Если ввести этот URL в браузере, то вы увидите, что текст на странице заключен в фигурные скобки; это означает, что перед вами словарь. Но в ответе трудно разобраться без дополнительного форматирования. Обработаем URL методом json.dumps(), как мы делали в проекте с землетрясениями из главы 16, чтобы было удобнее изучать возвращенную информацию:

hn_article.py

import requests

import json

 

# Вызов API и сохранение ответа.

url = "https://hacker-news.firebaseio.com/v0/item/31353677.json"

r = requests.get(url)

print(f"Status code: {r.status_code}")

 

# Анализ структуры данных.

response_dict = r.json()

response_string = json.dumps(response_dict, indent=4)

❶ print(response_string)

В этой программе все должно быть вам знакомо, поскольку все эти элементы использовались в двух предшествующих главах. Основное отличие заключается в том, что отформатированную строку ответа мы можем вывести , а не записывать в файл, так как вывод не очень длинный.

Ответ представляет собой словарь с информацией о статье с идентификатором 31353677:

{

    "by": "sohkamyung",

❶     "descendants": 302,

    "id": 31353677,

❷     "kids": [

        31354987,

        31354235,

        --пропуск--

    ],

    "score": 785,

    "time": 1652361401,

❸     "title": "Astronomers reveal first image of the black hole

        at the heart of our galaxy",

    "type": "story",

❹     "url": "https://public.nrao.edu/news/.../"

}

В словаре хранится ряд ключей, которые могут нам пригодиться. Ключ 'descendants' содержит количество комментариев, полученных статьей . Ключ 'kids' предоставляет идентификаторы всех комментариев, сделанных непосредственно в ответ на эту статью . У каждого из этих комментариев могут быть дочерние комментарии, так что количество потомков у статьи может превышать количество дочерних комментариев. Кроме того, в данных видны заголовок обсуждаемой статьи и ее URL .

Следующий URL возвращает простой список всех идентификаторов текущих популярных статей на сайте Hacker News:

https://hacker-news.firebaseio.com/v0/topstories.json

С помощью этого вызова можно узнать, какие статьи находятся на главной странице, а затем сгенерировать серию вызовов API, аналогичных приведенному выше. Это позволит нам вывести сводку всех статей, находящихся на главной странице Hacker News в данный момент:

hn_submissions.py

from operator import itemgetter

 

import requests

 

# Создание вызова API и сохранение ответа.

❶ url = "https://hacker-news.firebaseio.com/v0/topstories.json"

r = requests.get(url)

print(f"Status code: {r.status_code}")

 

# Обработка информации о каждой статье.

❷ submission_ids = r.json()

❸ submission_dicts = []

for submission_id in submission_ids[:30]:

    # Создание отдельного вызова API для каждой статьи.

❹     url = f"https://hacker-news.firebaseio.com/v0/item/{submission_id}.json"

    r = requests.get(url)

    print(f"id: {submission_id}\tstatus: {r.status_code}")

    response_dict = r.json()

 

    # Создание словаря для каждой статьи.

❺     submission_dict = {

        'title': response_dict['title'],

        'hn_link': f"http://news.ycombinator.com/item?id={submission_id}",

        'comments': response_dict['descendants'],

    }

❻     submission_dicts.append(submission_dict)

 

❼ submission_dicts = sorted(submission_dicts, key=itemgetter('comments'),

                             reverse=True)

 

❽ for submission_dict in submission_dicts:

    print(f"\nTitle: {submission_dict['title']}")

    print(f"Discussion link: {submission_dict['hn_link']}")

    print(f"Comments: {submission_dict['comments']}")

Сначала программа создает вызов API и выводит статус ответа . Этот вызов возвращает список идентификаторов 500 самых популярных статей на Hacker News на момент выдачи вызова. Текст ответа преобразуется в список Python , который сохраняется в переменной submission_ids. Идентификаторы будут использованы для создания набора словарей, каждый из которых содержит информацию об одной из текущих статей.

Далее создается пустой список submission_dicts для хранения словарей . Затем программа перебирает идентификаторы 30 самых популярных статей и выдает новый вызов API для каждой статьи, генерируя URL с текущим значением submission_id . Выводится и статус каждого запроса, чтобы мы могли проверить, успешно ли он был обработан.

Далее создается словарь для текущей статьи , в котором сохраняются заголовок статьи, ссылка на страницу с ее обсуждением и количество комментариев к статье. Затем каждый элемент submission_dict присоединяется к списку submission_dicts .

Статьи Hacker News ранжируются по общей системе, основанной на нескольких факторах: сколько раз за статью голосовали, сколько комментариев она получила и давно ли была опубликована. Требуется отсортировать список словарей по количеству комментариев. Для этого мы используем функцию itemgetter() из модуля operator. Мы передаем ей ключ 'comments', а она извлекает связанное с ним значение из каждого словаря в списке. Затем функция sorted() использует это значение для сортировки списка. Мы сортируем список в обратном порядке, чтобы публикации с наибольшим количеством комментариев оказались на первом месте.

После того как список будет отсортирован, мы перебираем элементы и выводим для каждой из самых популярных статей три атрибута: заголовок, ссылку на страницу обсуждения и текущее количество комментариев:

Status code: 200

id: 31390506    status: 200

id: 31389893    status: 200

id: 31390742    status: 200

--пропуск--

 

Title: Fly.io: The reclaimer of Heroku's magic

Discussion link: https://news.ycombinator.com/item?id=31390506

Comments: 134

 

Title: The weird Hewlett Packard FreeDOS option

Discussion link: https://news.ycombinator.com/item?id=31389893

Comments: 64

 

Title: Modern JavaScript Tutorial

Discussion link: https://news.ycombinator.com/item?id=31390742

Comments: 20

--пропуск--

Аналогичный процесс применяется для обращения и анализа информации из любого API. С такими данными вы сможете создать визуализацию, показывающую, какие публикации вызывали наиболее активные обсуждения за последнее время. Этот метод также лежит в основе приложений, предоставляющих специализированные средства чтения таких сайтов, как Hacker News. Чтобы узнать больше об информации, доступной через Hacker News API, посетите страницу документации по адресу https://github.com/HackerNews/API/.

ПРИМЕЧАНИЕ

Сотрудники компании Hacker News иногда позволяют определенным компаниям публиковать специальные посты о найме, комментарии к которым отключены. Если вы запустите программу hn_submissions.py в момент, когда один из таких постов опубликован, то увидите ошибку KeyError. При возникновении подобной проблемы вы можете поместить код submission_dict в блок try-except и пропустить эти посты.

Упражнения

17.1. Другие языки. Измените вызов API в программе python_repos.py так, чтобы на диаграмме отображались самые популярные проекты на других языках. Попробуйте такие языки, как JavaScript, Ruby, C, Java, Perl, Haskell и Go.

17.2. Активные обсуждения. На основании данных из файла hn_submissions.py создайте столбцовую диаграмму самых активных обсуждений, проходящих на Hacker News. Высота каждого столбца должна соответствовать количеству комментариев к каждой статье. Метка столбца должна содержать заголовок статьи и служить ссылкой на страницу обсуждения этой публикации. Если вы получаете ошибку KeyError при создании диаграммы, то используйте блок try-except, чтобы пропустить рекламные сообщения.

17.3. Тестирование программы python_repos.py. В python_repos.py для проверки успешности вызова API выводится значение status_code. Напишите программу test_python_repos.py, которая использует модуль pytest для проверки того, что значение status_code равно 200. Придумайте другие условия, которые могут проверяться при тестировании, — например, что количество возвращаемых элементов совпадает с ожидаемым, а общее количество репозиториев превышает некий порог.

17.4. Дальнейшие исследования. Ознакомьтесь с документацией Plotly и GitHub API (или Hacker News API). Используйте полученную информацию для настройки форматирования уже созданных диаграмм или загрузите другие данные и создайте собственные визуализации. Если вам интересно изучить другие API, упомянутые в репозитории GitHub, то посетите страницу https://github.com/public-apis.

Резюме

В этой главе вы узнали, как задействовать API для написания программ, которые автоматически собирают необходимые данные и используют их для создания визуализации. Вы использовали GitHub API для получения информации о самых популярных проектах Python на GitHub, а также в общих чертах рассмотрели API Hacker News. Вы узнали, как использовать пакет requests для автоматического вызова API и как обработать его результаты. Кроме того, в главе были описаны некоторые средства конфигурации Plotly, позволяющие выполнить дополнительную настройку внешнего вида создаваемых диаграмм.

В следующей главе мы рассмотрим работу с Django для создания веб-приложения, которое станет последним проектом в этой книге.

Назад: 16. Загрузка данных
Дальше: 18. Знакомство с Django