0

I'm trying to get a list of HTML source code from a list of URLs asynchronously with asyncio and aiohttp but I get 2 Exceptions:

  1. TypeError('An asyncio.Future, a coroutine or an awaitable is ' TypeError: An asyncio.Future, a coroutine or an awaitable is required Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001C92141F310>
  2. raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed

This is my code:

import asyncio
import aiohttpasync 

def main():
    tasks = []
    html_list = []
    async with aiohttp.ClientSession() as session:
        for url in ['http://www.apple.com', 'http://www.google.cl']:
            async with session.get(url) as resp:
                tasks.append(html_list.append(await resp.read()))
        print(html_list[0])
        print(html_list[1])
        await(asyncio.wait(tasks))

if __name__ == '__main__':
    asyncio.run(main())

This code gets to print the html codes of ['http://www.apple.com', 'http://www.google.cl'] but immediatly after I get the aforementioned Exceptions.

  • 1
    `html_list.append` returns `None`. When you try to await `tasks`, it contains nothing but a list of `None`s. At that point you've already awaited all of the requests so you don't even need `tasks`. – dirn Aug 01 '20 at 18:38
  • @dirn I removed the `tasks` `list`, replaced `tasks.append(html_list.append(await resp.read()))` with just `html_list.append(await resp.read())` and removed the `await(asyncio.wait(tasks))` line. HTML fetched codes are printed and I don't get the first error, but I still get the `RuntimeError: Event loop is closed` error. – Oliver Mohr Bonometti Aug 01 '20 at 19:41

1 Answers1

1

There are a few things that aren't quite right with your example:

  1. You're not running anything concurrently. When you await resp.read() in your for loop, this blocks main until the result comes back. Perhaps this is what you want, but you need to use asyncio.create_task to run your requests concurrently.

  2. As pointed out, you don't need the task array at all because of point number one. You can just append to html_list.

  3. You don't need to call asynio.wait because you're not awaiting any tasks or coroutines at this point.

You can resolve your direct issues by what you have done in the comments, but a version that runs concurrently looks like this:

import asyncio
import aiohttp

async def read_url(session, url):
    async with session.get(url) as resp:
        return await resp.read()


async def main():
    tasks = []
    async with aiohttp.ClientSession() as session:
        for url in ['http://www.apple.com', 'http://www.google.cl']:
            tasks.append(asyncio.create_task(read_url(session, url)))
        html_list = await asyncio.gather(*tasks)
        print(html_list[0])
        print(html_list[1])

if __name__ == '__main__':
    asyncio.run(main())

Here we define a coroutine read_url which gets the contents of a single url. Then in the loop, you create a task for reading the url and append it to the tasks list. Then you use asyncio.gather - this will wait for all tasks to finish concurrently.

As written, I'm unable to reproduce your RuntimeError: Event loop is closed error.

Matt Fowler
  • 2,563
  • 2
  • 15
  • 18
  • Thanks, this works, but I still get the error: `Exception ignored in: ` `...(asyncio Traceback error details...)...` `raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed` – Oliver Mohr Bonometti Aug 02 '20 at 16:30
  • @OliverMohrBonometti interesting that I'm not seeing this - how are you running your script and which versions of Python and aiohttp are you using? I'm using Python 3.7.8 and aiohttp 3.6.2 – Matt Fowler Aug 02 '20 at 20:27
  • it is `Python 3.8` and `aiohttp 3.6.2` – Oliver Mohr Bonometti Aug 02 '20 at 20:45