Dalam skrip saya, requests.get
tidak pernah kembali:
import requests
print ("requesting..")
# This call never returns!
r = requests.get(
"http://www.some-site.com",
proxies = {'http': '222.255.169.74:8080'},
)
print(r.ok)
Apa kemungkinan alasannya? Ada obatnya? Apa waktu tunggu default yang get
digunakan?
python
get
python-requests
Nawaz
sumber
sumber
proxies={'http': 'http://222.255.169.74:8080'}
. Itu bisa jadi mengapa tidak selesai tanpa batas waktu.Jawaban:
Batas waktu default adalah
None
, yang berarti menunggu (hang) hingga koneksi ditutup.Apa yang terjadi jika Anda memberikan nilai batas waktu?
r = requests.get( 'http://www.justdial.com', proxies={'http': '222.255.169.74:8080'}, timeout=5 )
sumber
None
berarti tak terbatas (atau "tunggu sampai koneksi dekat"). Jika saya sendiri melewatkan waktu tunggu, itu kembali!print(requests.request.__doc__)
di IPython lebih dari apa yang saya cari. Saya bertanya-tanya argumen opsional apa lagi yangrequest.get()
ada.Dari dokumentasi permintaan :
Itu sering terjadi pada saya bahwa requests.get () membutuhkan waktu yang sangat lama untuk kembali bahkan jika
timeout
1 detik. Ada beberapa cara untuk mengatasi masalah ini:1. Gunakan
TimeoutSauce
kelas internalDari: https://github.com/kennethreitz/requests/issues/1928#issuecomment-35811896
2. Gunakan garpu permintaan dari kevinburke: https://github.com/kevinburke/requests/tree/connect-timeout
Dari dokumentasinya: https://github.com/kevinburke/requests/blob/connect-timeout/docs/user/advanced.rst
CATATAN: Perubahan tersebut telah digabungkan ke proyek Permintaan utama .
3. Menggunakan
evenlet
atausignal
seperti yang telah disebutkan dalam pertanyaan serupa: Batas waktu untuk permintaan python. Dapatkan seluruh responssumber
Saya ingin batas waktu default dengan mudah ditambahkan ke sekumpulan kode (dengan asumsi batas waktu menyelesaikan masalah Anda)
Ini adalah solusi yang saya ambil dari tiket yang dikirimkan ke repositori untuk Permintaan.
kredit: https://github.com/kennethreitz/requests/issues/2011#issuecomment-477784399
Solusinya adalah beberapa baris terakhir di sini, tetapi saya menunjukkan lebih banyak kode untuk konteks yang lebih baik. Saya suka menggunakan sesi untuk mencoba lagi perilaku.
import requests import functools from requests.adapters import HTTPAdapter,Retry def requests_retry_session( retries=10, backoff_factor=2, status_forcelist=(500, 502, 503, 504), session=None, ) -> requests.Session: session = session or requests.Session() retry = Retry( total=retries, read=retries, connect=retries, backoff_factor=backoff_factor, status_forcelist=status_forcelist, ) adapter = HTTPAdapter(max_retries=retry) session.mount('http://', adapter) session.mount('https://', adapter) # set default timeout for method in ('get', 'options', 'head', 'post', 'put', 'patch', 'delete'): setattr(session, method, functools.partial(getattr(session, method), timeout=30)) return session
maka Anda dapat melakukan sesuatu seperti ini:
sumber
Meninjau semua jawaban dan sampai pada kesimpulan bahwa masalahnya masih ada. Di beberapa situs, permintaan mungkin macet tanpa batas dan menggunakan multiprosesing tampaknya berlebihan. Inilah pendekatan saya (Python 3.5+):
import asyncio import aiohttp async def get_http(url): async with aiohttp.ClientSession(conn_timeout=1, read_timeout=3) as client: try: async with client.get(url) as response: content = await response.text() return content, response.status except Exception: pass loop = asyncio.get_event_loop() task = loop.create_task(get_http('http://example.com')) loop.run_until_complete(task) result = task.result() if result is not None: content, status = task.result() if status == 200: print(content)
MEMPERBARUI
Jika Anda menerima peringatan penghentian penggunaan conn_timeout dan read_timeout, periksa di dekat bagian bawah referensi INI untuk mengetahui cara menggunakan struktur data ClientTimeout. Satu cara sederhana untuk menerapkan struktur data ini per referensi yang ditautkan ke kode asli di atas adalah:
async def get_http(url): timeout = aiohttp.ClientTimeout(total=60) async with aiohttp.ClientSession(timeout=timeout) as client: try: etc.
sumber
Menambal fungsi "kirim" yang terdokumentasi akan memperbaiki masalah ini untuk semua permintaan - bahkan di banyak pustaka dan SDK yang bergantung. Saat menambal libs, pastikan untuk menambal fungsi yang didukung / didokumentasikan, bukan TimeoutSauce - jika tidak, Anda mungkin akan kehilangan efek tambalan Anda secara diam-diam.
import requests DEFAULT_TIMEOUT = 180 old_send = requests.Session.send def new_send(*args, **kwargs): if kwargs.get("timeout", None) is None: kwargs["timeout"] = DEFAULT_TIMEOUT return old_send(*args, **kwargs) requests.Session.send = new_send
Efek dari tidak adanya batas waktu cukup parah, dan penggunaan batas waktu default hampir tidak pernah merusak apa pun - karena TCP sendiri memiliki batas waktu default juga.
sumber
Dalam kasus saya, alasan "requests.get tidak pernah kembali" adalah karena
requests.get()
upaya untuk menyambung ke host diselesaikan dengan ipv6 ip terlebih dahulu . Jika ada yang salah saat menghubungkan ipv6 ip itu dan macet, maka ipv4 ip akan dicoba lagi jika saya secara eksplisit mengaturtimeout=<N seconds>
dan menekan batas waktu.Solusi saya adalah menambal python
socket
untuk mengabaikan ipv6 (atau ipv4 jika ipv4 tidak berfungsi), baik jawaban ini atau jawaban ini berfungsi untuk saya.Anda mungkin bertanya-tanya mengapa
curl
perintah berfungsi, karenacurl
sambungkan ipv4 tanpa menunggu ipv6 selesai. Anda dapat melacak sistem socket denganstrace -ff -e network -s 10000 -- curl -vLk '<your url>'
perintah. Untuk python,strace -ff -e network -s 10000 -- python3 <your python script>
perintah bisa digunakan.sumber