Permintaan Asinkron dengan permintaan Python


142

Saya mencoba sampel yang disediakan dalam dokumentasi pustaka permintaan untuk python.

Dengan async.map(rs), saya mendapatkan kode respons, tetapi saya ingin mendapatkan konten dari setiap halaman yang diminta. Ini, misalnya, tidak berfungsi:

out = async.map(rs)
print out[0].content

Mungkin respons yang Anda dapatkan memiliki tubuh kosong?
Mariusz Jamro

Bekerja untukku. Silakan kirim kesalahan lengkap yang Anda dapatkan.
Chewie

tidak ada kesalahan itu hanya berjalan selamanya oleh url tes yang disediakan.
trbck

itu jelas muncul ketika saya menggunakan url di atas https. http bekerja dengan baik
trbck

Sepertinya sudah requests-threadsada sekarang.
OrangeDog

Jawaban:


154

Catatan

Jawaban di bawah ini tidak berlaku untuk permintaan v0.13.0 +. Fungsionalitas tidak sinkron dipindahkan ke grequests setelah pertanyaan ini ditulis. Namun, Anda bisa saja menggantinya requestsdengangrequests bawah ini dan itu akan berfungsi.

Saya telah meninggalkan jawaban ini untuk mencerminkan pertanyaan awal yang tentang menggunakan permintaan <v0.13.0.


Untuk melakukan banyak tugas dengan async.map asinkron Anda harus:

  1. Tetapkan fungsi untuk apa yang ingin Anda lakukan dengan setiap objek (tugas Anda)
  2. Tambahkan fungsi itu sebagai pengait acara dalam permintaan Anda
  3. Panggil async.mapdaftar semua permintaan / tindakan

Contoh:

from requests import async
# If using requests > v0.13.0, use
# from grequests import async

urls = [
    'http://python-requests.org',
    'http://httpbin.org',
    'http://python-guide.org',
    'http://kennethreitz.com'
]

# A simple task to do to each response object
def do_something(response):
    print response.url

# A list to hold our things to do via async
async_list = []

for u in urls:
    # The "hooks = {..." part is where you define what you want to do
    # 
    # Note the lack of parentheses following do_something, this is
    # because the response will be used as the first argument automatically
    action_item = async.get(u, hooks = {'response' : do_something})

    # Add the task to our list of things to do via async
    async_list.append(action_item)

# Do our list of things to do via async
async.map(async_list)

2
Ide bagus untuk meninggalkan komentar Anda: karena masalah kompatibilitas antara permintaan terbaru dan grequests (kurangnya opsi max_retries dalam permintaan 1.1.0) saya harus menurunkan versi permintaan untuk mengambil async dan saya telah menemukan bahwa fungsi asinkron dipindahkan dengan versi 0.13+ ( pypi.python.org/pypi/requests )
outforawhile

1
Pertanyaan bodoh: Berapakah peningkatan kecepatan penggunaan grequest sebagai lawan dari sekadar permintaan? Batas apa yang ada tentang permintaan? misalnya apakah menempatkan 3500 permintaan di async.map tidak masalah?
droope

10
from grequests import asyncjangan bekerja .. dan definisi kerja dosomething ini bagi saya def do_something(response, **kwargs):, saya menemukannya dari stackoverflow.com/questions/15594015/…
Allan Ruin

3
jika panggilan async.map masih memblokir, lalu bagaimana ini tidak sinkron? Selain permintaan itu sendiri yang dikirimkan secara tidak sinkron, pengambilan masih sinkron?
bryanph

3
Mengganti from requests import asyncdengan import grequests as asyncbekerja untuk saya.
Martin Thoma

80

asyncsekarang modul independen: grequests.

Lihat di sini: https://github.com/kennethreitz/grequests

Dan di sana: Metode ideal untuk mengirim beberapa permintaan HTTP melalui Python?

instalasi:

$ pip install grequests

pemakaian:

membangun tumpukan:

import grequests

urls = [
    'http://www.heroku.com',
    'http://tablib.org',
    'http://httpbin.org',
    'http://python-requests.org',
    'http://kennethreitz.com'
]

rs = (grequests.get(u) for u in urls)

kirim tumpukan

grequests.map(rs)

hasilnya terlihat seperti

[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]

grequests tampaknya tidak menetapkan batasan untuk permintaan bersamaan, yaitu ketika beberapa permintaan dikirim ke server yang sama.


11
Sehubungan dengan batasan pada permintaan bersamaan - Anda dapat menentukan ukuran kumpulan saat menjalankan peta () / imap (). yaitu grequests.map (rs, size = 20) untuk memiliki 20 pengambilan secara bersamaan.
synthesizerpatel

1
Sampai sekarang ini tidak mampu python3 (gevent gagal membangun v2.6 di py3.4).
saarp

1
Saya tidak begitu mengerti bagian async. jika saya membiarkan results = grequests.map(rs)kode setelah baris ini diblokir, saya dapat melihat efek async?
Allan Ruin

47

Saya menguji permintaan-futures dan grequest . Grequests lebih cepat tetapi membawa tambalan monyet dan masalah tambahan dengan dependensi. request-futures beberapa kali lebih lambat dari grequests. Saya memutuskan untuk menulis permintaan saya sendiri dan hanya membungkusnya ke dalam ThreadPoolExecutor dan itu hampir secepat grequest, tetapi tanpa ketergantungan eksternal.

import requests
import concurrent.futures

def get_urls():
    return ["url1","url2"]

def load_url(url, timeout):
    return requests.get(url, timeout = timeout)

with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

    future_to_url = {executor.submit(load_url, url, 10): url for url in     get_urls()}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            resp_err = resp_err + 1
        else:
            resp_ok = resp_ok + 1

Jenis pengecualian apa yang memungkinkan di sini?
Lambat Harry

requests.exceptions.Timeout
Hodza

2
Maaf saya tidak mengerti pertanyaan Anda. Gunakan hanya satu url di beberapa utas? Hanya satu kasus serangan DDoS))
Hodza

1
Saya tidak mengerti mengapa jawaban ini mendapat banyak upvotes. Pertanyaan OP adalah tentang permintaan async. ThreadPoolExecutor menjalankan utas. Ya, Anda dapat membuat permintaan di banyak utas, tetapi itu tidak akan pernah menjadi program async, jadi saya bagaimana itu bisa menjadi jawaban untuk pertanyaan awal?
nagylzs

1
Sebenarnya, pertanyaannya adalah tentang bagaimana memuat URL secara paralel. Dan ya pelaksana thread pool bukan pilihan terbaik, lebih baik menggunakan async io, tetapi bekerja dengan baik di Python. Dan saya tidak mengerti mengapa utas tidak dapat digunakan untuk async? Bagaimana jika Anda perlu menjalankan tugas yang terikat CPU secara tidak sinkron?
Hodza

29

mungkin permintaan-berjangka adalah pilihan lain.

from requests_futures.sessions import FuturesSession

session = FuturesSession()
# first request is started in background
future_one = session.get('http://httpbin.org/get')
# second requests is started immediately
future_two = session.get('http://httpbin.org/get?foo=bar')
# wait for the first request to complete, if it hasn't already
response_one = future_one.result()
print('response one status: {0}'.format(response_one.status_code))
print(response_one.content)
# wait for the second request to complete, if it hasn't already
response_two = future_two.result()
print('response two status: {0}'.format(response_two.status_code))
print(response_two.content)

Juga direkomendasikan dalam dokumen kantor . Jika Anda tidak ingin melibatkan gevent, itu bagus.


1
Salah satu solusi termudah. Jumlah permintaan bersamaan dapat ditingkatkan dengan mendefinisikan parameter max_workers
Jose Cherian

1
Akan lebih baik melihat contoh skala ini jadi kami tidak menggunakan satu nama variabel per item untuk mengulang.
user1717828

memiliki satu utas per permintaan adalah pemborosan sumber daya! tidak mungkin untuk melakukan misalnya 500 permintaan secara bersamaan, itu akan membunuh CPU Anda. ini seharusnya tidak dianggap solusi yang baik.
Corneliu Maftuleac

Poin bagus @CorneliuMaftuleac. Mengenai penggunaan utas, Anda pasti perlu mempedulikannya dan perpustakaan menyediakan opsi untuk mengaktifkan kolam threading atau kolam pemrosesan. ThreadPoolExecutor(max_workers=10)
Dreampuf

@Dreampuf pemrosesan pool saya percaya ini lebih buruk?
Corneliu Maftuleac

11

Saya memiliki banyak masalah dengan sebagian besar jawaban yang diposting - mereka baik menggunakan perpustakaan yang sudah usang yang telah porting dengan fitur terbatas, atau memberikan solusi dengan sihir terlalu banyak pada pelaksanaan permintaan, sehingga sulit untuk menangani kesalahan. Jika mereka tidak termasuk dalam salah satu kategori di atas, itu adalah perpustakaan pihak ketiga atau sudah usang.

Beberapa solusi berfungsi dengan baik dalam permintaan http, tetapi solusi tersebut tidak memenuhi semua jenis permintaan lainnya, yang menggelikan. Solusi yang sangat khusus tidak diperlukan di sini.

Cukup menggunakan python built-in library asynciocukup untuk melakukan permintaan asinkronik dari jenis apa pun, serta memberikan fluiditas yang cukup untuk penanganan kesalahan kompleks dan usecase spesifik.

import asyncio

loop = asyncio.get_event_loop()

def do_thing(params):
    async def get_rpc_info_and_do_chores(id):
        # do things
        response = perform_grpc_call(id)
        do_chores(response)

    async def get_httpapi_info_and_do_chores(id):
        # do things
        response = requests.get(URL)
        do_chores(response)

    async_tasks = []
    for element in list(params.list_of_things):
       async_tasks.append(loop.create_task(get_chan_info_and_do_chores(id)))
       async_tasks.append(loop.create_task(get_httpapi_info_and_do_chores(ch_id)))

    loop.run_until_complete(asyncio.gather(*async_tasks))

Cara kerjanya sederhana. Anda sedang membuat serangkaian tugas yang ingin Anda lakukan secara tidak sinkron, dan kemudian meminta loop untuk menjalankan tugas-tugas itu dan keluar setelah selesai. Tidak ada perpustakaan tambahan yang mengalami kekurangan pemeliharaan, tidak ada kekurangan fungsionalitas yang diperlukan.


2
Jika saya mengerti dengan benar, ini akan memblokir loop acara saat melakukan panggilan GRPC dan HTTP? Jadi, jika panggilan ini membutuhkan waktu beberapa detik untuk diselesaikan, seluruh putaran acara Anda akan diblokir selama beberapa detik? Untuk menghindarinya, Anda harus menggunakan perpustakaan GRPC atau HTTP async. Maka misalnya Anda bisa melakukannya await response = requests.get(URL). Tidak?
Coder Nr 23

Sayangnya, ketika mencoba ini, saya menemukan bahwa membuat pembungkus requestshampir tidak lebih cepat (dan dalam beberapa kasus lebih lambat) daripada hanya memanggil daftar URL secara serempak. Misalnya, meminta titik akhir yang membutuhkan 3 detik untuk merespons 10 kali menggunakan strategi di atas membutuhkan waktu sekitar 30 detik. Jika Anda ingin asynckinerja sejati , Anda perlu menggunakan sesuatu seperti aiohttp.
DragonBobZ

8

Saya tahu ini telah ditutup untuk sementara waktu, tetapi saya pikir mungkin berguna untuk mempromosikan solusi async lain yang dibangun di pustaka permintaan.

list_of_requests = ['http://moop.com', 'http://doop.com', ...]

from simple_requests import Requests
for response in Requests().swarm(list_of_requests):
    print response.content

Dokumen ada di sini: http://pythonhosted.org/simple-requests/


@YSY Jangan ragu untuk mengirim masalah: github.com/ctheiss/simple-requests/issues ; Saya benar-benar menggunakan perpustakaan ini ribuan kali sehari.
Monkey Boson

Boston, bagaimana Anda menangani kesalahan 404/500? bagaimana dengan https url? akan menghargai potongan yang mendukung ribuan url. bisakah Anda menempelkan contoh? terima kasih
YSY

@YSY Secara default 404/500 kesalahan menimbulkan pengecualian. Perilaku ini dapat ditimpa (lihat pythonhosted.org/simple-requests/… ). URL HTTPS rumit karena bergantung pada gevent, yang saat ini memiliki bug yang luar biasa pada hal ini ( github.com/gevent/gevent/issues/477 ). Ada shim di tiket yang dapat Anda jalankan, tetapi masih akan memberikan peringatan untuk server SNI (tetapi akan berfungsi). Sedangkan untuk snipping, saya khawatir semua penggunaan saya di perusahaan saya dan ditutup. Tapi saya yakinkan Anda kami menjalankan ribuan permintaan lebih dari puluhan pekerjaan.
Monkey Boson

Perpustakaan terlihat ramping sehubungan dengan interaksi. Apakah Python3 + dapat digunakan? Maaf tidak bisa melihat penyebutan.
Isaac Philip

@Jethro benar sekali, perpustakaan akan membutuhkan penulisan ulang total karena teknologi yang mendasari sangat berbeda dalam Python 3. Untuk saat ini, perpustakaan itu "lengkap" tetapi hanya berfungsi untuk Python 2.
Monkey Boson

4
threads=list()

for requestURI in requests:
    t = Thread(target=self.openURL, args=(requestURI,))
    t.start()
    threads.append(t)

for thread in threads:
    thread.join()

...

def openURL(self, requestURI):
    o = urllib2.urlopen(requestURI, timeout = 600)
    o...

4
ini adalah permintaan "normal" di utas. tidak buruk contohnya beli di luar topik.
Nick



2

Anda bisa menggunakannya httpxuntuk itu.

import httpx

async def get_async(url):
    async with httpx.AsyncClient() as client:
        return await client.get(url)

urls = ["http://google.com", "http://wikipedia.org"]

# Note that you need an async context to use `await`.
await asyncio.gather(*map(get_async, urls))

jika Anda ingin sintaks fungsional, gamla lib membungkusnya get_async.

Maka Anda bisa melakukannya


await gamla.map(gamla.get_async(10), ["http://google.com", "http://wikipedia.org"])

Ini 10adalah batas waktu dalam hitungan detik.

(penafian: saya penulisnya)


Dan respxuntuk mengejek / pengujian :)
rlat

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.