python ahttp:簡單、高效、異步requests請求模塊

ahttp:簡單、高效、異步requests請求模塊

ahttp 是一個所有的http連接請求均使用協程的方式。 使請求過程中 IO 操作交給其他硬件,而CPU專注於處理計算型任務,可以大量的節約等待的時間。

適用版本: PYTHON 3.7

快速開始

安裝

你可以通過以下方式快速安裝:

pip install ahttp

單個請求

使用是非常簡單的:

import ahttp 

url = "http://httpbin.org/headers" 

構造一個get請求對象:

req = ahttp.get(url)

執行請求:

res = res.run()

打印出請求得到的文本:

print(res.text)

如果你使用過requests,那麼ahttp的使用方式基本上和它一致,只不過requests請求是同步,而ahttp的請求是異步。不同的是requests可以直接請求,而由於ahttp是異步的,所以需要構造好請求之後進行一次“執行”

多個請求

單個請求的時候,ahttp的異步是無法體現出來的,多個請求的時候則能很好的體現異步的不同和快速

構造一些請求(這裏以獲取 豆瓣電影排行250 爲例):

urls = [ f"https://movie.douban.com/top250?start={i*25}" for i in range(10) ]
reqs = [ahttp.get(url) for url in urls]

這裏的reqs裏存放了10個請求對象,但是還未執行請求,通過ahttp.run(reqs)執行這些請求:

resps = ahttp.run(reqs)

運行上面命令後,這10個請求會以異步的形式執行,打印執行結果:

print(resps)

#輸出結果如下:
[<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=0]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=25]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=75]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=50]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=100]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=125]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=150]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=175]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=200]>, 
<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=225]>]

查看第一個請求的html:

print(resps[0].text)

多個請求(使用session)

和使用ahttp構造請求list請求相比,使用session請求速度更快,而且共享cookies,因爲session創建的是一個持久的鏈接:

urls = [ f"https://movie.douban.com/top250?start={i*25}" for i in range(10) ]
sess = ahttp.Session()
reqs = [sess.get(url) for url in urls]

resps = ahttp.run(reqs)
print(resps)

輸出結果如下:

[<AhttpResponse status[200] url=[https://movie.douban.com/top250?start=25]>,
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=50]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=75]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=0]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=100]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=125]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=150]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=175]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=225]>, 
 <AhttpResponse status[200] url=[https://movie.douban.com/top250?start=200]>]

有序返回

reqs列表完成請求之後,得到的是一個resps列表。由於是異步請求,所以得到的resps並不是按照reqs請求的順序排列的。豆瓣排行我們需要按照順序處理,只需要在ahttp.run添加一個參數order

resps = ahttp.run(reqs, order=True)

提取文本

ahttp內置了使用requests_html來處理文本,使文本處理非常的簡單
例如提取第一個鏈接中的電影title:

resp = resps[0]
titles =[i[0] for i in resp.html.search_all('<span class="title">{}</span>')]

得到的結果如下:

['蝙蝠俠:黑暗騎士', '&nbsp;/&nbsp;The Dark Knight', '活着', '控方證人', 
'&nbsp;/&nbsp;Witness for the Prosecution', '亂世佳人', '&nbsp;/&nbsp;Gone with the Wind', '少年派的奇幻漂流',
 '&nbsp;/&nbsp;Life of Pi', '摔跤吧!爸爸', '&nbsp;/&nbsp;Dangal', '指環王3:王者無敵', 
 '&nbsp;/&nbsp;The Lord of the Rings: The Return of the King', '飛屋環遊記', 
 '&nbsp;/&nbsp;Up', '鬼子來了', '十二怒漢', '&nbsp;/&nbsp;12 Angry Men', '天空之城', 
 '&nbsp;/&nbsp;天空の城ラピュタ', '天堂電影院', '&nbsp;/&nbsp;Nuovo Cinema Paradiso', '尋夢環遊記', '&nbsp;/&nbsp;Coco', '大話西遊之月光寶盒',
  '&nbsp;/&nbsp;西遊記第壹佰零壹回之月光寶盒', '末代皇帝', '&nbsp;/&nbsp;The Last Emperor', '哈爾的移動城堡', '&nbsp;/&nbsp;ハウルの動く城',
   '羅馬假日', '&nbsp;/&nbsp;Roman Holiday', '搏擊俱樂部', '&nbsp;/&nbsp;Fight Club',
    '聞香識女人', '&nbsp;/&nbsp;Scent of a Woman', '素媛', '&nbsp;/&nbsp;소원', '辯護人', 
    '&nbsp;/&nbsp;변호인', '竊聽風暴', '&nbsp;/&nbsp;Das Leben der Anderen', '何以爲家', 
    '&nbsp;/&nbsp;كفرناحوم', '死亡詩社', '&nbsp;/&nbsp;Dead Poets Society', '兩杆大煙槍', 
    '&nbsp;/&nbsp;Lock, Stock and Two Smoking Barrels']

有關更多文本處理,請參考 requests_html 文本處理

基本使用

發送請求

  • 導入模塊

      import ahttp
    
  • 把需要請求的連接添加到任務中

      urls = [
          'http://www.heroku.com',
          'http://python-tablib.org',
          'http://httpbin.org',
          'http://python-requests.org',
          'http://fakedomain/',
          'http://kennethreitz.com'
      ]
    
      tasks=(ahttp.get(i) for i in urls)
    
  • 或者,你可能需要構建這樣的一組任務

      tasks=[ahttp.get(url1), ahttp.post(url2, data=data), ahttp.post(url3), ahttp.get(url4)]
    
  • 然後,送他們去工作

      ahttp.run(tasks)
    
  • 除了 POST 請求和 GET 請求之外,你也可以發送其他請求

      ahttp.put('http://httpbin.org/put', data=b'data')
      ahttp.delete('http://httpbin.org/delete')
      ahttp.head('http://httpbin.org/get')
      ahttp.options('http://httpbin.org/get')
      ahttp.patch('http://httpbin.org/patch', data=b'data')
    

響應內容

  • results = ahttp.run(tasks) 會讓我們得到響應對象的集合

      [<AhttpResponse [status 200]>, <AhttpResponse [status 200]>, <AhttpResponse [status 200]>, <AhttpResponse [status 200]>, None, <AhttpResponse [status 200]>]
    
  • 我們現在取出其中一個響應的對象

      resp = results[0]
    
  • 查看這個對象是哪個連接響應來的

      resp.url
    
  • 查看響應的請求方式(POST, GET或者其他)

      resp.method
    
  • 查看字節形式的響應文本

      resp.content
    
  • 查看字符串形式響應的文本

      resp.text
    
  • 查看響應Cookies

      resp.cookies
    
  • 查看響應狀態碼

      resp.status
    
  • 查看響應頭

      resp.headers
    
  • 如果你對 aiohttp 這個協程http庫比較熟悉,那麼你也可以通過獲得其原生的響應對象來獲得想要的內容,原生的響應對象的方法和屬性具體可參照 aiohttp 的官方文檔

      resp.raw #返回原生響應對象
    

在URL中傳遞參數

你可以通過以下幾種方式給URL傳遞參數

  • dict傳參

      params1 = {'key1': 'value1', 'key2': 'value2'} 
      params2 = [('key', 'value1'), ('key', 'value2')]
    
      tasks=[ahttp.get(url, params = params1), ahttp.get(url, params = params2)]
    
  • POST請求傳遞 json

      import json
    
      params = {'key1': 'value1', 'key2': 'value2'}
      tasks=[ahttp.get(url, params = json.dumps(params)), ]
    

POST數據的幾種方式

  • 模擬表單提交

      data = {'key1': 'value1', 'key2': 'value2'}
      
      tasks=[ahttp.post(url, data = data)]
    
  • POST json 數據

      import json
    
      data = {'key1': 'value1', 'key2': 'value2'}
      tasks=[ahttp.get(url, json=data)
    
  • POST 文件和預壓縮數據,請參考我的另一篇博客

      http://blog.csdn.net/getcomputerstyle/article/details/71515331
    

使用 Session

使用session後,將使用keep-alive,能夠很大大大程度上加快連接的速度

  • 你可以在創建任務之前,創建一個攜帶session的ahttp對象

      sess = ahttp.Session()
    
  • 然後一切又和以前一樣, 創建一個任務

      tasks = (sess.get(i) for i in urls)
    
  • 使用的同一個sess 則是使用的同一個session,它們有着共同的Cookie,不同的session請求對象也可以一起發出請求

      sess1 = ahttp.Session()
      sess2 = ahttp.Session()
      ...
      task1 = [sess1.get(i) for i in urls1]
      task2 = [sess2.post(i) for i in urls2]
      task3 = [ahttp.get(i) for i in urls3]
      tasks = task1 + task2 + task3
      
      ahttp.run(tasks)
    

創建單個請求

task = ahttp.get('https://www.google.com')
task.run()

或者可以這樣寫

result = ahttp.get('https://www.google.com').run()

自定義請求頭

  • 自定義請求頭可直接作爲參數傳遞到對象中,爲每一個連接賦予請求頭

      headers = {'content-type': 'application/json'}
      ...
      tasks = [ ahttp.get(url=url, headers=headers), ...]
    
  • 給session設置請求頭

      headers = {'content-type': 'application/json'}
      ...
      sess = ahttp.Session()
      sess.headers = {
      	...
      }
    

自定義 Cookie

  • session自定義Cookie

      cookies = {'content-type': 'application/json'}
      ...
      sess = ahttp.Session()
      sess.cookies = {...}
    
  • ahttp連接設置Cookie

      headers = {'PHPSESSIONID': '0XJFDFJJHFGKALDAS'}
      ...
      tasks = [ ahttp.get(url=url, cookies=cookies), ...]
    

自定義 encoding

ahttp採用了自動識別的編碼方式,能夠自動識別的編碼如下:

國際(Unicode)
UTF-8
UTF-16BE / UTF-16LE
UTF-32BE / UTF-32LE / X-ISO-10646-UCS-4-34121 / X-ISO-10646-UCS-4-21431
阿拉伯
ISO-8859-6
WINDOWS-1256
保加利亞語
ISO-8859-5
WINDOWS-1251
中文
ISO-2022-CN
BIG5
EUC-TW
GB18030
HZ-GB-2312
克羅地亞:
ISO-8859-2
ISO-8859-13
ISO-8859-16
Windows的1250
IBM852
MAC-CENTRALEUROPE
捷克
Windows的1250
ISO-8859-2
IBM852
MAC-CENTRALEUROPE
丹麥
ISO-8859-1
ISO-8859-15
WINDOWS-1252
英語
ASCII
世界語
ISO-8859-3
愛沙尼亞語
ISO-8859-4
ISO-8859-13
ISO-8859-13
Windows的1252
Windows的1257
芬蘭
ISO-8859-1
ISO-8859-4
ISO-8859-9
ISO-8859-13
ISO-8859-15
WINDOWS-1252
法國
ISO-8859-1
ISO-8859-15
WINDOWS-1252
德語
ISO-8859-1
WINDOWS-1252
希臘語
ISO-8859-7
WINDOWS-1253
希伯來語
ISO-8859-8
WINDOWS-1255
匈牙利:
ISO-8859-2
WINDOWS-1250
愛爾蘭蓋爾語
ISO-8859-1
ISO-8859-9
ISO-8859-15
WINDOWS-1252
意大利
ISO-8859-1
ISO-8859-3
ISO-8859-9
ISO-8859-15
WINDOWS-1252
日本
ISO-2022-JP
SHIFT_JIS
EUC-JP
朝鮮的
ISO-2022-KR
EUC-KR / UHC
立陶宛
ISO-8859-4
ISO-8859-10
ISO-8859-13
拉脫維亞
ISO-8859-4
ISO-8859-10
ISO-8859-13
馬耳他語
ISO-8859-3
拋光:
ISO-8859-2
ISO-8859-13
ISO-8859-16
Windows的1250
IBM852
MAC-CENTRALEUROPE
葡萄牙語
ISO-8859-1
ISO-8859-9
ISO-8859-15
WINDOWS-1252
羅馬尼亞:
ISO-8859-2
ISO-8859-16
Windows的1250
IBM852
俄語
ISO-8859-5
KOI8-R
WINDOWS-1251
MAC-CYRILLIC
IBM866
IBM855
斯洛伐克
Windows的1250
ISO-8859-2
IBM852
MAC-CENTRALEUROPE
斯洛文尼亞
ISO-8859-2
ISO-8859-16
Windows的1250
IBM852
中號

設置代理

  • 無驗證代理

      proxy = "http://some.proxy.com"
      ...
      tasks = [ahttp(url, proxy = proxy), ...]
    
  • 需要授權的代理

      proxy = "http://some.proxy.com"
      ...
      tasks = [ahttp(url, proxy = proxy, proxy_auth=proxy_auth), ...]	
    
  • 或者通過這種方式

      ...
      tasks = [ahttp(url, proxy="http://user:[email protected]"), ...]	
    
  • socks代理

      proxy = {
          "http":"socks5://127.0.0.1:1080",
          "https":"socks5h://127.0.0.1:1080"
      }
      ahttp.get(url, proxy=proxy)
    

修改最大併發的數量

連接池的默認值爲 2,意思是同時最多有 2 個協程及其週期在運轉,你可以通過下列代碼進行修改,但是請注意,不要超過1024個。 如果服務器響應很慢,那麼開的越多越好,可以改成 100 -200 , 如果服務器響應很快,那麼pool =2 和 pool = 100 幾乎是沒什麼區別的。因爲等待都在 IO 操作上

	...
	results = ahttp.run(tasks, pool = 100) #將連接池改爲100

回調函數

你可以通過給任務對象添加回調函數的方式以指定當前任務完成執行的函數

def callback(ahttp_response):
	ahttp_response # <AhttpResponse [status 200]>
	ahttp_response.status # 200
...
tasks=[ahttp.get(url, callback = callback), ...]

設置超時

task = ahttp.get(url,..., timeout=300)
# timeout 默認300s

歡迎 fork 並提交您的優化代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章