1. 程式人生 > 實用技巧 >Python非同步Request操作: aiohttp

Python非同步Request操作: aiohttp

目錄

1. Tutorial

homepage

cnblog: aiohttp的使用

2. 其他庫推薦

2.1. aiohttp-requests

這個庫時對aiohttp庫的網路請求模組的封裝,用了這個庫,在非同步網路請求的時候,可以在寫法上更簡潔易懂。本質上還是aiohttp庫的使用。推薦使用這個庫來做網路請求。

2.2. aiofiles

aiofiles是一個用Python編寫,用於處理asyncio應用程式中的本地磁碟檔案。爬蟲過程中用它來進行檔案的非同步操作。

2.3. grequests

grequests模組相當於是封裝了gevent的requests模組。

3. 問題記錄

3.1. Multipart.FormData 示例

下面示例展示上傳圖片至SM.MS。

with open(abspath_file, 'rb') as fp:
    multipart_form_data = aiohttp.FormData(quote_fields=False)  # quote_fields: 將對中文進行轉碼
    multipart_form_data.add_field('smfile', fp,
                                  content_type="image/jpeg",
                                  filename=os.path.basename(relpath_file),
                                  content_transfer_encoding="base64")

    headers = {'Authorization': self.api_token} if self.api_token else None
    # headers = {"Content-Type": "multipart/form-data"}
    async with aiohttp.ClientSession() as session:
        async with session.post(self.endpoint,
                                data=multipart_form_data,
                                headers=headers) as resp:
            await resp.text()
            str_response = await resp.text()

            json_content = json.loads(str_response)
            if not json_content['success']:
                logger.error(json_content)
                raise UploadError()

            print(f"[+] 完成上傳: {relpath_file}")

3.2. with open("xxx") 會被自動關閉

程式是這樣的:

with open("xxx", "rb") as fp:
    ...
    async with aiohttp.ClientSession() as session:
        async with session.post(self.endpoint, data=fp) as resp:
            await resp.text()
            ...

    file_hash = hashlib.md5()
    while chunk := fp.read(8192):  # 這裡報錯:ValueError: read of closed file
        file_hash.update(chunk)
    return file_hash.hexdigest()

報錯:ValueError: read of closed file

找到一篇相似的文章,解釋不保證準確:

問題是open(...)返回一個檔案物件,並且您要將同一檔案物件傳遞給要start()在頂層建立的所有協程。恰好先排程的協程例項將檔案物件session.post()作為的一部分傳輸data,session.post()並將讀取檔案到最後並關閉檔案物件。下一個start()協程將嘗試從現在關閉的物件中讀取,這將引發異常。

要解決此問題而不多次開啟檔案,您需要確保實際將資料作為位元組物件讀取:

data = {'file': open('test_img.jpg', 'rb').read()}

這會將相同的位元組物件傳遞給所有協程,它們應按預期工作。

3.3. filename中文錯誤

使用post方式,上傳multipart到SM.MS時,影象儲存沒問題,但檔名從中文變成了諸如 %E9%B2%8D%E9%B.jpg 的樣子……應該是編碼問題。怎麼避免呢?

multipart_form_data = aiohttp.FormData(quote_fields=False)  # quote_fields: 將對中文進行轉碼

使用引數 quote_fields 將避免該問題。

3.4. aiohttp(yarl)對url部分字元自動urldecode

csdn

github

最新碰到一個用 aiohttp 訪問不出內容,但是用 requests 能訪問的情況,url 是事先進行了 urlencode 的, 下面的 url 隨便找了個站點代替,但是把重點的引數提了出來

%40 對應的是 `@`
%3a 對應的是 `:`

解決方案:

str_url = "https://www.xxx.com?xxx%40yyy%3azzz"
proxy_url = "http://localhost:8080"

async with session.get(URL(str_url), proxy=proxy_url) as resp:
    print(await resp.text())

async with session.get(URL(str_url, encoded=True), proxy=proxy_url) as resp:
    print(await resp.text())