Pythonを使用してWebサイトにログインし、いくつかのWebページから情報を収集しようとすると、次のエラーが表示されます。
Traceback (most recent call last): File "extract_test.py", line 43, in <module> response=br.open(v) File "/usr/local/lib/python2.7/dist-packages/mechanize/_mechanize.py", line 203, in open return self._mech_open(url, data, timeout=timeout) File "/usr/local/lib/python2.7/dist-packages/mechanize/_mechanize.py", line 255, in _mech_open raise response mechanize._response.httperror_seek_wrapper: HTTP Error 429: Unknown Response Code
time.sleep()
を使用しましたが、動作しますが、インテリジェントで信頼性が低いようですが、このエラーを回避する他の方法はありますか?
これが私のコードです:
import mechanize
import cookielib
import re
first=("example.com/page1")
second=("example.com/page2")
third=("example.com/page3")
fourth=("example.com/page4")
## I have seven URL's I want to open
urls_list=[first,second,third,fourth]
br = mechanize.Browser()
# Cookie Jar
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)
# Browser options
br.set_handle_equiv(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)
# Log in credentials
br.open("example.com")
br.select_form(nr=0)
br["username"] = "username"
br["password"] = "password"
br.submit()
for url in urls_list:
br.open(url)
print re.findall("Some String")
ステータス429の受信はではなくエラーであり、リクエストのスパム送信を停止するように「親切に」求める他のサーバーです。明らかに、リクエストの割合が高すぎて、サーバーはこれを受け入れません。
これを「回避」したり、IPをスプーフィングしようとしてサーバーのセキュリティ設定を回避したりすることは避けてください。あまり多くのリクエストを送信しないことでサーバーの回答を尊重する必要があります。
すべてが適切に設定されていれば、429応答とともに「Retry-after」ヘッダーも受信しています。このヘッダーは、別の呼び出しを行う前に待機する秒数を指定します。この「問題」に対処する適切な方法は、このヘッダーを読み取り、その数秒間プロセスをスリープさせることです。
ステータス429の詳細については、こちらをご覧ください: http://tools.ietf.org/html/rfc6585#page-
このコードを書いて問題を解決しました:
requests.get(link, headers = {'User-agent': 'your bot 0.1'})
MRAが言ったように、429 Too Many Requests
をかわそうとするのではなく、それに応じて処理する必要があります。ユースケースに応じて、いくつかのオプションがあります。
1)プロセスをスリープ状態にします。サーバーは通常、応答にRetry-after
ヘッダーを含め、再試行するまで待機する秒数を含めます。プロセスをスリープさせると問題が発生する可能性があることに注意してください。タスクキューで、代わりに後でタスクを再試行して、ワーカーを他の作業のために解放する必要があります。
2)指数バックオフ。サーバーが待機時間を通知していない場合は、その間に一時停止を増やしてリクエストを再試行できます。人気のタスクキューCeleryにはこの機能があります built-in-built 。
3)トークンバケット。この手法は、特定の時間に作成できるリクエストの数が事前にわかっている場合に役立ちます。 APIにアクセスするたびに、最初にバケットからトークンを取得します。バケットは一定の速度で補充されます。バケットが空の場合、APIに再度アクセスする前に待機する必要があることがわかります。トークンバケットは通常、相手側(API)に実装されますが、429 Too Many Requests
を取得しないようにするためにプロキシとして使用することもできます。 Celeryの rate_limit 機能は、トークンバケットアルゴリズムを使用します。
以下は、指数バックオフとレート制限/トークンバケットを使用したPython/Celeryアプリの例です。
class TooManyRequests(Exception):
"""Too many requests"""
@task(
rate_limit='10/s',
autoretry_for=(ConnectTimeout, TooManyRequests,),
retry_backoff=True)
def api(*args, **kwargs):
r = requests.get('placeholder-external-api')
if r.status_code == 429:
raise TooManyRequests()
別の回避策は、何らかのパブリックVPNまたはTorネットワークを使用してIPをスプーフィングすることです。これは、IPレベルでのサーバーのレート制限を前提としています。
Urllib2とともにtorを使用する方法を示す短いブログ投稿があります。