等待API调用成功响应

时间:2012-10-19 17:52:31

标签: python json httprequest urllib2

我正在使用Yahoo Api,我已经实现了随机睡眠方法,除了我已经增加了硬睡眠,但我仍然无法想象如果我在第一次尝试时没有得到响应我可以等待或再试一次。

例如,我在下面提到的代码在某些用户处完全失败,完全是随机的。在它失败之后,我在我的浏览器上获取网址,它就像一个魅力。所以我的问题是为什么?我该如何解决这个问题?或者我可以改进这段代码,以便在困难的睡眠后做另一个请求(只有这是一个好的方法)

我还有一些我忘记添加的信息,我更改了代码以获取我的http成功代码:

print urlobject.getcode()

并返回200,但没有json,因为有人认为这可能是节流。

注意:我已从网址

中删除了我的appid(Key)
# 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

3 个答案:

答案 0 :(得分:4)

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

1)我很确定在调用urllib2.urlopen和读取返回的addinfourl对象之间永远不需要等待。 http://docs.python.org/library/urllib2.html#examples处的示例没有任何此类睡眠。

2)

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

可以简化为

json.load(urlobject)

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

3)对于你目前的目的而言,这可能无关紧要,但我认为你的异常处理在这里是不好的。如果在“try:”块期间出现任何问题,则不会分配变量jsondata。然后当我们尝试在try / except块结束后返回它时,由于尝试使用未分配的变量,将引发NameError。这意味着如果应用程序中的某些其他函数调用returnJSONQuestion并发生异常,那么它将是一个NameError,而不是外部函数看到的原始异常,并且外部函数生成的任何回溯都不会指向哪里真正的问题发生了。在试图弄清楚出了什么问题时,这很容易引起混淆。因此,如果你所有的“除外”区块都以“加注”结束,那会更好。

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

无论如何,要实际回答你的问题 ......

当出于各种原因尝试打开URL时,您可能会看到一个看似随机的URLError。在处理您的请求时,服务器上可能存在错误;也许有连接问题,一些数据下降;也许服务器停机了几秒钟,而其中一个管理员更改了设置或推送了更新;也许完全是别的东西。我注意到在进行了一些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请求,您可能希望将这个“异常重试请求”逻辑抽象到某个函数中,这样您就不需要重新进行样板重试了。逻辑与其他东西混合在一起。 :)

答案 1 :(得分:1)

我不知道失败的原因,这可能是一些雅虎限制(或可能不是),但实际上,保存问题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。因此,您可以检查结果,如果它是None - 在某个列表中存储问题ID,然后重试约3次。也许第二次会更幸运。

当然,你也可以修改这个功能,它会在错误时同时重试请求几次,但第一个解决方案看起来更适合我。

BTW,将“User-Agent”标题设置为某个真实的浏览器值 - 在这种情况下通常也是一个好主意,例如Google在许多情况下不会为这样的“robo-parsers”返回结果

答案 2 :(得分:1)

我经常遇到这样的问题。我通常会像我这样实现我的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

基于网址或尝试,我也会更改请求参数。例如,某些网站将阻止Python识别的浏览器 - 所以如果它们匹配正则表达式,我将交换Firefox的用户代理。或者:如果第一次尝试失败,我可能会在第二次请求时尝试使用Firefox / Safari,或者在后续尝试之间实现随机超时。