在Python中,我們可以使用requests
庫來傳送HTTP請求,並使用threading
、multiprocessing
、asyncio
(配合aiohttp
)或concurrent.futures
等庫來併發執行這些請求。這裡,我將為我們展示使用concurrent.futures.ThreadPoolExecutor
和requests
庫併發執行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.ThreadPoolExecutor
和requests
庫併發傳送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通常比多執行緒具有更好的效能,特別是在高併發的網路應用中。