Рус Eng Cn Перевести страницу на:  
Please select your language to translate the article


You can just close the window to don't translate
Библиотека
ваш профиль

Вернуться к содержанию

Программные системы и вычислительные методы
Правильная ссылка на статью:

Практическое применение асинхронного программирования на языке Python при помощи пакета Asyncio

Савостин Петр Алексеевич

бакалавр, кафедра Алгоритмических Языков, Московский Государственний Университет им. Ломоносова

117437, Россия, г. Москва, ул. Ленинские Горы, 1, стр. 52

Savostin Petr Alekseevich

Bachelor's Degree, Department of Algorithmic Languages, Lomonosov Moscow State University 

117437, Russia, g. Moscow, ul. Leninskie Gory, 1, str. 52

petersavostin@gmail.com
Ефремова Наталья Эрнестовна

кандидат физико-математических наук

ассистент, кафедра Алгоритмических языков, Московский Государственный Университет имени М.В. Ломоносова

119234, Россия, Московская область, г. Москва, ул. Ленинские Горы, 1, стр. 52, ауд. 707

Efremova Natalya Ernestovna

PhD in Physics and Mathematics

Assistant, Department of Algorithmic Languages, Lomonosov Moscow State University

119234, Russia, Moskovskaya oblast', g. Moscow, ul. Leninskie Gory, 1, str. 52, aud. 707

nvasil@list.ru

DOI:

10.7256/2454-0714.2018.2.25851

Дата направления статьи в редакцию:

27-03-2018


Дата публикации:

13-06-2018


Аннотация: Предметом исследования является изучение основных принципов асинхронного программирования с помощью пакета Asyncio и их применение для решения прикладных задач на языке Python. Поскольку в интерпретаторе языка Python используется способ синхронизации потоков Global Interpreter Lock, ограничивающий возможность распараллеливания программ на данном языке и, как следствие, не позволяет достичь наибольшей эффективности, использование технологий асинхронного программирования позволяет значительно увеличить скорость работы программ на данном языке, обходя упомянутые ограничения. Вышеописанный подход к созданию программ применяется в решении многих задач, например: при создании веб-сервера, клиент-серверного приложения, при извлечении данных с информационного ресурса веб-краулером. Данная работа посвящена объяснению основных принципов работы с пакетом Asyncio на языке Python. Поскольку русскоязычной литературы по данному пакету зачастую не хватает для того, чтобы понять основы асинхронного программирования в языке Python, в этой статье приводятся примеры использования данной технологии с пояснениями.


Ключевые слова:

язык программирования python, библиотека asyncio, асинхронное программирование, веб-краулинг, парсинг, сопрограммы, скрапинг, GIL, параллельные вычисления, извлечение данных

Abstract: The subject of the study is the study of the basic principles of asynchronous programming with the Asyncio package and their application for solving applied problems in Python. Since the Python interpreter uses the Global Interpreter Lock synchronization method, which limits the ability to parallelize programs in the given language and, as a result, does not allow achieving the greatest efficiency, the use of asynchronous programming technologies allows to significantly increase the speed of programs in this language, avoiding the mentioned limitations. The above described approach to creating programs is used in many tasks, for example: when creating a web server, client-server application, when extracting data from a resource by a web crawler. This paper is devoted to explaining the basic principles of working with the packageAsyncio in Python. Since the Russian-language literature on this package is often not enough to understand the basics of asynchronous programming in Python, this article gives examples of the use of this technology with explanations.


Keywords:

python programming language, asyncio library, asynchronous programming, web-crawling, parsing, couroutines, scraping, GIL, parallel computing, data extraction

Практическое применение асинхронного программирования на языке Python при помощи пакета Asyncio

Введение

Сейчас высокоуровневый язык программирования Python [3] является одним из самых востребованных языков в сфере информационных технологий. На нем реализованы многие проекты, связанные с обработкой текстов на естественном языке, анализом больших данных, машинным обучением и пр. [1]. Основное преимущество данного языка заключается в том, что он достаточно прост в использовании. Помимо этого, для языка Python доступно множество библиотек и пакетов [2], которые существенно облегчают и ускоряют разработку программ.

Язык программирования Python стремительно развивается, и с выходом новых версий порой невозможно найти литературу, описывающую новые возможности языка. Например, в Python версии 3.4 появился пакет asyncio [2], предназначенный для организации асинхронного программирования с использованием цикла событий сопрограмм; подробное описание принципов работы с данным пакетом на русском языке пока отсутствует.

Асинхронное программирование в Python

В интерпретаторе Python используется Global Interpreter Lock (GIL), который накладывет ограничение на потоки, а именно запрещает использование несколько процессоров одновременно одной программой [5]. GIL – это способ синхронизации потоков, одновременно обращающихся к одному и тому же участку памяти: когда один процесс захватывает его, GIL блокирует остальные процессы [6]. Поэтому для ускорения работы последовательной программы вместо параллельного приходится использовать асинхронное программирование.

Концепция асинхронного программирования подразумевает явное установление особых точек при выполнении вычислений (процессов). Эти точки позволяют отдельным вычислениям избегать ожидания завершения всех остальных вычислений, как это происходит в случае последовательного программирования [4].

Основное отличие асинхронной программы от последовательной заключается в том, что при асинхронных вычислениях порядок выполнения операций может отличаться от порядка их следования в программе [4].

Ранее, для организации асинхронного программирования в языке Python использовались пакеты gevent.

Асинхронное программирование с использованием библиотеки asyncio основано на следующих понятиях:

· Цикл событий (event loop) – планировщик задач, который управляет их выполнением.

· Сопрограмма (coroutine) – компонент программы, решающий задачу, который поддерживает множество входных точек (а не одну, как подпрограмма), остановку и продолжение выполнения с сохранением определённого положения [2]. Сопрограмма запускается только в цикле событий и должна содержать в себе ключевое слово await, после которого управление из сопрограммы передается обратно в цикл событий.

· Фьючер (future) – объект, который хранит в себе состояние выполнения задачи:

· ожидание (pending);

· выполнение (running);

· завершение (done);

· исключение (exception).

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

Последовательный код

Асинхронный код

# Функция ждет секунду и печатает сообщение

def hello_world():

sleep(1.0)

print("Hello World!")

hello_world()

import asyncio

# Функция ждет секунду и печатает сообщение

async def hello_world():

await asyncio.sleep(1.0)

print("Hello World!")

loop = asyncio.get_event_loop()

loop.run_until_complete(hello_world())

loop.close()

Между двумя программами есть некоторые различия. При объявлении функции, которые должны будут вызываться асинхронно используется ключевое слово async, которое. Ключевое слово await обозначает, что в этой точке сопрограмма передает управление циклу событий, при этом начинает выполнять команду, стоящую за ней. Переменная loop представляет собой объект цикла событий (планировщика), чтобы выполнить функцию hello_world() асинхронно, нужно воспользоваться функцией run_until_complete. В конце программы цикл событий завершается командой close().

Отметим, что основным удобством использования библиотеки asyncio является то, что код, написанный с ее помощью, почти не отличается от последовательного кода.

Использование asyncio при извлечении данных с web-страниц

Библиотека asyncio была использована нами при создании программы, осуществляющей:

· поиск существующих URL – единых указателей ресурса (обычно такая программа называется краулером);

· извлечение данных с найденных html-страниц.

При написании краулера необходимо было организовать

Покажем, каким образом был организован обход html-страниц с помощью библиотеки asyncio. Для этого опишем алгоритм работы простейшего краулера и приведем фрагмент программы, реализующей его.

Алгоритм работы краулера заключается в следующем:

1. Вводится стартовый URL страницы.

2. Программа посылает запрос на сервер по данному URL.

3. Программа получает ответ от сервера в виде html-страницы.

4. Из полученной страницы извлекаются ссылки на другие страницы.

5. Переходим на шаг 2.

Процесс заканчивается, когда достигается заданная глубина обхода.

Ниже приведен код для последовательной и асинхронной версии реализации краулера. На вход программам подается стартовый URL и глубина обхода (depth), на выходе получаем список обойденных URL (url_list).

Последовательный код

Асинхронный код

Импортирование необходимых пакетов.

Urllib для создания запросов на сервер

Bs4 для парсинга страниц

Импортирование необходимых пакетов.

Asyncio для асинхронного программирования

Aiohttp для асинхронных запросов

Bs4 для парсинга страниц

import urllib.request

from bs4 import BeautifulSoup

from urllib.parse import urljoin

import asyncio

import aiohttp

from bs4 import BeautifulSoup

from urllib.parse import urljoin

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

depth глубина обхода

url_list хранит в себе список адресов для обхода

start_url = 'http://www.aclweb.org/anthology/'

depth = 2

url_list = [start_url]

start_url = 'http://www.aclweb.org/anthology/'

depth = 1

url_list = [start_url]

parse_for_links функция, которая ищет ссылки в HTML коде страницы

def parse_for_links(url, text):

soup = BeautifulSoup(text, "html.parser")

tags = soup.findAll('a', href=True)

return [urljoin(url, tag['href']) for tag in tags]

async def fetch_async(url):

def parse_for_links(url, text):

soup = BeautifulSoup(text, "html.parser")

tags = soup.findAll('a', href=True)

return [urljoin(url, tag['href']) for tag in tags]

get_links функция, которая делает запрос по данному URL и ищет ссылки в коде страницы

def get_links(url):

try:

req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})

req = urllib.request.urlopen(req)

req = map(lambda x: x.decode('utf-8'), req)

return parse_for_links(url, ''.join(list(req)))

except:

return []

try:

async with aiohttp.ClientSession() as session:

async with session.get(url, timeout=15) as resp:

assert resp.status == 200

global url_list

url_list += parse_for_links(url, await resp.text())

except:

print('Error while downloading: ', url)

async def wrap_tasks(urls):

tasks = []

for url in urls:

tasks.append(asyncio.ensure_future(fetch_async(url)))

await asyncio.wait(tasks)

for i in range(depth):

print('Depth: ', depth)

for url in url_list:

url_list += get_links(url)

print(url_list)

for i in range(depth):

print('Depth: ', depth)

ioloop = asyncio.get_event_loop() ioloop.run_until_complete(wrap_tasks(url_list))

print(url_list)

Как видно есть несколько отличий между последовательным и асинхронным кодом:

· сопрограмма fetch_async посылает асинхронный запрос на сервер, в то время, как запрос обрабатывается, передает управление циклу событий и ждет ответа от сервера.

· wrap_tasks – создает список фьючерсов из сопрограмм, которые будут выполнятся в цикле событий

Заключение

Мы рассмотрели примеры создания программ с использованием технологий асинхронного программирования на языке программирования Python . Как видно из приведенных программ, пакет asyncio является удобным в использовании: он позволяет с минимальным количеством затрат при переписывании последовательной версии программы в асинхронную, получить более эффективную программу.

Показано, что применение библиотеки позволяет ускорить работу программы за счет разбиения решаемой задачи на несколько подзадач, которые могут выполняться асинхронно.

Библиография
1. Applications written in Python [Электронный ресурс]. – Электрон. дан. – URL: https://wiki.python.org/moin/Applications (дата обращения: 25.03.2018).
2. Asyncio documentation [Электронный ресурс]. – Электрон. дан. – URL: http://asyncio.readthedocs.io (дата обращения: 25.03.2018).
3. Python [Электронный ресурс]. – Электрон. дан. – URL: http://www.python.org/ (дата обращения: 08.02.2018).
4. Журнал Tproger [Электронный ресурс]. – Электрон. дан. – URL: https://tproger.ru/translations/programming-concepts-concurrency/ (дата обращения: 25.03.2018)
5. Дональд Кнут. Искусство программирования, том 1. Основные алгоритмы = The Art of Computer Programming, vol.1. Fundamental Algorithms. – 3-е изд. – М.: «Вильямс», 2006. – С. 720. – ISBN 0-201-89683-4.Раздел 1.4.2: Сопрограммы, стр. 229-236.
6. Саммерфилд М., Программирование на Python 3. Подробное руководство, 2009.
References
1. Applications written in Python [Elektronnyi resurs]. – Elektron. dan. – URL: https://wiki.python.org/moin/Applications (data obrashcheniya: 25.03.2018).
2. Asyncio documentation [Elektronnyi resurs]. – Elektron. dan. – URL: http://asyncio.readthedocs.io (data obrashcheniya: 25.03.2018).
3. Python [Elektronnyi resurs]. – Elektron. dan. – URL: http://www.python.org/ (data obrashcheniya: 08.02.2018).
4. Zhurnal Tproger [Elektronnyi resurs]. – Elektron. dan. – URL: https://tproger.ru/translations/programming-concepts-concurrency/ (data obrashcheniya: 25.03.2018)
5. Donal'd Knut. Iskusstvo programmirovaniya, tom 1. Osnovnye algoritmy = The Art of Computer Programming, vol.1. Fundamental Algorithms. – 3-e izd. – M.: «Vil'yams», 2006. – S. 720. – ISBN 0-201-89683-4.Razdel 1.4.2: Soprogrammy, str. 229-236.
6. Sammerfild M., Programmirovanie na Python 3. Podrobnoe rukovodstvo, 2009.