Bagaimana menghindari kesalahan HTTP 429 (Terlalu Banyak Permintaan) python

94

Saya mencoba menggunakan Python untuk masuk ke situs web dan mengumpulkan informasi dari beberapa halaman web dan saya mendapatkan kesalahan berikut:

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

Saya menggunakan time.sleep()dan berfungsi, tetapi tampaknya tidak cerdas dan tidak dapat diandalkan, apakah ada cara lain untuk menghindari kesalahan ini?

Ini kode saya:

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")
Aous1000
sumber
6
Tidak ada jalan lain, ini adalah penegakan di sisi server yang melacak berapa banyak permintaan / unit waktu yang Anda buat. Jika Anda melebihi unit ini, Anda akan diblokir sementara. Beberapa server mengirimkan informasi ini di header, tetapi kejadian tersebut jarang terjadi. Periksa header yang diterima dari server, gunakan informasi yang tersedia .. Jika tidak, periksa seberapa cepat Anda dapat memalu tanpa ketahuan dan gunakan a sleep.
Diturunkan

Jawaban:

158

Menerima status 429 bukanlah suatu kesalahan , itu adalah server lain yang "dengan hormat" meminta Anda untuk menghentikan permintaan spamming. Jelasnya, tingkat permintaan Anda terlalu tinggi dan server tidak mau menerimanya.

Anda tidak boleh berusaha untuk "mengelak" ini, atau bahkan mencoba mengelak dari pengaturan keamanan server dengan mencoba menipu IP Anda, Anda harus menghormati jawaban server dengan tidak mengirimkan terlalu banyak permintaan.

Jika semuanya sudah diatur dengan benar, Anda juga akan menerima header "Retry-after" bersama dengan respon 429. Header ini menentukan berapa detik Anda harus menunggu sebelum melakukan panggilan lain. Cara yang tepat untuk menangani "masalah" ini adalah dengan membaca tajuk ini dan menghentikan proses Anda selama beberapa detik.

Anda dapat menemukan informasi lebih lanjut tentang status 429 di sini: http://tools.ietf.org/html/rfc6585#page-3

Penilaian Multi Penilai
sumber
23
Nah, tidak ada yang pernah mengatakan bahwa semua server web dikonfigurasi dengan benar. Selain itu, karena sebagian besar pembatas kecepatan mengidentifikasi pengunjung berdasarkan IP, hal ini dapat menyebabkan masalah dalam skenario di mana IP dibagikan secara dinamis. Jika Anda tetap menerima status 429 meskipun Anda yakin bahwa Anda tidak mengirim terlalu banyak permintaan sama sekali, Anda dapat mempertimbangkan untuk menghubungi administrator situs.
MRA
2
Terima kasih telah menyebutkan tajuk "Coba lagi setelah". Saya ingin contoh kode untuk melihat bagaimana mendapatkan nilai itu (saya menggunakan urllib, untuk mekanik OP, dalam kedua kasus saya tidak berpikir header termasuk dalam pengecualian yang dimunculkan)
MacFreek
@MacFreek Saya tidak memiliki contoh kode Python tertentu yang siap, tetapi saya berasumsi beberapa contoh tentang cara mengambil header respons secara umum dapat diambil dari jawaban atas pertanyaan ini: stackoverflow.com/q/843392
MRA
Terima kasih @MRA. Saya menemukan bahwa header juga tersedia dalam pengecualian: setelah menangkap HTTPError as my_exception, itu tersedia di my_exception.headers, setidaknya untuk urllib2.
MacFreek
38

Menulis kode ini memperbaiki masalah saya:

requests.get(link, headers = {'User-agent': 'your bot 0.1'})

tadm123
sumber
26
Jawaban ini tidak disukai, tetapi beberapa situs secara otomatis mengembalikan kode kesalahan 429 jika agen pengguna diblokir karena penyalahgunaan dari orang lain. Jika Anda mendapatkan kode kesalahan 429 meskipun Anda hanya mengirim sedikit permintaan, coba setel agen pengguna ke yang lain.
Ferry Boender
7
Juga ingin menambahkan, beberapa situs menolak permintaan dengan jelas kecuali agen pengguna dikirim, dan Anda mungkin mendapatkan banyak sekali tanggapan lain: 503/403 / beberapa halaman indeks umum.
pengguna3791372
1
Bisa mengkonfirmasi ini. Hanya mencoba untuk menghubungkan python dengan reddit dan tanpa mengatur agen pengguna saya selalu mendapatkan kode kesalahan 429.
Karrq
1
bisakah anda menambahkan penjelasan?
Tokci
Di mana Anda "menulis kode ini"? Solusi ini membutuhkan lebih banyak detail.
Joe McLean
29

Seperti yang dikatakan Penilaian Multi Penilai, Anda tidak boleh mencoba mengelak, 429 Too Many Requeststetapi menanganinya dengan tepat. Anda memiliki beberapa opsi tergantung pada kasus penggunaan Anda:

1) Tidur proses Anda . Server biasanya menyertakan Retry-aftertajuk dalam respons dengan jumlah detik yang seharusnya Anda tunggu sebelum mencoba lagi. Ingatlah bahwa menghentikan proses dapat menyebabkan masalah, misalnya dalam antrian tugas, di mana Anda harus mencoba ulang tugas di lain waktu untuk membebaskan pekerja dari hal-hal lain.

2) Kemunduran eksponensial . Jika server tidak memberi tahu Anda berapa lama harus menunggu, Anda dapat mencoba kembali permintaan Anda menggunakan jeda yang meningkat di antaranya. Tugas populer antrian Seledri memiliki fitur ini dibangun tepat-in .

3) Ember token . Teknik ini berguna jika Anda mengetahui sebelumnya berapa banyak permintaan yang dapat Anda buat dalam waktu tertentu. Setiap kali Anda mengakses API, pertama-tama Anda mengambil token dari bucket. Ember diisi ulang dengan kecepatan konstan. Jika keranjang kosong, Anda harus menunggu sebelum membuka API lagi. Keranjang token biasanya diterapkan di ujung lain (API) tetapi Anda juga dapat menggunakannya sebagai proxy untuk menghindari mendapatkan file 429 Too Many Requests. Fitur rate_limit Celery menggunakan algoritma ember token.

Berikut adalah contoh aplikasi Python / Celery yang menggunakan backoff eksponensial dan bucket rate-limiting / token:

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()
psaniko
sumber
9

Solusi lain adalah memalsukan IP Anda menggunakan semacam VPN Publik atau jaringan Tor. Ini akan mengasumsikan pembatasan kecepatan pada server pada tingkat IP.

Ada posting blog singkat yang mendemonstrasikan cara menggunakan tor bersama dengan urllib2:

http://blog.flip-edesign.com/?p=119

Gaurav Agarwal
sumber
8
Inilah sebabnya mengapa saya selalu meminta pengguna API saya untuk mendaftar kunci untuk membuat permintaan. Dengan cara ini saya bisa membatasi permintaan dengan kunci daripada IP. Mendaftarkan kunci lain akan menjadi satu-satunya cara untuk mendapatkan batas yang lebih tinggi.
Mnebuerquo
4
if response.status_code == 429:
  time.sleep(int(response.headers["Retry-After"]))
davidbrown
sumber
1

Saya telah menemukan solusi yang bagus untuk pemblokiran IP saat mengorek situs. Ini memungkinkan Anda menjalankan Scraper tanpa batas waktu dengan menjalankannya dari Google App Engine dan menerapkannya kembali secara otomatis saat Anda mendapatkan 429.

Lihat artikel ini

Juan Luis Ruiz-tagle
sumber
Haha wow ... menggunakan Google untuk mengikis Google. Dan kemudian mengubah IP Google Anda saat Google memblokirnya.
sam1370