2012-10-19 162 views
0

我正在使用Yahoo Api,除此之外我还实现了随机睡眠方法,但我仍然无法确定如何等待或再次尝试,在第一次尝试时得到回应。等待API响应成功

举例来说,我在下面提供的代码在某些用户中完全失败。失败后,我会在浏览器上看到这个网址,它就像一个魅力一样。所以我的问题是为什么?我该如何解决这个问题?或者我可以改善这个代码做一个困难的睡眠后做另一个请求(只有如果这是一个很好的做法)

我有更多的信息,我忘了添加,我改变了代码,以获得我的http成功代码:

print urlobject.getcode() 

它返回200,但没有JSON,因为一些人认为这可能是油门。

注:我已经从URL

# 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 
+0

这将是伟大的,如果你将添加确切的失败信息 – cleg

+0

哇感谢这么多奇妙的答案!我将尝试其中的一个工具并看看。我不知道我应该选择哪个答案,他们都精彩地解释希望所有人都能得到选票。 –

回答

3

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

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。在处理您的请求期间,可能在服务器上存在一个错误;也许有连接问题,有些数据丢失;也许服务器停机了几秒钟,而其中一个管理员更改了设置或推送了更新;也许完全是别的。我在做一点网络开发之后已经注意到有些服务器比其他服务器更可靠,但是我认为对于大多数真实世界的目的,您可能不需要担心原因。最简单的事情就是重试请求直到你成功。

与所有上述考虑,下面的代码可能会做你需要的东西:

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请求,你可能想把这个“异常重试请求”逻辑抽象到某个函数中,这样你就不需要重试样板文件了与其他东西混合在一起的逻辑。 :)

+0

非常感谢你的一个好主意。 –

+0

TypeError:期望的字符串或缓冲区有任何机会可以告诉我为什么我会从这种方法得到这样的错误? –

+0

这里是你告诉不要使用json.loads(urlobject.read()。decode(“utf-8”))的协议,但没有这个我没有得到任何json数据,所以我猜解码应该发生。 –

1

我不知道故障原因,打消了我的appid(关键),这可能是一些雅虎节流限制(或可能不会)但实际上,保存问题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 

而在任何失败的情况下,这个函数将返回你无。所以,你可以检查结果,如果它没有 - 在某个列表中存储问题ID,然后重试约3次。也许第二次会更幸运。

当然,你也可以修改这个函数,所以它在错误时同时重试请求几次,但是对于我来说第一个解决方案看起来更可取。

顺便说一句,“用户代理”标头设置为一些真正的浏览器的值 - 通常也是在这种情况下,一个好主意,例如谷歌没有在许多情况下,返回结果为这样的“ROBO-解析器”

+0

其实我已经在做你所建议的我正在记录所有失败的问题并重新运行它们。谢谢, –

1

我遇到过这样的问题很多。我通常执行我的API请求包装或浏览器“得到”是这样的:

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,或者在后续尝试之间实施随机超时。

+0

这将完成这项工作,但亲自: 1)我不会在这里使用递归时,迭代将会很好 2)我不会限制自己捕捉只重试HTTPError,并且绝对不会限制自己只重试某些错误代码; httplib的。IncompleteRead和不是HTTPError实例的各种形式的URLError(例如:连接拒绝错误)通常也是值得重试的瞬态问题。 3)我会在两次尝试之间休息几秒钟。日常浏览告诉我,网站有时会下降几秒钟;重试之间的延迟有助于解决这个问题。 –

+0

这只是示例代码来说明这一点。我发现递归效果最好,因为(并且我从示例中省略了这一点),因为在处理URL时,往往会出现大量的misc重定向或随机重新格式化。例如,您可能会处理一个指向nyti.ms链接的bit.ly链接,该链接指向newyorktimes.com文章,然后尝试触发付费墙重定向(尽管内容仍可用)。通过将函数分解出来,更容易从调度到的辅助函数中调用。 –

+0

嗯。注意详细说明在链接辅助函数时如何使用递归而不是迭代提供好处?我不明白这是怎么回事,并认为你在这一点上是错误的,虽然我可能只是错过了一些明显的东西(不会是第一次!) –