2012-10-19 163 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

嗯。注意詳細說明在鏈接輔助函數時如何使用遞歸而不是迭代提供好處?我不明白這是怎麼回事,並認爲你在這一點上是錯誤的,雖然我可能只是錯過了一些明顯的東西(不會是第一次!) –