upbit / pixivpy

Pixiv API for Python
https://pypi.org/project/PixivPy3/#files
The Unlicense
1.8k stars 147 forks source link

使用api过程遇到的2个以结果返回的错误 #327

Open xiyihan0 opened 10 months ago

xiyihan0 commented 10 months ago

一个是当api调用频率过高时,会返回:{'error': {'user_message': '', 'message': 'Rate Limit', 'reason': '', 'user_message_details': {}}}

另一个是当api访问到一些不存在的资源(如被删除的用户页面或小说)时,会返回:{'error': {'user_message': 'Your access is currently restricted.', 'message': '', 'reason': '', 'user_message_details': {}}} 例如:

api = AppPixivAPI()
api.set_auth(...)
print(api.user_detail(52714160))

这两种错误均以结果直接返回,并没有引发PixivError异常。

xiyihan0 commented 10 months ago

一个可能的解决方案是定义一个装饰器:

def retry_on_error(func):
    def wrapper(*args, **kwargs):
        import time
        while True:
            result = func(*args, **kwargs)
            if 'error' not in result:
                return result
            print('Error detected:%s'%result['error'])
            if result['error']['message'] == 'Rate Limit':  #retry
                print('Rate Limit, wait for 8s to try again')
                time.sleep(8)
            else:
                raise PixivError('Error detected:%s'%result['error'])
    return wrapper

然后将其应用到aapi中的方法上:

    @retry_on_error
    def novel_comments(
        self,
        novel_id: int | str,
        offset: int | str | None = None,
        include_total_comments: str | bool | None = None,
        req_auth: bool = True,
    ) -> ParsedJson:
    ...
upbit commented 10 months ago

装饰器确实是个很好的思路,能节省很多应用层error的判断的时间。 retry可以再考虑下,有时rate limit不一定要当前线程time.sleep(8),比如在集群内换个新的token/proxy

因此建议:

@raise_on_error
def xxx_api(...)

class RateLimitError(Exception):
    pass

def retry_on_error(func):
    # 对 message 这类明确的如`Rate Limit`,抛异常让外部去决定如何处理
    raise RateLimitError()