python爬蟲aiohttp非同步請求,高效率
0. 前言
本文翻譯自aiohttp的官方文件,如有紕漏,歡迎指出。
aiohttp分為伺服器端和客戶端,本文只介紹客戶端。
另外我已經對 aiohttp 和 asyncio進行了封裝,可以參考我的 github 地址:
由於上下文的緣故,請求程式碼必須在一個非同步的函式中進行:
async def fn():
pass
1. aiohttp安裝
pip3 install aiohttp
1.1. 基本請求用法
-
async with aiohttp.get('https://github.com') as r:
-
await r.text()
其中r.text(), 可以在括號中指定解碼方式,編碼方式,例如
await resp.text(encoding='windows-1251')
或者也可以選擇不編碼,適合讀取影象等,是無法編碼的
await resp.read()
2.發起一個session請求
首先是匯入aiohttp模組:
import aiohttp
然後我們試著獲取一個web原始碼,這裡以GitHub的公共Time-line頁面為例:
async with aiohttp.ClientSession() as session: async with session.get('https://api.github.com/events') as resp: print(resp.status) print(await resp.text())
上面的程式碼中,我們建立了一個 ClientSession 物件命名為session,然後通過session的get方法得到一個 ClientResponse 物件,命名為resp,get方法中傳入了一個必須的引數url,就是要獲得原始碼的http url。至此便通過協程完成了一個非同步IO的get請求。
有get請求當然有post請求,並且post請求也是一個協程:
session.post('http://httpbin.org/post', data=b'data')
用法和get是一樣的,區別是post需要一個額外的引數data,即是需要post的資料。
除了get和post請求外,其他http的操作方法也是一樣的:
session.put('http://httpbin.org/put', data=b'data') session.delete('http://httpbin.org/delete') session.head('http://httpbin.org/get') session.options('http://httpbin.org/get') session.patch('http://httpbin.org/patch', data=b'data')
小記:
不要為每次的連線都建立一次session,一般情況下只需要建立一個session,然後使用這個session執行所有的請求。
每個session物件,內部包含了一個連線池,並且將會保持連線和連線複用(預設開啟)可以加快整體的效能。
3.在URL中傳遞引數
我們經常需要通過 get 在url中傳遞一些引數,引數將會作為url問號後面的一部分發給伺服器。在aiohttp的請求中,允許以dict的形式來表示問號後的引數。舉個例子,如果你想傳遞 key1=value1 key2=value2 到 httpbin.org/get 你可以使用下面的程式碼:
params = {'key1': 'value1', 'key2': 'value2'} async with session.get('http://httpbin.org/get', params=params) as resp: assert resp.url == 'http://httpbin.org/get?key2=value2&key1=value1'
可以看到,程式碼正確的執行了,說明引數被正確的傳遞了進去。不管是一個引數兩個引數,還是更多的引數,都可以通過這種方式來傳遞。除了這種方式之外,還有另外一個,使用一個 list 來傳遞(這種方式可以傳遞一些特殊的引數,例如下面兩個key是相等的也可以正確傳遞):
params = [('key', 'value1'), ('key', 'value2')] async with session.get('http://httpbin.org/get', params=params) as r: assert r.url == 'http://httpbin.org/get?key=value2&key=value1'
除了上面兩種,我們也可以直接通過傳遞字串作為引數來傳遞,但是需要注意,通過字串傳遞的特殊字元不會被編碼:
async with session.get('http://httpbin.org/get', params='key=value+1') as r: assert r.url == 'http://httpbin.org/get?key=value+1'
4.響應的內容
還是以GitHub的公共Time-line頁面為例,我們可以獲得頁面響應的內容:
async with session.get('https://api.github.com/events') as resp: print(await resp.text())
執行之後,會打印出類似於如下的內容:
'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...
resp的text方法,會自動將伺服器端返回的內容進行解碼--decode,當然我們也可以自定義編碼方式:
await resp.text(encoding='gb2312')
除了text方法可以返回解碼後的內容外,我們也可以得到型別是位元組的內容:
print(await resp.read())
執行的結果是:
b'[{"created_at":"2015-06-12T14:06:22Z","public":true,"actor":{...
gzip和deflate轉換編碼已經為你自動解碼。
小記:
text(),read()方法是把整個響應體讀入記憶體,如果你是獲取大量的資料,請考慮使用”位元組流“(streaming response)
5.特殊響應內容:json
如果我們獲取的頁面的響應內容是json,aiohttp內建了更好的方法來處理json:
async with session.get('https://api.github.com/events') as resp: print(await resp.json())
如果因為某種原因而導致resp.json()解析json失敗,例如返回不是json字串等等,那麼resp.json()將丟擲一個錯誤,也可以給json()方法指定一個解碼方式:
print(await resp.json(
encoding='gb2312'
)) 或者傳遞一個函式進去:
print(await resp.json( lambda(x:x.replace('a','b')) ))
6.以位元組流的方式讀取響應內容
雖然json(),text(),read()很方便的能把響應的資料讀入到記憶體,但是我們仍然應該謹慎的使用它們,因為它們是把整個的響應體全部讀入了記憶體。即使你只是想下載幾個位元組大小的檔案,但這些方法卻將在記憶體中載入所有的資料。所以我們可以通過控制位元組數來控制讀入記憶體的響應內容:
async with session.get('https://api.github.com/events') as resp: await resp.content.read(10) #讀取前10個位元組
一般地,我們應該使用以下的模式來把讀取的位元組流儲存到檔案中:
with open(filename, 'wb') as fd: while True: chunk = await resp.content.read(chunk_size) if not chunk: break fd.write(chunk)
7.自定義請求頭
如果你想新增請求頭,可以像get新增引數那樣以dict的形式,作為get或者post的引數進行請求:
import json url = 'https://api.github.com/some/endpoint' payload = {'some': 'data'} headers = {'content-type': 'application/json'} await session.post(url, data=json.dumps(payload), headers=headers)
8.自定義Cookie
給伺服器傳送cookie,可以通過給 ClientSession 傳遞一個cookie引數:
url = 'http://httpbin.org/cookies' cookies = {'cookies_are': 'working'} async with ClientSession(cookies=cookies) as session: async with session.get(url) as resp: assert await resp.json() == { "cookies": {"cookies_are": "working"}}
可直接訪問連結 “httpbin.org/cookies”檢視當前cookie,訪問session中的cookie請見第10節。
9.post資料的幾種方式
(1)模擬表單post資料
payload = {'key1': 'value1', 'key2': 'value2'} async with session.post('http://httpbin.org/post', data=payload) as resp: print(await resp.text())
注意:data=dict的方式post的資料將被轉碼,和form提交資料是一樣的作用,如果你不想被轉碼,可以直接以字串的形式 data=str 提交,這樣就不會被轉碼。
(2)post json
import json url = 'https://api.github.com/some/endpoint' payload = {'some': 'data'} async with session.post(url, data=json.dumps(payload)) as resp: ...
其實json.dumps(payload)返回的也是一個字串,只不過這個字串可以被識別為json格式
(3)post 小檔案
url = 'http://httpbin.org/post' files = {'file': open('report.xls', 'rb')} await session.post(url, data=files)
可以設定好檔名和content-type:
url = 'http://httpbin.org/post' data = FormData() data.add_field('file', open('report.xls', 'rb'), filename='report.xls', content_type='application/vnd.ms-excel') await session.post(url, data=data)
如果將檔案物件設定為資料引數,aiohttp將自動以位元組流的形式傳送給伺服器。
(4)post 大檔案
aiohttp支援多種型別的檔案以流媒體的形式上傳,所以我們可以在檔案未讀入記憶體的情況下發送大檔案。
@aiohttp.streamer def file_sender(writer, file_name=None): with open(file_name, 'rb') as f: chunk = f.read(2**16) while chunk: yield from writer.write(chunk) chunk = f.read(2**16) # Then you can use `file_sender` as a data provider: async with session.post('http://httpbin.org/post', data=file_sender(file_name='huge_file')) as resp: print(await resp.text())
同時我們可以從一個url獲取檔案後,直接post給另一個url,並計算hash值:
async def feed_stream(resp, stream): h = hashlib.sha256() while True: chunk = await resp.content.readany() if not chunk: break h.update(chunk) stream.feed_data(chunk) return h.hexdigest() resp = session.get('http://httpbin.org/post') stream = StreamReader() loop.create_task(session.post('http://httpbin.org/post', data=stream)) file_hash = await feed_stream(resp, stream)
因為響應內容型別是StreamReader,所以可以把get和post連線起來,同時進行post和get:
r = await session.get('http://python.org') await session.post('http://httpbin.org/post', data=r.content)
(5)post預壓縮資料
在通過aiohttp傳送前就已經壓縮的資料, 呼叫壓縮函式的函式名(通常是deflate 或 zlib)作為content-encoding的值:
async def my_coroutine(session, headers, my_data): data = zlib.compress(my_data) headers = {'Content-Encoding': 'deflate'} async with session.post('http://httpbin.org/post', data=data, headers=headers) pass
10.keep-alive, 連線池,共享cookie
ClientSession 用於在多個連線之間共享cookie:
async with aiohttp.ClientSession() as session: await session.get( 'http://httpbin.org/cookies/set?my_cookie=my_value') filtered = session.cookie_jar.filter_cookies('http://httpbin.org') assert filtered['my_cookie'].value == 'my_value' async with session.get('http://httpbin.org/cookies') as r: json_body = await r.json() assert json_body['cookies']['my_cookie'] == 'my_value'
也可以為所有的連線設定共同的請求頭:
async with aiohttp.ClientSession( headers={"Authorization": "Basic bG9naW46cGFzcw=="}) as session: async with session.get("http://httpbin.org/headers") as r: json_body = await r.json() assert json_body['headers']['Authorization'] == \ 'Basic bG9naW46cGFzcw=='
ClientSession 還支援 keep-alive連線和連線池(connection pooling)
11.cookie安全性
預設ClientSession使用的是嚴格模式的 aiohttp.CookieJar. RFC 2109,明確的禁止接受url和ip地址產生的cookie,只能接受 DNS 解析IP產生的cookie。可以通過設定aiohttp.CookieJar 的 unsafe=True 來配置:
jar = aiohttp.CookieJar(unsafe=True) session = aiohttp.ClientSession(cookie_jar=jar)
12.控制同時連線的數量(連線池)
也可以理解為同時請求的數量,為了限制同時開啟的連線數量,我們可以將限制引數傳遞給聯結器:
conn = aiohttp.TCPConnector(limit=30)#同時最大進行連線的連線數為30,預設是100,limit=0的時候是無限制
限制同時開啟限制同時開啟連線到同一端點的數量((host, port, is_ssl) 三的倍數),可以通過設定 limit_per_host 引數:
conn = aiohttp.TCPConnector(limit_per_host=30)#預設是0
13.自定義域名解析
我們可以指定域名伺服器的 IP 對我們提供的get或post的url進行解析:
from aiohttp.resolver import AsyncResolver resolver = AsyncResolver(nameservers=["8.8.8.8", "8.8.4.4"]) conn = aiohttp.TCPConnector(resolver=resolver)
14.設定代理
aiohttp支援使用代理來訪問網頁:
async with aiohttp.ClientSession() as session: async with session.get("http://python.org", proxy="http://some.proxy.com") as resp: print(resp.status)
當然也支援需要授權的頁面:
async with aiohttp.ClientSession() as session: proxy_auth = aiohttp.BasicAuth('user', 'pass') async with session.get("http://python.org", proxy="http://some.proxy.com", proxy_auth=proxy_auth) as resp: print(resp.status)
或者通過這種方式來驗證授權:
session.get("http://python.org", proxy="http://user:[email protected]")
15.響應狀態碼 response status code
可以通過 resp.status來檢查狀態碼是不是200:
async with session.get('http://httpbin.org/get') as resp: assert resp.status == 200
16.響應頭
我們可以直接使用 resp.headers 來檢視響應頭,得到的值型別是一個dict:
>>> resp.headers {'ACCESS-CONTROL-ALLOW-ORIGIN': '*', 'CONTENT-TYPE': 'application/json', 'DATE': 'Tue, 15 Jul 2014 16:49:51 GMT', 'SERVER': 'gunicorn/18.0', 'CONTENT-LENGTH': '331', 'CONNECTION': 'keep-alive'}
或者我們可以檢視原生的響應頭:
>>> resp.raw_headers ((b'SERVER', b'nginx'), (b'DATE', b'Sat, 09 Jan 2016 20:28:40 GMT'), (b'CONTENT-TYPE', b'text/html; charset=utf-8'), (b'CONTENT-LENGTH', b'12150'), (b'CONNECTION', b'keep-alive'))
17.檢視cookie
url = 'http://example.com/some/cookie/setting/url' async with session.get(url) as resp: print(resp.cookies)
18.重定向的響應頭
如果一個請求被重定向了,我們依然可以檢視被重定向之前的響應頭資訊:
>>> resp = await session.get('http://example.com/some/redirect/') >>> resp <ClientResponse(http://example.com/some/other/url/) [200]> >>> resp.history (<ClientResponse(http://example.com/some/redirect/) [301]>,)
19.超時處理
預設的IO操作都有5分鐘的響應時間 我們可以通過 timeout 進行重寫:
async with session.get('https://github.com', timeout=60) as r: ...
如果 timeout=None 或者 timeout=0 將不進行超時檢查,也就是不限時長
相關推薦
python爬蟲aiohttp非同步請求,高效率
0. 前言 本文翻譯自aiohttp的官方文件,如有紕漏,歡迎指出。 aiohttp分為伺服器端和客戶端,本文只介紹客戶端。 另外我已經對 aiohttp 和 asyncio進行了封裝,可以參考我的 github 地址: 由於上下文的緣故,請求程式碼必須在一個非同
python flask裏 post請求,JSON數據獲取方式總結
out ren response 獲取 post task world! json數據 appid #!flask/bin/python #encodig=utf-8 # _*_ coding:utf-8 _*_ # Writer : byz # dateTim
Python爬蟲之post請求
對象 parse ... src pytho clas open 網址 源代碼 暑假放假在家沒什麽事情做,所以在學習了爬蟲,在這個博客園裏整理記錄一些學習的筆記。 構建表單數據(以http://www.iqianyue.com/mypost 這個簡單的網頁為例) 查看源代碼
python爬取ajax請求,返回的json資料格式化報錯json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
python爬取ajax請求,返回Json資料中帶有<html><head></head><body><prestyle="word-wrap: break-word; white-space: pre-wrap;"></pre>
python:爬蟲之Post請求以及動態Ajax資料的爬取(3)
#爬蟲的post方式 作用:對引數進行打包反饋給伺服器 import urllib.request import urllib.parse #對引數打包 url = "http://www.sunck.wang:8085/form" data = { "use
python爬蟲自學第一天,全新的開始!
一直在學習,跟著雪峰老師的講解,跟著《Python爬蟲開發與專案實戰》,跟著笨辦法學Python(不得不說確實是新手上路的一本好書),跟著CSDN裡的大神,跟著慕課網的老師,跟著一切能跟著資源......只是今天才開始記錄自己的學習歷程,之前的會慢慢補齊,今天就從“分散式程序”開始記錄吧 話不多少
非同步請求,不要讓shiro重定向
轉自:https://blog.csdn.net/iTommyChi/article/details/81474638 shiro 重要的幾個類: DefaultFilter 列舉類 AuthorizationFilter FormAuthenticationFilter
非同步請求,跨域訪問報錯問題 POST提交方式變成OPTIONS
跨域訪問報錯:無訪問許可權、POST請求過來變成OPTIONS 在返回servletResponse物件裡增加如下設定: servletResponse.setHeader("Access-Contro
[python]socket傳送http請求,非阻塞io的一個例子
#通過非阻塞io實現http請求 import socket from urllib.parse import urlparse #使用非阻塞io完成http請求 def get_url(url): #通過socket請求html url
Python爬蟲有什麼用,網友紛紛給出自己的答案,以前是小看了爬蟲
前言 爬蟲可以從網站某一個頁面(通常是首頁)開始,讀取網頁的內容,找到在網頁中的其它連結地址,然後通過這些連結地址尋找下一個網頁,這樣一直迴圈下去,直到把這個網站所有的網頁都抓取完為止。 難道爬蟲真的只是這樣麼? 小編特地詢問很多網友Python有什麼用,大家給出
Python爬蟲有什麼用,網友紛紛給出自己的答案,爬蟲能做的還是很多的
爬蟲可以從網站某一個頁面(通常是首頁)開始,讀取網頁的內容,找到在網頁中的其它連結地址,然後通過這些連結地址尋找下一個網頁,這樣一直迴圈下去,直到把這個網站所有的網頁都抓取完為止。 難道爬蟲真的只是這樣麼? 小編特地詢問很多網友Python有什麼用,大家給出答案也是五花八門。 @冰藍
python爬蟲建立代理池,爬取5000個代理IP並進行驗證!
前面已經介紹了urllib+正則表示式和BeautifulSoup進行爬取資料的方法,今天再解決一個實際問題——構建自己的代理池。 通過爬蟲在網上進行資料的獲取,由於效率很快,換言之,訪問的速度過快,導致一段時間內的流量過大,會使得對方的伺服器壓力過
python爬蟲:http請求頭部(header)詳解
通常HTTP訊息包括客戶機向伺服器的請求訊息和伺服器向客戶機的響應訊息。這兩種型別的訊息由一個起始行,一個或者多個頭域,一個只是頭域結束的空行和可 選的訊息體組成。HTTP的頭域包括通用頭,請求頭,響應頭和實體頭四個部分。每個頭域由一個域名,冒號
Django專案實戰總結一----非同步請求,echarts
function ajax_submit() { $.ajax({ //url: "{% url 'GetIndex' %}", url:"/index/", type: "GET", data: {},
Android--使用原生技術實現ListView(原生技術實現網路非同步請求,解析json資料)
涉及到的原生技術: 1.原生技術實現網路非同步請求 1.原生技術解析json資料 實現步驟: 實現程式碼: **第一二步比較簡單,直接跳過 import android.content.Context; import
【Python爬蟲】Requests 請求並讀寫、儲存到excel檔案中
爬取前程無憂職位資訊 此次我們用簡單的爬蟲來展示如何把爬到提取出的資訊儲存的excel檔案中.(ps:首先你要安裝好模組openpyxl否則就點選右上角離開,百度搜素安裝.) 選前程無憂的網頁作為案例是因為主編最近在看看工作的訊息,想想就順手寫了一個為方便尋找滿足自己要
Ajax非同步請求,頁面不跳轉問題的解決
背景:在進行ssm整和shiro時有一個許可權不足時跳轉到許可權不足頁面的需求。前端是easyUI的dataGrid表格傳送了一個Ajax請求,到達後端之後這個請求被perms攔截器攔截,許可權校驗未通過,於是要向/webApp/u
解決微信小程式 app onLaunch非同步請求,在沒有請求執行完就載入首頁了的問題
今天在除錯小程式的過程中,進了一個坑,程式載入需要先獲取使用者資訊,然後儲存到Storage中,然後首頁去Storage取資訊,根據使用者資訊去查本地伺服器資料列表,可是發現第一次進入的時候,資料總是載入不出來,只有再次進入才有資料。除錯之後發現app.js onLaunc
從零開始學 Web 之 Ajax(五)同步非同步請求,資料格式
一、同步請求與非同步請求 同步請求:在使用者進行請求傳送之後,瀏覽器會一直等待伺服器的資料返回,如果網路延遲比較高,瀏覽器就一直卡在當前介面,直到伺服器返回資料才可進行其他操作。 非同步請求:在使用者進行請求傳送之後,瀏覽器可以自由操作頁面中其他的元素,當伺服器放回資料的時候,才觸發相應事件,對返回的資料
python 爬蟲抓豆瓣電影,並存入資料庫
import urllib.request import json import codecs class info(object): #@classmethod def moviedown(url): #網址 url = "https://m