GRequests: Asynchronous HTTP Requests with Gevent
grequests is a Python library that combines the popular `requests` library with `gevent` to enable easy asynchronous HTTP requests. It allows developers to send multiple HTTP requests concurrently, significantly reducing the time spent waiting for network I/O, particularly useful for tasks like web scraping and interacting with multiple API endpoints. The library's current version is 0.7.0, and while its release cadence is not strictly regular, it receives updates for compatibility and new features.
Warnings
- gotcha Import `grequests` (and thus `gevent`) before `requests` or other networking libraries that might conflict with `gevent`'s monkey-patching. Incorrect import order can lead to unexpected blocking behavior or errors.
- gotcha When using `grequests.map()`, failed requests (e.g., due to timeouts or connection errors) will result in `None` being present in the returned list of responses. You must explicitly handle these `None` values.
- gotcha `grequests.imap()` returns a generator of responses, and the order in which responses are yielded is arbitrary; it does not correspond to the order of the input requests. This differs from `grequests.map()` which maintains order.
- gotcha Making a large number of concurrent requests without controlling the pool size (`size` parameter) can lead to 'Too many open files' errors, rate limiting by target servers, or servers closing connections prematurely.
- breaking The behavior of `grequests.imap` changed in version 0.4.0. While not explicitly detailed as a breaking change in releases, 'behavior changes' suggest that code relying on previous `imap` functionality might need adjustments.
- deprecated The underlying `gevent` library has deprecated and removed support for older Python versions (e.g., Python 2.7, 3.6, and soon 3.9). While `grequests` itself doesn't always specify Python requirements, its compatibility is bound by `gevent`'s support.
Install
-
pip install grequests
Imports
- grequests
import grequests
- grequests.map
responses = grequests.map(requests_list)
- grequests.imap
for response in grequests.imap(requests_list):
- grequests.imap_enumerated
for index, response in grequests.imap_enumerated(requests_list):
Quickstart
import grequests
import time
urls = [
'http://httpbin.org/delay/1',
'http://httpbin.org/status/200',
'http://httpbin.org/delay/3',
'http://httpbin.org/status/500'
]
def exception_handler(request, exception):
print(f"Request to {request.url} failed: {exception}")
start_time = time.time()
# Create a list of unsent Request objects
reqs = (
grequests.get(u, timeout=2) for u in urls
)
# Send all requests concurrently using map
# Responses will be in the same order as requests, with None for failed ones
responses = grequests.map(reqs, exception_handler=exception_handler, size=5)
print(f"\n--- Responses (using map, took {time.time() - start_time:.2f}s) ---")
for response in responses:
if response:
print(f"URL: {response.url}, Status: {response.status_code}")
else:
print("Request failed or timed out.")
print("\n--- Responses (using imap_enumerated) ---")
# imap_enumerated yields (index, response) and includes Nones for failures
reqs_for_imap_enumerated = (
grequests.get(u, timeout=2) for u in urls
)
for index, response in grequests.imap_enumerated(reqs_for_imap_enumerated, exception_handler=exception_handler, size=5):
if response:
print(f"Index: {index}, URL: {response.url}, Status: {response.status_code}")
else:
print(f"Index: {index}, Request failed or timed out.")