【问题标题】:Keep aiohttp session alive保持 aiohttp 会话处于活动状态
【发布时间】:2018-11-26 01:08:49
【问题描述】:

我正在尝试每隔 X 秒访问一个网站,并使用并行和单独的会话,然后分析响应中的内容以查看每个会话是否应该继续。但是,一旦代码到达第二个循环,它就会失败。

import asyncio
from aiohttp import ClientSession
import logging
import time

interval = 30
instances = 2
visit_url = 'http://www.example.org'

tasks = []

logging.basicConfig(
    format='%(asctime)s.%(msecs)03d %(message)s',  # Log in format time.milliseconds {message}
    level=logging.INFO,  # Use with logging.info()
    datefmt='%H:%M:%S')  # Display time as Hours:Minutes:Seconds


class StopException(Exception):
    pass


async def quit_app(session, task_, reason):
    logging.info("[{}] {}.".format(task_, reason))
    session.cookies.clear()  # Reset cookies
    session.headers.clear()  # Reset headers
    session.close()  # End HTTP connection
    raise StopException


async def get_status(response):
    if "abow" in response:
        return "success"
    elif "odoap" or "daoscp" in response:
        return "waiting"
    elif "nullt" in response:
        return "fail"
    elif "issue" in response:
        return "banned"
    elif "pending" in response:
        return "pending"
    else:
        return "pending"


async def initialise(headers):
    session = ClientSession()
    task_ = len(asyncio.Task.all_tasks()) - instances - 1
    passed = False
    while passed is False:
        async with session as session:
            async with session.get(visit_url, headers=headers, allow_redirects=True) as initial:
                status = await get_status(await initial.text())  # Check HTML for status
                if status == "success":
                    logging.info("[{}] {}.".format(task_, "Success"))
                    passed = True
                elif status == "pending":
                    logging.info("[{}] {}.".format(task_, "Pending.."))
                    await asyncio.sleep(interval)
                elif status == "waiting":
                    logging.info("[{}] {}.".format(task_, "Waiting..."))
                    await asyncio.sleep(interval)
                elif status == "banned":
                    await quit_app(initial, task_, "Banned")
                elif status == "fail":
                    await quit_app(initial, task_, "Failed")


if __name__ == "__main__":
    headers = {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'en-US,en;q=0.9',
        'upgrade-insecure-asks': '1',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36'
    }  # Add appropriate headers
    start = time.clock()
    loop = asyncio.get_event_loop()
    for i in range(instances):
        task = asyncio.ensure_future(initialise(headers))
        tasks.append(task)
    loop.run_until_complete(asyncio.wait(tasks))
    end = time.clock()
    print("Process took {0:.4f} seconds.".format(end - start))

此代码返回以下错误:

13:56:58.604 从未检索到任务异常 未来:任务完成 coro=initialise() 完成,定义在 C:/Users/x/PycharmProjects/tests/src/aiohttp_main.py:49 exception=RuntimeError('会话已关闭',) RuntimeError: 会话已关闭

在我 .close() 之前,我无法弄清楚如何让会话保持活跃......

【问题讨论】:

    标签: python python-asyncio aiohttp


    【解决方案1】:

    在我 .close() 之前,我无法弄清楚如何让会话保持活跃......

    async with 是关闭会话的请求。这意味着在你写完之后:

    async with session as session:
    

    ...一旦async with 的主体执行完毕,您就不能再使用session。这不是 aiohttp 特有的,它是with 在 Python 中的工作方式。例如,在处理文件时,with 是关闭文件的请求:

    with open('data.csv') as fileobj:
        # ... read stuff from fileobj
    
    # outside the "with" block, fileobj is closed and you
    # can no longer read from it
    

    修复很简单,只需将with 移到while 循环之外。例如:

    async def initialise(headers):
        async with ClientSession() as session:
            # the rest of the code, including the `while` loop, here
    

    在不相关的注释中,您可能希望将len(asyncio.Task.all_tasks()) 替换为您自己的全局计数器。如果您稍后将其他不相关的任务合并到事件循环中(或第三方库为您执行此操作),以这种方式使用 Task.all_tasks() 可能会开始产生错误的结果。

    【讨论】:

    • 顺便说一句,很抱歉在评论中提出问题,但您是否知道如何关闭会话然后停止代码运行(只有该会话/线程,其余的应该继续运行)?这就是我试图用 quit_app 做的,但它仍然崩溃。
    • 我想我需要试一试:...除了 StopException:通过某处
    • @JP 查看Task.cancel
    • 我不理解文档,我没有为每个会话设置任务,因此没有任何意义。但无论如何,当没有更多代码要运行时,它会自行停止,而我之前提到的应该这样做。如果我在具有try: <function> except StopException: pass 的函数中引发 StopException,那么它将起作用。但是,我不知道在哪里尝试...除了...
    • 我从来不知道(async) with 声明,这对我帮助很大!为了完整起见,异步 with 的行为记录在 PEP-492 中。在 ClientSession 对象上调用 async with 之后会触发其 __aexit__() 方法,该方法会执行 await self._close(),可以在 aiohttp source code 中检索。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2012-03-06
    • 1970-01-01
    • 2019-11-07
    • 2019-12-28
    • 2019-02-25
    相关资源
    最近更新 更多