Настоящий бой | Реализовать высокопроизводительный краулер с помощью aiohttp и uvloop

Python рептилия Google Chrome
Настоящий бой | Реализовать высокопроизводительный краулер с помощью aiohttp и uvloop

asyncio был введен в стандартную библиотеку в Python 3.4 и добавил поддержку асинхронного ввода-вывода.Основываясь на цикле событий, asyncio может легко реализовать асинхронные операции ввода-вывода. Затем мы реализуем высокопроизводительный сканер с библиотекой на основе asyncio.

Готов к работе

Earth View from Google Earth— это плагин для Chrome, который автоматически загружает фоновое изображение из Google Планета Земля при открытии новой вкладки.

Earth View from Google Earth

Используя инструменты разработчика Chrome для наблюдения за сетевым запросом плагина, мы обнаружили, что плагин будет запрашивать такой адрес, какУууууууууууууууууууууууууууууууууууууууууууууууууууууФайл JSON содержит содержимое изображения Base64. Замечено, что диапазон идентификаторов изображения составляет примерно от 1000 до 8000. Наш сканер собирается просканировать эти изысканные фоновые изображения.

Реализовать основную логику

Поскольку целью сканирования является файл JSON, основная логика сканера становитсяСканировать JSON--> Извлечь изображение--> Сохранить изображение.

запросы — это широко используемая библиотека HTTP-запросов, но, поскольку все запросы запросов являются синхронными, мы используемaiohttpВместо этого эта библиотека асинхронных http-запросов.

async def fetch_image_by_id(item_id):
	url = f'https://www.gstatic.com/prettyearth/assets/data/v2/{item_id}.json'
        # 由于URL是https的,所以选择不验证SSL
	async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
		async with session.get(url) as response:
            # 获取后需要将JSON字符串转为对象
			try:
				json_obj = json.loads(await response.text())
			except json.decoder.JSONDecodeError as e:
				print(f'Download failed - {item_id}.jpg')
				return
            # 获取JSON中的图片内容字段,经过Base64解码成二进制内容
			image_str = json_obj['dataUri'].replace('data:image/jpeg;base64,', '')
			image_data = base64.b64decode(image_str)
			save_folder = dir_path = os.path.dirname(
				os.path.realpath(__file__)) + '/google_earth/'
			with open(f'{save_folder}{item_id}.jpg', 'wb') as f:
				f.write(image_data)
			print(f'Download complete - {item_id}.jpg')

aiohttp основан на asyncio, поэтому вам нужно использовать синтаксический сахар async/await при вызове.Вы можете видеть, что, поскольку aiohttp предоставляет контекст ClientSession, код использует async с синтаксическим сахаром.

Добавить параллельную логику

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

async def fetch_all_images():
    # 使用Semaphore限制最大并发数
	sem = asyncio.Semaphore(10)
	ids = [id for id in range(1000, 8000)]
	for current_id in ids:
		async with sem:
			await fetch_image_by_id(current_id)

Затем добавьте этот метод в цикл обработки событий asyncio.

event_loop = asyncio.get_event_loop()
future = asyncio.ensure_future(fetch_all_images())
results = event_loop.run_until_complete(future)

Ускоряйтесь с uvloop

uvloop основан на libuv, высокопроизводительной библиотеке асинхронного ввода-вывода, реализованной на языке C. uvloop используется для замены стандартного цикла обработки событий asyncio, что может еще больше ускорить асинхронные операции ввода-вывода.

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

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

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

爬取下来的Google Earth图片

Сравнение производительности

Чтобы проверить производительность aiohttp и uvloop, автор использует библиотеку request+concurrent для реализации многопроцессорной версии сканера, просматривая 20 идентификаторов соответственно, а потребление времени показано на рисунке.

Видно, что разница во времени составляет примерно 7. Можно сказать, что комбинация aiohttp+uvloop имеет подавляющее преимущество в сценариях с интенсивным вводом-выводом, таких как краулер. Я верю, что в ближайшем будущем библиотеки, основанные на асинхронности, избавят бесчисленное количество инженеров-краулеров от сверхурочной работы.

Отсканируйте код, чтобы пройти на личную кухню Python

拯救加班的你