Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/352.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用Asyncio避免python出现错误429(请求过多)_Python_Python Asyncio_Http Status Code 429 - Fatal编程技术网

如何使用Asyncio避免python出现错误429(请求过多)

如何使用Asyncio避免python出现错误429(请求过多),python,python-asyncio,http-status-code-429,Python,Python Asyncio,Http Status Code 429,我使用以下代码向aiohttp客户端发出请求。我尝试发送请求的服务器每IP每小时有30k请求限制。所以我收到了429个太多的请求错误。每当这项工作达到极限时,我就想让它进入睡眠状态 我可以从标题中提取x_rateLimit_重置,所以我想我可以用它来让作业进入睡眠状态,但我观察到了非常奇怪的行为。有时工作睡眠时间变为负值,有时工作陷入睡眠模式 例如,上次我运行作业时,它先休眠2000秒,然后在时间过去后,它再次尝试再休眠2500秒,并陷入休眠模式。我认为可能是其他并行进程导致了这个问题,所以我想

我使用以下代码向aiohttp客户端发出请求。我尝试发送请求的服务器每IP每小时有30k请求限制。所以我收到了429个太多的请求错误。每当这项工作达到极限时,我就想让它进入睡眠状态

我可以从标题中提取x_rateLimit_重置,所以我想我可以用它来让作业进入睡眠状态,但我观察到了非常奇怪的行为。有时工作睡眠时间变为负值,有时工作陷入睡眠模式

例如,上次我运行作业时,它先休眠2000秒,然后在时间过去后,它再次尝试再休眠2500秒,并陷入休眠模式。我认为可能是其他并行进程导致了这个问题,所以我想知道在使用Asyncio时如何处理太多的请求错误消息

@backoff.on_exception(backoff.expo, (asyncio.TimeoutError, aiohttp.client_exceptions.ServerDisconnectedError,TooManyRequests),
                          max_time=300)
    async def fetch(self, url, session, params):
        try:
            async with session.get(url, params=params) as response:
                now = int(time.time())
                print(response)
                output = await response.read()
                output = json.loads(output)

                if 'X-RateLimit-Remaining' in response.headers:
                    rate = response.headers['X-RateLimit-Remaining']

                if 'status' in output and output['status'] == 429:
                    x_rateLimit_reset = int(response.headers['X-RateLimit-Reset'])
                    print("sleep mode")
                    seconds = x_rateLimit_reset - now
                    LOGGER.info("The job will sleep for {} seconds".format(seconds))
                    time.sleep(max(seconds,0))
                    raise TooManyRequests()



            return output

        except (asyncio.TimeoutError, TypeError, json.decoder.JSONDecodeError,
                aiohttp.client_exceptions.ServerDisconnectedError) as e:
            print(str(e))

    async def bound_fetch(self, sem, url, session, params):
        # Getter function with semaphore.
        async with sem:
            output = await self.fetch(url, session, params)
        return {"url": url, "output": output}
编辑: 这就是我如何启动绑定获取和定义URL的方法:

def get_responses(self, urls, office_token, params=None):   
    loop = asyncio.get_event_loop()
    future = asyncio.ensure_future(self.run(office_token, urls, params))
    responses = loop.run_until_complete(future)
    return responses

async def run(self, office_token, urls, params):
        tasks = []
        # create instance of Semaphore
        sem = asyncio.BoundedSemaphore(200)
        timeout = ClientTimeout(total=1000)

        async with ClientSession(auth=BasicAuth(office_token, password=' '), timeout=timeout,
                                 connector=TCPConnector(ssl=False)) as session:
            for url in urls:
                # pass Semaphore and session to every GET request
                task = asyncio.ensure_future(self.bound_fetch(sem, url, session, params))
                tasks.append(task)

            responses = await asyncio.gather(*tasks)
            return responses

urls = [
                        "{}/{}".format(self.base_url, "{}?page={}&api_key={}".format(object_name, page_number, self.api_keys))
                        for page_number in range(batch * chunk_size + 1, chunk_size * (1 + batch) + 1)]
您使用time.sleep而不是wait asyncio.sleep的主要原因

更新

下面是最简单的工作解决方案和一些注释

请使用它来采用您的解决方案

看看

进口aiohttp 导入异步 从日期时间导入日期时间 异步def fetchsession,任务:获取URL并标记执行结果 与session.gettask['url']异步作为响应: 如果响应.status!=200: response.raise_以获取_状态 在这里,如果429代码被获取,您需要以某种方式处理它 在我的例子中,我只是跳过它。 任务['result']=response.status 任务['status']='done' 等待回复。文本以确保我们获取数据 printf{strdatetime.now}:获取了{task['url']}日志记录的结果 任务['result']=response.status 任务['status']='done' 异步def fetch_allsession,URL,persecond: 转换成目录 url_tasks=[{'url':i,'result':None,'status':'new'}用于url中的i] n=0计数器 尽管如此: 计算当前正在获取多少任务 running_tasks=len[i for i in url_tasks,如果i['fetch']中的i['status'] 计算还有多少任务需要执行 is_tasks_to_wait=len[i在url_tasks中代表i,如果i['status']!='done'] 检查我们是否不在列表的末尾n=persecond: 打印“节流” 等待asyncio.sleep1 这是另一个主要技巧 保持asyncio.run或loop.run_直到_完成执行 我们需要等待一点,而不是检查所有的任务都完成了,并且 等等等等 如果是任务等待0: 等待asyncio.sleep0.1等待所有任务完成 其他: 所有任务都完成了 打破 返回url\u任务 异步def主: URL=['http://google.com/?1', 'http://google.com/?2', 'http://google.com/?3']*3 与aiohttp.ClientSession作为会话异步: res=等待获取所有会话,URL,3 印刷品 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': asyncio.runmain asyncio.run取消所有挂起的任务我们没有这些任务, 因为我们检查所有的任务都完成了 asyncio.run不等待取消所有任务 asyncio.run do stop循环 退出程序
请显示您定义URL列表并启动绑定抓取的代码请参阅编辑部分请参阅编辑回答如何处理429代码?我们能将429个URL排队并重新发送吗?很简单,它的优点是异步的。因此,不需要序列化数据,所有变量都可以访问。此外,在一般情况下,您不会面临竞争条件。在我的示例中,fetch函数只需在收到任务后将任务的状态设置为“新建”=200条件。在fetch_all函数中,此任务将在main中重新调度。