等待来自API C的成功响应

2024-06-16 11:33:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用Yahoo Api,我已经实现了随机睡眠方法,此外,我已经添加了硬睡眠,但我仍然无法想象,如果我在第一次尝试时没有得到响应,我如何才能等待或重试。

举个例子,我在下面放的代码,在一些用户身上完全是随机失败的。失败后,我在我的浏览器上的网址,它的工作就像一个魅力。所以我的问题是为什么?我该怎么解决呢?或者,我可以改进这段代码,以便在睡眠不足之后执行另一个请求(只有在这是一个好方法的情况下)

我忘记添加的信息很少,我更改了代码以获取http成功代码:

print urlobject.getcode()

它返回200,但没有json,因为有些人认为这可能是throttle。

注意:我已从url中删除appid(密钥)

# return the json question for given question id
def returnJSONQuestion(questionId):
    randomSleep()
    url = 'http://answers.yahooapis.com/AnswersService/V1/getQuestion?appid=APPIDREMOVED8&question_id={0}&output=json'
    format_url = url.format(questionId)
    try:
        request = urllib2.Request(format_url)
        urlobject = urllib2.urlopen(request)
        time.sleep(10)
        jsondata = json.loads(urlobject.read().decode("utf-8"))
        print jsondata
    except urllib2.HTTPError, e:
        print e.code
        logging.exception("Exception")
    except urllib2.URLError, e:
        print e.reason
        logging.exception("Exception")
    except(json.decoder.JSONDecodeError,ValueError):
        print 'Question ID ' + questionId + ' Decode JSON has failed'
        logging.info("This qid didn't work " + questionId)
    return jsondata

Tags: 方法代码jsonformathttpurlloggingurllib2
3条回答

我不知道失败的原因,这可能是一些雅虎限制(或可能不是),但实际上,这是一个好主意,保存问题ID,这会导致失败,稍后重试。

这很容易做到。稍微修改一下函数:

def returnJSONQuestion(questionId):
    randomSleep()
    jsondata = None
    url = 'http://answers.yahooapis.com/AnswersService/V1/getQuestion?appid=APPIDREMOVED8&question_id={0}&output=json'
    format_url = url.format(questionId)
    try:
        request = urllib2.Request(format_url)
        urlobject = urllib2.urlopen(request)
        time.sleep(10)
        jsondata = json.loads(urlobject.read().decode("utf-8"))
        print jsondata
    except urllib2.HTTPError, e:
        print e.code
        logging.exception("Exception")
    except urllib2.URLError, e:
        print e.reason
        logging.exception("Exception")
    except(json.decoder.JSONDecodeError,ValueError):
        print 'Question ID ' + questionId + ' Decode JSON has failed'
        logging.info("This qid didn't work " + questionId)
    return jsondata

在任何失败的情况下,这个函数都不会返回任何结果。所以,您可以检查结果,如果结果不是某个列表中的None-store问题id,然后重试3次。也许第二次会更幸运。

当然,您也可以修改函数,这样,它会在出错时同时重试请求几次,但第一个解决方案对我来说更可取。

顺便说一句,将“用户代理”头设置为一些真正的浏览器值-在这种情况下通常也是一个好主意,例如,在很多情况下,Google不会为这种“机器人解析器”返回结果

好吧,首先,有几点不能直接回答你的问题,但可能会有帮助:

1)我确信在调用urllib2.urlopen和读取返回的addinfourl对象之间不需要等待。在http://docs.python.org/library/urllib2.html#examples的例子中没有任何这样的睡眠。

2个)

json.loads(urlobject.read().decode("utf-8"))

可以简化为

json.load(urlobject)

更简单易读。 基本上,.load使用类似文件的对象作为参数,而.load使用字符串。您可能认为有必要先读取()数据,以便从utf-8对其进行解码,但事实上这没有问题,因为.load默认假定它读取的对象是ascii或utf-8编码的(请参见http://docs.python.org/library/json.html#json.load)。

3)对于您目前的目的,这可能不重要,但我认为您在这里的异常处理是糟糕的。如果在“try:”块中出现任何错误,那么将不会分配变量jsondata。然后,当尝试在try/except块结束后返回时,由于尝试使用未分配的变量,将引发NameError。这意味着,如果应用程序中的其他某个函数调用returnJSONQuestion并发生异常,则外部函数看到的将是NameError,而不是原始异常,并且外部函数生成的任何回溯都不会指向实际问题发生的位置。这很容易在试图找出哪里出了问题时引起混乱。因此,如果你所有的“除了”积木都用“raise”完成,那就更好了。

4)在Python中,最好在函数上方添加注释,说明函数作为docstring(请参见http://www.python.org/dev/peps/pep-0257/#what-is-a-docstring)的作用,而不是作为注释。

无论如何,要真正回答你的问题。。。

由于各种原因尝试打开URL时,可能会出现看似随机的URLError。可能是服务器在处理您的请求时出现了一个bug;可能是连接问题和一些数据丢失;可能是服务器在某个管理员更改设置或推送更新时关闭了几秒钟;可能是完全其他的问题。在做了一点web开发之后,我注意到一些服务器比其他服务器更可靠,但是我想对于大多数现实世界来说,您可能不需要担心为什么。最简单的方法就是重试请求直到成功。

考虑到以上所有因素,下面的代码可能会满足您的需要:

def returnJSONQuestion(questionId):
    """return the json question for given question id"""

    url = 'http://answers.yahooapis.com/AnswersService/V1/getQuestion?appid=APPIDREMOVED8&question_id={0}&output=json'
    format_url = url.format(questionId)
    try:
        request = urllib2.Request(format_url)

        # Try to get the data and json.load it 5 times, then give up
        tries = 5
        while tries >= 0:
            try:
                urlobject = urllib2.urlopen(request)
                jsondata = json.load(urlobject)
                print jsondata
                return jsondata
            except:
                if tries == 0:
                    # If we keep failing, raise the exception for the outer exception
                    # handling to deal with
                    raise
                else:
                    # Wait a few seconds before retrying and hope the problem goes away
                    time.sleep(3) 
                    tries -= 1
                    continue

    except urllib2.HTTPError, e:
        print e.code
        logging.exception("Exception")
        raise
    except urllib2.URLError, e:
        print e.reason
        logging.exception("Exception")
        raise
    except(json.decoder.JSONDecodeError,ValueError):
        print 'Question ID ' + questionId + ' Decode JSON has failed'
        logging.info("This qid didn't work " + questionId)
        raise

希望这有帮助!如果您要在程序中发出许多不同的web请求,您可能需要将这个“异常时重试请求”逻辑抽象到某个函数中,这样就不需要将样板重试逻辑与其他内容混合在一起。:)

我经常遇到这样的问题。我通常实现我的API请求包装器或浏览器“get”,如下所示:

def get_remote( url , attempt=0 ):
   try :
       request = urllib2.Request(format_url)
       urlobject = urllib2.urlopen(request)
       ...
       return data
   except urllib2.HTTPError , error:
       if error.code in ( 403 , 404 ):
           if attempt < MAX_ATTEMPTS :
                return get_remote( url , attempt=attempt+1 )
       raise

基于url或尝试,我还将更改请求参数。例如,某些网站会阻止Python识别的浏览器,因此如果它们匹配regex,我将用用户代理替换Firefox。或者:如果第一次尝试失败,我可能总是在第二次请求时尝试Firefox/Safari,或者在随后的两次尝试之间实现随机超时。

相关问题 更多 >