ฉันลองตัวอย่างที่ให้ไว้ในเอกสารประกอบของไลบรารีคำขอสำหรับหลาม
ด้วยasync.map(rs)
ฉันได้รับรหัสตอบกลับ แต่ฉันต้องการรับเนื้อหาของแต่ละหน้าที่ร้องขอ เช่นนี้ไม่ทำงาน:
out = async.map(rs)
print out[0].content
requests-threads
มีอยู่ตอนนี้
ฉันลองตัวอย่างที่ให้ไว้ในเอกสารประกอบของไลบรารีคำขอสำหรับหลาม
ด้วยasync.map(rs)
ฉันได้รับรหัสตอบกลับ แต่ฉันต้องการรับเนื้อหาของแต่ละหน้าที่ร้องขอ เช่นนี้ไม่ทำงาน:
out = async.map(rs)
print out[0].content
requests-threads
มีอยู่ตอนนี้
คำตอบ:
คำตอบด้านล่างไม่สามารถใช้ได้กับคำขอ v0.13.0 + ฟังก์ชันการทำงานแบบอะซิงโครนัสถูกย้ายไปที่grequestsหลังจากเขียนคำถามนี้ อย่างไรก็ตามคุณสามารถแทนที่requests
ด้วยgrequests
ด้านล่างและควรใช้งานได้
ฉันได้ทิ้งคำตอบนี้ไว้เพื่อสะท้อนถึงคำถามเดิมซึ่งเกี่ยวกับการใช้คำขอ <v0.13.0
ในการทำงานหลายอย่างด้วยasync.map
อะซิงโครนัสคุณต้อง:
async.map
รายการคำขอ / การกระทำทั้งหมดตัวอย่าง:
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)
from grequests import async
ไม่ทำงาน .. และคำจำกัดความของ dosomething นี้ทำงานได้สำหรับฉันdef do_something(response, **kwargs):
ฉันพบได้จากstackoverflow.com/questions/15594015/ …
from requests import async
ด้วยการimport grequests as async
ทำงานสำหรับฉัน
async
grequests
คือตอนนี้โมดูลอิสระ
ดูที่นี่: https://github.com/kennethreitz/grequests
และมีวิธีที่เหมาะสำหรับการส่งคำขอ HTTP หลายรายการผ่าน Python หรือไม่
$ pip install grequests
สร้างสแต็ค:
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)
ส่งสแต็ก
grequests.map(rs)
ผลดูเหมือนว่า
[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]
grequests ดูเหมือนจะไม่ได้ตั้งข้อ จำกัด สำหรับคำขอที่เกิดขึ้นพร้อมกันเช่นเมื่อมีการส่งคำขอจำนวนมากไปยังเซิร์ฟเวอร์เดียวกัน
results = grequests.map(rs)
โค้ดหลังจากบรรทัดนี้ถูกบล็อกฉันจะเห็นเอฟเฟกต์ async ได้หรือไม่
ผมทดสอบทั้งคำขอฟิวเจอร์สและgrequests Grequests เร็วขึ้น แต่นำการปะของลิงและปัญหาเพิ่มเติมเกี่ยวกับการพึ่งพา การร้องขอ - ฟิวเจอร์ช้ากว่า grequests หลายครั้ง ฉันตัดสินใจที่จะเขียนของฉันเองและเพียงแค่ห่อคำขอไปยังThreadPoolExecutorและมันก็เกือบจะเร็วเท่ากับ grequests แต่ไม่มีการพึ่งพาจากภายนอก
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
บางทีการร้องขอล่วงหน้าเป็นทางเลือกอื่น
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)
มันเป็นยังแนะนำในเอกสารสำนักงาน หากคุณไม่ต้องการมีส่วนร่วมกับ gevent มันเป็นสิ่งที่ดี
ThreadPoolExecutor(max_workers=10)
ฉันมีปัญหามากมายเกี่ยวกับคำตอบส่วนใหญ่ที่โพสต์ - พวกเขาอาจใช้ห้องสมุดที่เลิกใช้แล้วซึ่งได้รับการย้ายข้อมูลด้วยคุณสมบัติที่ จำกัด หรือเสนอวิธีแก้ปัญหาด้วยเวทมนตร์ที่มากเกินไปในการดำเนินการตามคำขอทำให้ยากที่จะจัดการข้อผิดพลาด หากพวกเขาไม่ได้อยู่ในหมวดหมู่ใดหมวดหนึ่งข้างต้นพวกเขาเป็นห้องสมุดบุคคลที่สามหรือเลิกใช้
โซลูชันบางตัวทำงานได้อย่างหมดจดในคำขอ HTTP แต่โซลูชันนั้นสั้นเกินไปสำหรับคำขอประเภทอื่น ๆ ซึ่งน่าหัวเราะ โซลูชันที่กำหนดเองสูงไม่จำเป็นที่นี่
เพียงใช้ไลบรารี่ในตัวของไพ ธ อนasyncio
ก็เพียงพอที่จะดำเนินการตามคำขอแบบอะซิงโครนัสทุกประเภทรวมถึงให้การไหลที่เพียงพอสำหรับการจัดการข้อผิดพลาดที่ซับซ้อน
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))
มันทำงานอย่างไรง่าย คุณกำลังสร้างชุดของงานที่คุณต้องการให้เกิดขึ้นแบบอะซิงโครนัสแล้วขอให้วงวนเพื่อดำเนินงานเหล่านั้นและออกเมื่อเสร็จสิ้น ไม่มีไลบรารีเพิ่มเติมที่ต้องขาดการบำรุงรักษาไม่จำเป็นต้องใช้ฟังก์ชัน
async
ห้องสมุดที่มี await response = requests.get(URL)
จากนั้นคุณสามารถทำเช่น ไม่มี?
requests
นั้นเร็วขึ้น (และในบางกรณีช้ากว่า) เพียงแค่เรียกรายการ URL พร้อมกัน เช่นการขอปลายทางที่ใช้เวลา 3 วินาทีในการตอบสนอง 10 ครั้งโดยใช้กลยุทธ์ด้านบนใช้เวลาประมาณ 30 วินาที หากคุณต้องการที่แท้จริงของประสิทธิภาพการทำงานของคุณจะต้องใช้สิ่งที่ต้องการasync
aiohttp
run_until_complete
- นี่คือเว้นแต่คุณจะใช้โมดูลเธรดซึ่งมอบหมาย async ให้กับเลเยอร์ OS อ่านเพิ่มเติมเกี่ยวกับปัญหา GIL ใน python สำหรับข้อมูลเพิ่มเติม
requests.get
ก็ตาม แต่คำถามคือทำอย่างไรกับคำขอแบบอะซิงโครนัสกับrequests
ห้องสมุดหลาม คำตอบนี้ไม่ได้เป็นอย่างนั้น
ฉันรู้ว่านี่ถูกปิดไประยะหนึ่งแล้ว แต่ฉันคิดว่ามันอาจมีประโยชน์ในการโปรโมตโซลูชัน async อื่นที่สร้างขึ้นในไลบรารีคำขอ
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
เอกสารอยู่ที่นี่: http://pythonhosted.org/simple-requests/
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...
หากคุณต้องการใช้ asyncio คุณrequests-async
จะได้รับฟังก์ชั่น async / await สำหรับrequests
- https://github.com/encode/requests-async
ฉันได้ใช้การร้องขอของ python สำหรับการโทรแบบ async กับ gistub ของ github เป็นระยะเวลาหนึ่ง
ตัวอย่างเช่นดูรหัสที่นี่:
https://github.com/davidthewatson/flasgist/blob/master/views.py#L60-72
ไพ ธ อนรูปแบบนี้อาจไม่ใช่ตัวอย่างที่ชัดเจน แต่ฉันรับรองได้ว่าโค้ดใช้งานได้ แจ้งให้เราทราบหากสิ่งนี้ทำให้คุณสับสนและฉันจะบันทึกไว้
คุณสามารถใช้httpx
สำหรับการที่
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))
ถ้าคุณต้องการไวยากรณ์การทำงาน, gamla lib จะรวมสิ่งนี้เข้าget_async
ไว้
จากนั้นคุณสามารถทำได้
await gamla.map(gamla.get_async(10), ["http://google.com", "http://wikipedia.org"])
10
คือหมดเวลาในไม่กี่วินาที
(ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน)
ฉันได้ลองบางสิ่งโดยใช้วิธีการแบบอะซิงโครนัสในงูใหญ่ฉันเคยโชคดีกว่านี้มาก่อนโดยใช้การบิดสำหรับการเขียนโปรแกรมแบบอะซิงโครนัส มันมีปัญหาน้อยลงและมีการบันทึกไว้อย่างดี นี่คือลิงค์ของสิ่งที่คล้ายกันกับสิ่งที่คุณพยายามบิด
http://pythonquirks.blogspot.com/2011/04/twisted-asynchronous-http-request.html
โชคไม่ดีเท่าที่ฉันรู้ห้องสมุดการร้องขอไม่ได้มีไว้สำหรับดำเนินการตามคำขอแบบอะซิงโครนัส คุณสามารถasync/await
ล้อมรอบไวยากรณ์ได้requests
แต่จะทำให้คำร้องขอพื้นฐานไม่ซิงโครนัสน้อยลง หากคุณต้องการคำขอ async จริงคุณต้องใช้เครื่องมืออื่น ๆ ที่ให้มา วิธีแก้ปัญหาหนึ่งอย่างคือaiohttp
(Python 3.5.3+) มันทำงานได้ดีในประสบการณ์ของฉันใช้กับasync/await
ไวยากรณ์Python 3.7 ด้านล่างฉันเขียนการใช้งานสามอย่างของการดำเนินการคำขอทางเว็บโดยใช้
sync_requests_get_all
) โดยใช้requests
ไลบรารีPythonasync_requests_get_all
) โดยใช้requests
ไลบรารีPython ที่หุ้มด้วย Python 3.7 async/await
และasyncio
async_aiohttp_get_all
) ด้วยaiohttp
ไลบรารีPython ที่หุ้มด้วยasync/await
ไวยากรณ์Python 3.7 และasyncio
import time
import asyncio
import requests
import aiohttp
from types import SimpleNamespace
durations = []
def timed(func):
"""
records approximate durations of function calls
"""
def wrapper(*args, **kwargs):
start = time.time()
print(f'{func.__name__:<30} started')
result = func(*args, **kwargs)
duration = f'{func.__name__:<30} finsished in {time.time() - start:.2f} seconds'
print(duration)
durations.append(duration)
return result
return wrapper
async def fetch(url, session):
"""
asynchronous get request
"""
async with session.get(url) as response:
response_json = await response.json()
return SimpleNamespace(**response_json)
async def fetch_many(loop, urls):
"""
many asynchronous get requests, gathered
"""
async with aiohttp.ClientSession() as session:
tasks = [loop.create_task(fetch(url, session)) for url in urls]
return await asyncio.gather(*tasks)
@timed
def asnyc_aiohttp_get_all(urls):
"""
performs asynchronous get requests
"""
loop = asyncio.get_event_loop()
return loop.run_until_complete(fetch_many(loop, urls))
@timed
def sync_requests_get_all(urls):
"""
performs synchronous get requests
"""
# use session to reduce network overhead
session = requests.Session()
return [SimpleNamespace(**session.get(url).json()) for url in urls]
@timed
def async_requests_get_all(urls):
"""
asynchronous wrapper around synchronous requests
"""
loop = asyncio.get_event_loop()
# use session to reduce network overhead
session = requests.Session()
async def async_get(url):
return session.get(url)
async_tasks = [loop.create_task(async_get(url)) for url in urls]
return loop.run_until_complete(asyncio.gather(*async_tasks))
if __name__ == '__main__':
# this endpoint takes ~3 seconds to respond,
# so a purely synchronous implementation should take
# little more than 30 seconds and a purely asynchronous
# implementation should take little more than 3 seconds.
urls = ['https://postman-echo.com/delay/3']*10
sync_requests_get_all(urls)
async_requests_get_all(urls)
asnyc_aiohttp_get_all(urls)
print('----------------------')
[print(duration) for duration in durations]
บนเครื่องของฉันนี่คือผลลัพธ์:
sync_requests_get_all started
sync_requests_get_all finsished in 30.92 seconds
async_requests_get_all started
async_requests_get_all finsished in 30.87 seconds
asnyc_aiohttp_get_all started
asnyc_aiohttp_get_all finsished in 3.22 seconds
----------------------
sync_requests_get_all finsished in 30.92 seconds
async_requests_get_all finsished in 30.87 seconds
asnyc_aiohttp_get_all finsished in 3.22 seconds