TypeError: объект list_iterator не может быть асинхронным - PullRequest
0 голосов
/ 09 сентября 2018

У меня есть простой веб-браузер python3, который работал синхронно. Я хотел сделать его асинхронным, поэтому немного его изменил. Но программа не выполняет итерацию по распакованному списку [(,), (,), (,), ...] даже после преобразования его в преобразование в итеративный список с помощью iter (). Говорит TypeError: 'list_iterator' object is not async iterable.

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

Моей целью было асинхронное получение ссылок для ускорения процесса. Есть ли способ решить эту проблему?


Код:

import requests as req
from bs4 import BeautifulSoup as bs
import os
import asyncio
from aiostream import stream, pipe

myList = []

def get_myList():
    """
    Append values to myList in the format [(,), (,), (,), ...]
    """

async def download(link, title):

    # Download a page
    try:
        page = await req.get(link)
        # Process with BeautifulSoup
        pass
    except:
        pass

async def main():
    get_myList()

    min_iterList = iter(myList[:])

    stream.starmap(min_iterList, download, ordered=True, task_limit=10)

if __name__=="__main__":
    freeze_support()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()

Ошибка:

Traceback (most recent call last):
  File "async_update_code.py", line 253, in <module>
    loop.run_until_complete(main())
  File "/usr/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
    return future.result()
  File "async_update_code.py", line 207, in main
    stream.starmap(min_iterList, download, ordered=True, task_limit=10)
  File "/home/yahyaa/.local/lib/python3.6/site-packages/aiostream/core.py", line 273, in init
    assert_async_iterable(args[0])
  File "/home/yahyaa/.local/lib/python3.6/site-packages/aiostream/aiter_utils.py", line 56, in assert_async_iterable
    f"{type(obj).__name__!r} object is not async iterable")
TypeError: 'list_iterator' object is not async iterable

Ответы [ 2 ]

0 голосов
/ 09 сентября 2018

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

Например, вы не можете await результат requests.get, потому что requests.get() не возвращает асинхронное будущее, а Response. Удаление await устраняет ошибку, но в итоге вы получаете обычный синхронный код. Чтобы ускорить параллельное выполнение нескольких загрузок, вам необходимо:

  • переключение с requests на асинхронную http-библиотеку, например отличную aiohttp

  • используйте asyncio.gather для ожидания параллельной загрузки, как показано, например, здесь

aiostream, вероятно, излишне для этой цели - он специально обрабатывает асинхронные итераторы. (Асинхронные итераторы являются объектами, подобными итераторам, чей __next__ называется __anext__ и является сопрограммой. Типичным вариантом использования может быть API базы данных, который предоставляет строки результатов с асинхронным итератором. Они повторяются с async for, но aiostream предлагает широкий спектр операторов для асинхронных итераторов, включая создание, преобразование, выбор, агрегирование и т. Д.)

0 голосов
/ 09 сентября 2018

Да, здесь вам нужен асинхронный итератор. После исправления он загружает URL-адреса асинхронно по порядку.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *.

xs = stream.iterate(myList)
ys = stream.starmap(xs, download, ordered=True, task_limit=20)
zs = stream.starmap(ys, process, ordered=True, task_limit=20)
await ys

Как это работает?

  • Сначала мы создаем асинхронный итерируемый объект над myList.

  • Затем мы передаем его download, который извлекает результаты асинхронно.

  • Затем мы передаем загруженный контент, возвращенный функцией download, process, чтобы обработать его как мы хотим. В моем случае с Beautiful Soup 4.

Обратите внимание, что starmap принимает неупакованные значения в виде [(,), (,), (,), . . .]. Вы можете сжать больше значений внутри этих кортежей. Мне нужно было два, поэтому я установил их в этом формате.


Рабочий код:

import asyncio
from aiostream import stream, pipe
from aiohttp import ClientSession
from bs4 import BeautifulSoup as bs

myList = []

def get_myList():
    #Append values to myList in the format [(,), (,), (,), ...]
    pass

async def download(link, title):
    # Download a page
    async with ClientSession() as session:
        async with session.get(url) as response:
            response = await response.read()
            return (response, title)

def process(response, title):
    # Do some processing with bs4
    pass

async def main():
    get_myList()

    xs = stream.iterate(myList)
    ys = stream.starmap(xs, download, ordered=True, task_limit=20)
    zs = stream.starmap(ys, process, ordered=True, task_limit=20)
    await ys

if __name__=="__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()
...