写点什么

python 并发执行 request 请求

  • 2024-06-27
    福建
  • 本文字数:3934 字

    阅读完需:约 13 分钟

在 Python 中,我们可以使用requests库来发送 HTTP 请求,并使用threadingmultiprocessingasyncio(配合aiohttp)或concurrent.futures等库来并发执行这些请求。这里,我将为我们展示使用concurrent.futures.ThreadPoolExecutorrequests库并发执行 HTTP 请求的示例。


1.使用concurrent.futures.ThreadPoolExecutor并发发送请求示例


首先,我们需要安装requests库(如果还没有安装的话):

bash复制代码
pip install requests
复制代码


然后,我们可以使用以下代码来并发地发送 HTTP GET 请求:

import concurrent.futures  import requests    # 假设我们有一个URL列表  urls = [      'http://example.com/api/data1',      'http://example.com/api/data2',      'http://example.com/api/data3',      # ... 添加更多URL  ]    # 定义一个函数,该函数接收一个URL,发送GET请求,并打印响应内容  def fetch_data(url):      try:          response = requests.get(url)          response.raise_for_status()  # 如果请求失败(例如,4xx、5xx),则抛出HTTPError异常          print(f"URL: {url}, Status Code: {response.status_code}, Content: {response.text[:100]}...")      except requests.RequestException as e:          print(f"Error fetching {url}: {e}")    # 使用ThreadPoolExecutor并发地执行fetch_data函数  with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:  # 你可以根据需要调整max_workers的值      future_to_url = {executor.submit(fetch_data, url): url for url in urls}      for future in concurrent.futures.as_completed(future_to_url):          url = future_to_url[future]          try:              # 通过调用future.result()来获取函数的返回值,这会阻塞,直到结果可用              # 但是请注意,这里我们只是打印结果,没有返回值,所以调用future.result()只是为了等待函数完成              future.result()          except Exception as exc:              print(f'Generated an exception for {url}: {exc}')
复制代码


在这里简单解释一下这个代码示例。


(1)我们首先定义了一个 URL 列表,这些是我们想要并发访问的 URL。

(2)然后,我们定义了一个函数fetch_data,它接收一个 URL 作为参数,发送 GET 请求,并打印响应的状态码和内容(只打印前 100 个字符以节省空间)。如果发生任何请求异常(例如,网络错误、无效的 URL、服务器错误等),它会捕获这些异常并打印错误消息。

(3)使用concurrent.futures.ThreadPoolExecutor,我们可以轻松地并发执行fetch_data函数。我们创建了一个ThreadPoolExecutor实例,并指定了最大工作线程数(在这个例子中是 5,但我们可以根据需要调整这个值)。然后,我们使用列表推导式将每个 URL 与一个Future对象关联起来,该对象表示异步执行的函数。

(4)最后,我们使用as_completed函数迭代所有完成的Future对象。对于每个完成的Future对象,我们调用result方法来获取函数的返回值(尽管在这个例子中我们没有使用返回值)。如果函数执行期间发生任何异常,result方法会重新引发该异常,我们可以捕获并处理它。


这个示例展示了如何使用 Python 的concurrent.futures模块来并发地发送 HTTP 请求。这种方法在 IO 密集型任务(如网络请求)上特别有效,因为它允许在等待 IO 操作完成时释放 CPU 资源供其他线程使用。


2.requests 库并发发送 HTTP GET 请求的完整 Python 代码示例


以下是一个使用concurrent.futures.ThreadPoolExecutorrequests库并发发送 HTTP GET 请求的完整 Python 代码示例:

import concurrent.futures  import requests    # 假设我们有一个URL列表  urls = [      'https://www.example.com',      'https://httpbin.org/get',      'https://api.example.com/some/endpoint',      # ... 添加更多URL  ]    # 定义一个函数来发送GET请求并处理响应  def fetch_url(url):      try:          response = requests.get(url, timeout=5)  # 设置超时为5秒          response.raise_for_status()  # 如果请求失败,抛出HTTPError异常          return response.text  # 返回响应内容,这里只是作为示例,实际使用中可能不需要返回      except requests.RequestException as e:          print(f"Error fetching {url}: {e}")          return None    # 使用ThreadPoolExecutor并发地发送请求  def fetch_all_urls(urls):      with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:          # 使用executor.map来自动处理迭代和Future的获取          results = executor.map(fetch_url, urls)        # 处理结果(这里只是简单地打印出来)      for result in results:          if result is not None:              print(f"Fetched content from a URL (truncated): {result[:100]}...")    # 调用函数  fetch_all_urls(urls)
复制代码


在这个示例中,我们定义了一个fetch_url函数,它接收一个 URL,发送 GET 请求,并返回响应内容(或在出错时返回None)。然后,我们定义了一个fetch_all_urls函数,它使用ThreadPoolExecutor并发地调用fetch_url函数,并将结果收集在一个迭代器中。最后,我们遍历这个迭代器,并打印出每个成功获取到的响应内容(这里只打印了前 100 个字符作为示例)。


请注意,我们在requests.get中设置了一个超时参数(timeout=5),这是为了防止某个请求因为网络问题或其他原因而无限期地等待。在实际应用中,根据我们的需求调整这个值是很重要的。


此外,我们还使用了executor.map来自动处理迭代和Future的获取。executor.map函数会返回一个迭代器,它会产生fetch_url函数的返回值,这些值在函数完成后会自动从相应的Future对象中提取出来。这使得代码更加简洁,并且减少了显式处理Future对象的需要。


3.如何在 Python 中实现并发编程


在 Python 中实现并发编程,主要有以下几种方式:


(1)使用threading模块threading模块提供了多线程编程的 API。Python 的线程是全局解释器锁(GIL)下的线程,这意味着在任意时刻只有一个线程能够执行 Python 字节码。然而,对于 I/O 密集型任务(如网络请求),多线程仍然可以通过并发地等待 I/O 操作来提高性能。


示例:

import threading  import requests   def fetch_url(url):      try:          response = requests.get(url)          response.raise_for_status()          print(f"URL: {url}, Status Code: {response.status_code}")      except requests.RequestException as e:          print(f"Error fetching {url}: {e}")   threads = []  for url in urls:      t = threading.Thread(target=fetch_url, args=(url,))      threads.append(t)      t.start()   # 等待所有线程完成  for t in threads:      t.join()
复制代码


(2)使用multiprocessing模块


multiprocessing模块提供了跨多个 Python 解释器的进程间并行处理。这对于 CPU 密集型任务特别有用,因为每个进程都有自己的 Python 解释器和 GIL,可以充分利用多核 CPU 的并行处理能力。


示例:

from multiprocessing import Pool  import requests   def fetch_url(url):      try:          response = requests.get(url)          response.raise_for_status()          return f"URL: {url}, Status Code: {response.status_code}"      except requests.RequestException as e:          return f"Error fetching {url}: {e}"   with Pool(processes=4) as pool:  # 设定进程池的大小      results = pool.map(fetch_url, urls)   for result in results:      print(result)
复制代码


(3)使用asyncio模块(针对异步 I/O)


asyncio是 Python 3.4+中引入的用于编写单线程并发代码的库,特别适合编写网络客户端和服务器。它使用协程(coroutine)和事件循环(event loop)来管理并发。


示例(使用aiohttp库进行异步 HTTP 请求):

import asyncio  import aiohttp   async def fetch_url(url, session):      async with session.get(url) as response:          return await response.text()   async def main():      async with aiohttp.ClientSession() as session:          tasks = []          for url in urls:              task = asyncio.create_task(fetch_url(url, session))              tasks.append(task)           results = await asyncio.gather(*tasks)          for result, url in zip(results, urls):              print(f"URL: {url}, Content: {result[:100]}...")   # Python 3.7+ 可以使用下面的方式运行主协程  asyncio.run(main())
复制代码


注意:asyncio.run()是在 Python 3.7 中引入的,用于运行顶层入口点函数。在 Python 3.6 及以下版本中,我们需要自己设置和运行事件循环。


(4)使用concurrent.futures模块


concurrent.futures模块提供了高层次的接口,可以轻松地编写并发代码。它提供了ThreadPoolExecutor(用于线程池)和ProcessPoolExecutor(用于进程池)。


前面已经给出了ThreadPoolExecutor的示例,这里不再重复。ProcessPoolExecutor的用法与ThreadPoolExecutor类似,只是它是基于进程的。


选择哪种并发方式取决于我们的具体需求。对于 I/O 密集型任务,多线程或异步 I/O 通常是更好的选择;对于 CPU 密集型任务,多进程可能是更好的选择。此外,异步 I/O 通常比多线程具有更好的性能,特别是在高并发的网络应用中。


文章转载自:TechSynapse

原文链接:https://www.cnblogs.com/TS86/p/18268776

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
python并发执行request请求_Python_不在线第一只蜗牛_InfoQ写作社区