特點
- Keep-Alive & 連接池
- 國際化域名和 URL
- 帶持久 Cookie 的會話
- 瀏覽器式的SSL認證
- 自動內(nèi)容解碼
- 基本/摘要式的身份認證
- 優(yōu)雅的key/value Cookie
- 自動解壓
- Unicode 響應(yīng)體
- HTTP(S) 代理支持
- 文件分塊上傳
- 流下載
- 連接超時
- 分塊請求
- 支持 .netrc
缺點:
- 同步阻塞模式娃惯,不支持異步和協(xié)程
- 尚不支持HTTP2.0
官方文檔:https://requests.readthedocs.io/zh_CN/latest/
安裝
通過pip命令安裝即可:pip install requests
發(fā)送請求
發(fā)送GET請求
使用requests發(fā)送請求叽掘,只要使用request.get(url)方法填入對應(yīng)的接口地址即可谆棱,支持攜帶URL參數(shù)邓深。調(diào)用方法返回響應(yīng)對象,可以通過響應(yīng)對象的status_code偏序、text、headers等屬性,來獲取狀態(tài)碼吩案、響應(yīng)文本和響應(yīng)頭等數(shù)據(jù),示例如下帝簇。
import requests
res = requests.get('https://httpbin.org/get?name=臨淵&age=18')
print('狀態(tài)碼', res.status_code)
print('響應(yīng)文本', res.text)
print('響應(yīng)頭', res.headers)
URL只支持ASCII(美國標準碼)徘郭,在實際的傳輸過程中,中文及一些特殊字符需要經(jīng)過urlencode(URL編碼)丧肴。如上例中的接口地址會被編碼成:
https://httpbin.org/get?name=%E4%B8%B4%E6%B8%8A&age=18
requests在發(fā)送請求時會自動進行編碼残揉,運行后顯示如下。
狀態(tài)碼 200
響應(yīng)文本 {
"args": {
"age": "18",
"name": "\u4e34\u6e0a"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.4"
},
"origin": "111.194.126.253, 111.194.126.253",
"url": "https://httpbin.org/get?name=\u4e34\u6e0a&age=18"
}
響應(yīng)頭 {'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', 'Content-Encoding': 'gzip', 'Content-Type': 'application/json', 'Date': 'Mon, 20 Jan 2020 02:33:47 GMT', 'Referrer-Policy': 'no-referrer-when-downgrade', 'Server': 'nginx', 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block', 'Content-Length': '222', 'Connection': 'keep-alive'}
使用Params
Params又叫Query Params芋浮,即URL參數(shù)抱环,如?name=臨淵&age=18
。如果參數(shù)很多纸巷,直接寫到URL中會比較長镇草,不方便查看和修改。URL參數(shù)由多組鍵值對組成瘤旨√找梗可以通過字典傳給requests請求放到的params參數(shù),即request.get(url, params={})
裆站,示例如下条辟。
import requests
res = requests.get('https://httpbin.org/get', params={'name': '臨淵', 'age': '18'})
print('響應(yīng)文本轉(zhuǎn)為字典', res.json())
由于參數(shù)可能較多,一般我們可以使用變量宏胯,先把url及參數(shù)等數(shù)據(jù)組裝好羽嫡,然后在傳入請求方法中。
res.json()方法實際上是使用了json.loads(res.text)
將響應(yīng)文本嘗試以JSON格式轉(zhuǎn)為字典肩袍。由于該方法存在異常(比如正常情況下返回JSON格式杭棵,500報錯時則會返回非JSON格式的報錯信息),建議使用try...except處理氛赐,修改如下魂爪。
import requests
url = 'https://httpbin.org/get'
url_params = {'name': '臨淵', 'age': '18'}
res = requests.get(url, params=url_params)
try:
print('響應(yīng)文本轉(zhuǎn)為字典', res.json())
except:
print('響應(yīng)文本', res.text)
url_params是自定義的變量名,一般筆者習(xí)慣使用params作為變量名艰管,來表示和請求方法參數(shù)params的對應(yīng)關(guān)系滓侍。即
params = {'name': '臨淵', 'age': '18'}
res = requests.get(url, params=params)
運行結(jié)果如下。
響應(yīng)文本轉(zhuǎn)為字典 {'args': {'age': '18', 'name': '臨淵'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.18.4'}, 'origin': '111.194.126.253, 111.194.126.253', 'url': 'https://httpbin.org/get?name=臨淵&age=18'}
使用請求頭
請求頭是鏈接和請求數(shù)據(jù)的一些輔助說明信息牲芋,常見的請求頭有:
- Accept:客戶端能接受的內(nèi)容類型
- Accept-Charset:瀏覽器可以接受的字符編碼
- Accept-Encoding:瀏覽器可以支持的壓縮編碼類型
- Accept-Languge:瀏覽器可以接受的語言
- Referer:連接來路
- User-Agent:發(fā)送請求的客戶端信息
- Connection:連接類型(Keepalive保持連接/Close關(guān)閉連接)
- X-Requested-With:XMLHttpRequest(是Ajax異步請求)
- Cookie:服務(wù)器標記信息
- Cache-Control:緩存機制(no-cache無緩存或max-age=緩存保存時間)
- Expries:緩存過期時間
- Content-Type:內(nèi)容類型(MIME類型)
- Content-Length:數(shù)據(jù)長度
請求頭項一般不區(qū)分大小寫撩笆。Cookie是請求頭的一項(注意為單數(shù)形式捺球,不帶s)。因此在請求一些需要登錄狀態(tài)的接口時可以手動抓取到Cookie夕冲,放到請求頭中使用氮兵,示例如下。
(1)手動登錄后歹鱼,通過Chrome開發(fā)者工具抓取請求正常訪問時的請求頭信息泣栈。
請求頭中一般cookie
用于驗證登錄,referer
用于防止盜鏈弥姻,user-agent
用于反爬秩霍。
(2)組裝字典格式的請求頭并使用
請求頭一般有一組組鍵值對組成,我們同樣使用Python中的的字典格式蚁阳,構(gòu)造出請求頭數(shù)據(jù),并傳遞給請求方法的headers參數(shù)即可鸽照。
import requests
url = 'http://www.reibang.com/shakespeare/v2/notes/9d3f991c901a/book'
headers = {
'user-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
'referer': 'http://www.reibang.com/p/9d3f991c901a',
'cookie': '__yadk_uid=ratDswBmN3Kzid42v2gKV2q8veUvOsEd; read_mode=day; default_font=font2; locale=zh-CN; remember_user_token=W1s3NTc1NzIxXSwiJDJhJDExJFRVVTNvMlV6NjJaVTlXZjF0YWFuZi4iLCIxNTc5NTczNDg1Ljk2MzgyODYiXQ%3D%3D--feb4c1d88427a88d7321791daf2d76f7b11ed4b3; _m7e_session_core=d0065296a1834086d0279a548d932927; Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1579573487,1579587460,1579591333,1579591335; Hm_lpvt_0c0e9d9b1e7d617b3e6842e85b9fb068=1579601795; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22%24device_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_utm_source%22%3A%22weixin-friends%22%2C%22%24latest_utm_medium%22%3A%22reader_share%22%2C%22%24latest_utm_campaign%22%3A%22hugo%22%2C%22%24latest_utm_content%22%3A%22note%22%7D%2C%22first_id%22%3A%22%22%7D'
}
res = requests.get(url, headers=headers)
print(res.text)
headers=headders
第一個headers是請求方法的固定參數(shù)螺捐,第二個headers是我們自定義的字典變量(變量也可以使用其他名稱),執(zhí)行后打印信息如下矮燎。
{"notebook_id":26739010,"notebook_name":"Python接口測試","liked_by_user":false}
注:本例中請求頭實際并沒有登錄限制定血,只需要在請求頭添加了
user-agent
即可正常使用。
使用Cookies
Cookies可以作為一個整體的字符串放到請求頭的Cookie
字段中诞外,當Cookies很多并且需要組裝時澜沟,使用字符串會比較長并難以維護。此時可以將Cookies拆開成一組組鍵值對峡谊,構(gòu)造為字典格式的數(shù)據(jù)茫虽,傳遞給請求方法的cookies參數(shù),示例如下既们。
import requests
url = 'http://www.reibang.com/shakespeare/v2/notes/9d3f991c901a/book'
headers = {
'user-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36',
'referer': 'http://www.reibang.com/p/9d3f991c901a',
}
cookies = {'Hm_lpvt_0c0e9d9b1e7d617b3e6842e85b9fb068': '1579601795',
'Hm_lvt_0c0e9d9b1e7d617b3e6842e85b9fb068': '1579573487,1579587460,1579591333,1579591335',
'__yadk_uid': 'ratDswBmN3Kzid42v2gKV2q8veUvOsEd',
'_m7e_session_core': 'd0065296a1834086d0279a548d932927',
'default_font': 'font2',
'locale': 'zh-CN',
'read_mode': 'day',
'remember_user_token': 'W1s3NTc1NzIxXSwiJDJhJDExJFRVVTNvMlV6NjJaVTlXZjF0YWFuZi4iLCIxNTc5NTczNDg1Ljk2MzgyODYiXQ%3D%3D--feb4c1d88427a88d7321791daf2d76f7b11ed4b3',
'sensorsdata2015jssdkcross': '%7B%22distinct_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22%24device_id%22%3A%221697595f5777a9-0a3caa4a8cc382-36667905-1024000-1697595f578430%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E7%9B%B4%E6%8E%A5%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC_%E7%9B%B4%E6%8E%A5%E6%89%93%E5%BC%80%22%2C%22%24latest_utm_source%22%3A%22weixin-friends%22%2C%22%24latest_utm_medium%22%3A%22reader_share%22%2C%22%24latest_utm_campaign%22%3A%22hugo%22%2C%22%24latest_utm_content%22%3A%22note%22%7D%2C%22first_id%22%3A%22%22%7D'
}
res = requests.get(url, headers=headers, cookies=cookies)
print(res.text)
注:Cookies中不能擁有非ASCII字符濒析,中文應(yīng)進行URL編碼后使用。
同名參數(shù)處理:
假設(shè)url中具有同名參數(shù)啥纸,如name=臨淵号杏,age=18,age=30
斯棒。由于字典中不能存在同名的鍵盾致,我們可以使用嵌套列表實現(xiàn)。示例如下荣暮。
params = [('name','臨淵'), ('age', '18'), ('age', '30')]
請求方法中的其他參數(shù)庭惜,如data、headers等穗酥,如果存在同名變量也可以這樣處理蜈块。
發(fā)送POST請求
POST方法和GET方法本質(zhì)上一樣的鉴腻,都是HTTP請求的一種請求動作。只是通常情況下GET請求不使用請求體數(shù)據(jù)百揭,而POST使用爽哎。既然POST方法會發(fā)送請求體數(shù)據(jù),就會涉及到數(shù)據(jù)類型的問題器一】涡浚客戶端和服務(wù)端商量好,才能正常的解析和通訊祈秕。這種數(shù)據(jù)類型又稱為媒體類型渺贤,標準稱法為MIME(Multipurpose Internet Mail Extensions)類型,即多用途互聯(lián)網(wǎng)郵件擴展類型请毛。數(shù)據(jù)類型的聲明志鞍,一般放在請求頭(請求輔助信息)的Content-Type字段中,常見的有以下幾種格式方仿。
- application/x-www-form-url-encoded:表單URL編碼格式
- multipart/form-data:復(fù)合表單格式(支持文件上傳固棚,文件二進制
- application/json:JSON格式
- application/xml:XML格式
不同數(shù)據(jù)類型的請求,數(shù)據(jù)組裝方式也不同仙蚜,至于什么時候用表單此洲,什么時候用JSON格式要看接口文檔或問開發(fā)小哥哥,接口在編寫時便已確定好了需要使用的的數(shù)據(jù)(媒體)類型委粉。
發(fā)送POST請求使用requests的post方法即可呜师,格式如下。
res = requests.post(url,data={}, json={}, files={})
data贾节、json汁汗、files都是可選參數(shù)(一般同時只用其中一個)。分別用來將數(shù)據(jù)按不同格式編碼發(fā)送栗涂。
- data參數(shù)接受字典時將數(shù)據(jù)按普通表單(application/x-www-form-url-encoded)格式發(fā)送碰酝。
- json參數(shù)存在時將字典格式的請求數(shù)據(jù)按JSON格式(application/json)發(fā)送
- files參數(shù)將字典格式的請求數(shù)據(jù)(可以包含打開的文件)按混合表單(multipart/form-data)格式發(fā)送。
同時使用三者之一時戴差,會自動在請求頭中添加對應(yīng)的內(nèi)容類型聲明Content-Type:...
送爸。
當data參數(shù)接受字符串格式的參數(shù)是按Raw原始格式發(fā)送,不進行編碼和添加請求頭暖释。當data參數(shù)接受文件對象時按binary二進制格式發(fā)送袭厂。
發(fā)送FORM表單格式數(shù)據(jù)
Form表單指網(wǎng)頁中包含輸入框、選擇框球匕、按鈕等組成的一組用戶填寫及選擇的數(shù)據(jù)纹磺。如登錄、注冊表單亮曹。表單是最常用的一種請求數(shù)據(jù)類型橄杨,對應(yīng)的請求頭媒體類型聲明:Content-Type:application/x-www-form-urlencoded
秘症。
之所以稱為urlencoded,是因為式矫,請求體數(shù)據(jù)乡摹,實際會按url編碼格式發(fā)送,如name=臨淵采转,password=123456
實際上會編碼為
name=%E4%B8%B4%E6%B8%8A&password=123456
作為請求體數(shù)據(jù)聪廉,后臺傳輸。
表單類型的參數(shù)同樣是由多組鍵值對組成故慈,我們同樣適用字典格式構(gòu)造請求體數(shù)據(jù)并傳遞給請求方法的data參數(shù)即可板熊,示例如下。
import requests
url = 'https://httpbin.org/post'
data = {'name': '臨淵', 'password': '123456'}
res = requests.post(url, data=data)
print(res.text)
發(fā)送POST請求只要使用requests.post()方法即可察绷,方法中的data=data
干签,第一個data是請求方法的一個固定的關(guān)鍵字參數(shù),后面的data是上面我自定義的變量拆撼,即{'name': '臨淵', 'password': '123456'}
容劳,使用其他變量名可以。打印結(jié)果如下情萤。
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "\u4e34\u6e0a",
"password": "123456"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "39",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.4"
},
"json": null,
"origin": "111.194.126.253, 111.194.126.253",
"url": "https://httpbin.org/post"
}
發(fā)送時,請求頭中會自動添加"Content-Type": "application/x-www-form-urlencoded"
摹恨。
對于JSON格式的響應(yīng)數(shù)據(jù)筋岛,我們可以使用res.json()
轉(zhuǎn)為字典格式并通過字典取值提取響應(yīng)字段的變量進行斷言。假設(shè)我們要斷言響應(yīng)結(jié)果的url為"https://httpbin.org/post"
晒哄,form不為空且name和password是我們傳的值睁宰,示例如下。
res_dict = res.json()
form = res_dict.get('form')
assert "https://httpbin.org/post" == res_dict.get('url')
assert form and "臨淵" == form.get('name') and '123456' == form.get('password')
再次運行寝凌,結(jié)果和上次一致柒傻。沒有報錯即為assert斷言通過,斷言失敗時會報AssertionError较木。
發(fā)送JSON格式數(shù)據(jù)
JSON格式是一種通用的數(shù)據(jù)格式红符,在Python中JSON實際為“符合JSON語法格式的字符串”,本質(zhì)是str類型伐债。JSON格式和Python的字典一一對應(yīng)预侯,略有不同,如JSON中的true/false/null
對應(yīng)字典中的True/False/None
峰锁。我們同樣可以使用字典來構(gòu)造JSON請求的數(shù)據(jù)萎馅,然后傳遞夠請求方法的json參數(shù)即可,示例如下虹蒋。
import requests
url = 'https://httpbin.org/post'
json_data = {'name': '臨淵', 'age': 18, 'on_site': True, 'favorite': None}
res = requests.post(url, json=json_data)
print(res.text)
URL參數(shù)和FORM變動格式中的數(shù)字實際都是轉(zhuǎn)為字符串格式去發(fā)送的糜芳,而JSON中可以區(qū)分數(shù)字格式和字符串格式飒货。如{"age": 18}
和{"age":"18"}
有可能是不一樣的。響應(yīng)文本打印結(jié)果如下峭竣。
{
"args": {},
"data": "{\"name\": \"\\u4e34\\u6e0a\", \"age\": 18, \"on_site\": true, \"favorite\": null}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "70",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.4"
},
"json": {
"age": 18,
"favorite": null,
"name": "\u4e34\u6e0a",
"on_site": true
},
"origin": "111.194.126.253, 111.194.126.253",
"url": "https://httpbin.org/post"
}
發(fā)送時塘辅,請求頭中會自動添加"Content-Type": "application/json"
。
細心的同學(xué)會發(fā)現(xiàn)邪驮,F(xiàn)ORM表單格式發(fā)送的數(shù)據(jù)會出現(xiàn)在響應(yīng)的form字段中莫辨,JSON格式的卻出現(xiàn)在data字段中。這是因為JSON和XML等格式一樣屬于Raw(原始格式)毅访,即原樣發(fā)送沮榜。但是在實際發(fā)送時仍要確保請求數(shù)據(jù)都轉(zhuǎn)為ASCII(美國標準碼)來傳輸。因此中文參數(shù)“臨淵”在傳輸是會按utf-8編碼轉(zhuǎn)換為“\u4e34\u6e0a”喻粹。由于JSON格式中只能使用雙引號蟆融,響應(yīng)中data參數(shù)是一個JSON格式的字符串,需要使用轉(zhuǎn)義字符“\”守呜。
發(fā)送XML格式的數(shù)據(jù)
上例提到XML和JSON都屬于Raw格式的數(shù)據(jù)型酥,XML和JSON在Python中實際都是不同格式的文本字符串。我們將字符串傳遞給請求方法的data參數(shù)即可原樣發(fā)送查乒,即data參數(shù)有以下3重作用:
- data = {} 或 [(,), (,)]:接受一個字典或嵌套列表格式的數(shù)據(jù)弥喉,會按表單Url編碼格式
- data = '':接受一個字符串或bytes二進制字符串,會原樣發(fā)送(需要手動添加請求頭玛迄,如果存在中文需要手動編碼)
- data = open('...', 'rb'):接受一個文件對象由境,按binary格式流式上傳。
發(fā)送XML格式的數(shù)據(jù)只要將XML格式的多行字符串傳遞給請求方法的data參數(shù)即可蓖议,示例如下虏杰。
import requests
url = 'https://httpbin.org/post'
xml_data = '''
<xml>
<name>臨淵</name>
<age>12</name>
</xml>
'''
headers = {'Content-Type': 'application/xml'}
res = requests.post(url, data=xml_data.encode('utf-8'), headers=headers)
print(res.text)
由于xml_data數(shù)據(jù)中存在非ASCII碼,需要將數(shù)據(jù)按utf-8格式編碼為bytes二進制字符串發(fā)送勒虾。由于使用Raw格式發(fā)送數(shù)據(jù)時不會自動添加請求頭纺阔,因此一般要手動在請求頭中添加內(nèi)容類型聲明,并將構(gòu)造的字典類型的請求頭變量修然,傳遞給請求方法的關(guān)鍵字參數(shù)headers笛钝。響應(yīng)結(jié)果如下。
{
"args": {},
"data": "\n<xml>\n <name>\u4e34\u6e0a</name>\n <age>12</name>\n</xml>\n",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "57",
"Content-Type": "application/xml",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.4"
},
"json": null,
"origin": "111.194.126.253, 111.194.126.253",
"url": "https://httpbin.org/post"
}
Raw格式的數(shù)據(jù)都會記錄在該接口響應(yīng)數(shù)據(jù)的data字段中愕宋。
Raw格式的請求(Text婆翔、JavaScript、JSON掏婶、XML啃奴、HTML等)都可以按這種方式發(fā)送。JSON請求自然也可以按原始方式發(fā)送雄妥,示例如下最蕾。
import requests
url = 'https://httpbin.org/post'
json_data_str = '''
{
"name": "臨淵",
"age": 18,
"on_site": true,
"favorite": null
}
'''
headers = {'Content-Type': 'application/json'}
res = requests.post(url, data=json_data_str.encode('utf-8'), headers=headers)
print(res.text)
注意以上的json_data_str
須是符合JSON格式的字符串依溯,包括必須使用雙引號,應(yīng)該使用小寫的true
瘟则,無值應(yīng)該是null
黎炉,由于字符串中存在中文,同樣要手動進行encode編碼醋拧,同時要手動添加請求頭指定內(nèi)容類型慷嗜。
為方便構(gòu)造請求數(shù)據(jù),也可以先構(gòu)造一個字典格式的請求數(shù)據(jù)丹壕,再使用json.dumps()庆械,將字典格式的數(shù)據(jù)轉(zhuǎn)為JSON字符串發(fā)送,示例如下菌赖。
import requests
import json
url = 'https://httpbin.org/post'
json_data = {
'name': '臨淵',
'age': 18,
'on_site': True,
'favorite': None
}
headers = {'Content-Type': 'application/json'}
res = requests.post(url, data=json.dumps(json_data), headers=headers)
print(res.text)
注意以上json_data
是字典格式的變量缭乘,因此要使用True
及None
。在將字典轉(zhuǎn)為JSON字符串時琉用,需要首先導(dǎo)入json庫堕绩。json.dumps()將字典格式的json_data轉(zhuǎn)換為JSON字符串,并通過默認的ensure_ascii=True
參數(shù)將中文轉(zhuǎn)換為\u形式的ASCII字符(如“臨淵”會轉(zhuǎn)換為“\u4e34\u6e0a”)邑时,因此不再需要進行編碼后發(fā)送奴紧。
發(fā)送Multipart/form-data請求(文件上傳)
網(wǎng)頁上的表單有兩種,一種是不包含文件上傳晶丘,所有用戶輸入或選擇的數(shù)據(jù)都可以使用字符串格式表示黍氮,這種稱為普通表單或純文本表單,對應(yīng)MIME類型為application/x-www-form-urlencoded
铣口。
另一種即包括普通輸入框等滤钱,也包含一個或多個文件上傳框觉壶。普通輸入框中的變量值可以已字符串格式編碼脑题,而上傳的文件(如圖片文件)則不一定能直接轉(zhuǎn)為字符串,要使用二進制格式铜靶。因此要使用多部分的混合格式叔遂,筆者稱之為混合表單,對應(yīng)MIME類型為multipart/form-data
争剿。在表單中已艰,每個需要上傳的文件和普通輸入框一樣對應(yīng)一個指定的變量。因此同樣可以使用字典格式組裝混合表單的請求數(shù)據(jù)傳遞給請求方法的files參數(shù)即可蚕苇,示例如下哩掺。
import requests
url = 'https://httpbin.org/post'
multi_form_data = {
'name': '臨淵',
'age': '18', # 不能使用int類型
'avatar': open('/Users/apple/Pictures/robot.png', 'rb'),
'avatar2': open('/Users/apple/Pictures/robot.jpg', 'rb'),
}
res = requests.post(url, files=multi_form_data)
print(res.text)
表單數(shù)據(jù)中的數(shù)字要使用字符串格式的數(shù)字,文件要以rb二進制格式打開傳輸涩笤,支持多個變量以及多個文件嚼吞。
文件類型的數(shù)據(jù)avatar可以只穿一個打開的文件對象open('/Users/apple/Pictures/robot.png', 'rb')
盒件,也可以傳遞三個參數(shù):要保存的文件名,打開的文件及文件MIME類型舱禽,即
'avatar': ('robot.png', open('/Users/apple/Pictures/robot.png', 'rb'), 'image/png'),
比如有些接口上傳Excel文件時必須聲明文件名和MIME類型炒刁,如:
res = request.post(url, files={'upload_file':
('data.xlsx',
open('data.xlsx', 'rb'),
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
})
MIME類型參考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types
發(fā)送Binary格式數(shù)據(jù)(單文件/流式上傳)
import requests
url = 'https://httpbin.org/post'
res = requests.post(url, data=open('/Users/apple/Pictures/robot.jpg', 'rb'))
print(res.text)
JSON與字典的相互轉(zhuǎn)換
JSON(JavaScript Object Notation),即JavaScript對象標記誊稚。 是一種通用的輕量級的數(shù)據(jù)交換格式翔始。在Python中,JSON本質(zhì)上是符合JSON格式的字符串(str類型)里伯,即JSON字符串城瞎。
JSON字符串中支持Object對象、Array數(shù)組俏脊、String字符串全谤、Number數(shù)字、true/false布爾值爷贫、null空值6中數(shù)據(jù)類型认然,并支持層次嵌套。Python中的字典和JSON字符串中描述的數(shù)據(jù)類型一一對應(yīng)漫萄,對應(yīng)關(guān)系如下表所示卷员。
JSON字符串 | Python |
---|---|
Object {...} | 字典 {...} |
Array [...] | 列表 [...] |
String "..." | 字符串 '...' 或 "..." |
Number 1.5或3 | 浮點型或整型 1.5或3 |
true或false | True或False |
null | None |
注意,JSON格式較為嚴格腾务,和Python字典格式略有不同:
- 字典中的引號支持單引號和雙引號毕骡,JSON格式只支持雙引號
- 字典中的True/False首字母大寫,JSON格式為true/false
- 字典中的空值為None, JSON格式為null
- 字典中可以使用#好注釋岩瘦,JSON中不允許使用任何形式的注釋
- 字典列表最后一項后可以有逗號未巫,JSON數(shù)組最后一項后不可以有逗號
作為一種標準格式的字符串,JSON方便在不同系統(tǒng)中進行數(shù)據(jù)交換启昧,方便進進行傳輸和存儲叙凡,卻不方便從整段字符串中提取響應(yīng)的字段對應(yīng)的值。
而字典作為內(nèi)存中的一種數(shù)據(jù)結(jié)構(gòu)密末,可以很方便的對其中的數(shù)據(jù)進行提取或添加等操作握爷。因此我們常常需要在JSON字符串和字典之間相互轉(zhuǎn)換。
在接口請求中常用的轉(zhuǎn)換如下严里。
- (1)使用字典格式構(gòu)造請求數(shù)據(jù)
- (2)轉(zhuǎn)為JSON字符串發(fā)送請求
- (3)服務(wù)端解析處理
- (4)返回JSON字符串格式的響應(yīng)數(shù)據(jù)
- (5)轉(zhuǎn)為字典格式提取相應(yīng)的字段并斷言
Python自帶的json庫提供JSON字符或JSON文件對象和字典之間的相互轉(zhuǎn)換新啼,主要方法如下:
- json.loads(JSON字符串)/json.load(JSON文件對象):JSON字符串/文件轉(zhuǎn)字典
- json.dumps(字典)、json.dump(字典刹碾,文件對象):字典轉(zhuǎn)JSON字符串/文件
使用示例如下燥撞。
import json
data = {
'name': '臨淵',
'age': 18,
'on_site': True,
'favorite': None
}
# dict --> JSON
data_str = json.dumps(data)
print('字典轉(zhuǎn)JSON字符串', type(data_str), data_str)
# JSON --> dict
data_dict = json.loads(data_str)
print('JSON字符串轉(zhuǎn)回字典', data_dict)
# dict --> JSON文件
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f)
# JSON文件 --> dict
with open('data.json', 'r', encoding='utf-8') as f:
data_dict = json.load(f)
print('JSON文件轉(zhuǎn)回字典', data_dict)
運行結(jié)果如下:
字典轉(zhuǎn)JSON字符串 <class 'str'> {"name": "\u4e34\u6e0a", "age": 18, "on_site": true, "favorite": null}
JSON字符串轉(zhuǎn)回字典 {'name': '臨淵', 'age': 18, 'on_site': True, 'favorite': None}
JSON文件轉(zhuǎn)回字典 {'name': '臨淵', 'age': 18, 'on_site': True, 'favorite': None}
生成的data.json文件內(nèi)容如下:
{"name": "\u4e34\u6e0a", "age": 18, "on_site": true, "favorite": null}
在使用json.dumps()將字典轉(zhuǎn)為JSON字符串時,默認為確保ASCII碼已方便HTTP傳輸會將中文進行轉(zhuǎn)換,同時默認使用單行格式物舒。如果想要更清晰的查看JSON字符串結(jié)果辆布,可以使用ensure_ascii=False
不進行轉(zhuǎn)換,使用indent=2
空2格縮進顯示茶鉴,sort_keys=True
按key排序輸出锋玲,示例如下。
import json
data = {
'name': '臨淵',
'age': 18,
'on_site': True,
'favorite': None
}
data_str = json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True)
print(data_str)
輸出格式如下:
{
"age": 18,
"favorite": null,
"name": "臨淵",
"on_site": true
}
通用的請求方法
PUT/DELETE等請求方法使用requests對應(yīng)的方法即可涵叮。
- requests.get(url, **kwargs):發(fā)送GET請求
- requests.post(url, **kwargs):發(fā)送POST請求
- requests.put(url, **kwargs):發(fā)送PUT請求
- requests.delete(url, **kwargs):發(fā)送DELETE請求
- requests.head(url, **kwargs):發(fā)送head請求
- erquests.options(url, **kwargs):發(fā)送options請求
這些請求方法的參數(shù)和用法一致惭蹂,必選參數(shù)為url,其他參數(shù)為可選參數(shù)割粮,常用參數(shù)如下盾碗。
- url: 字符串格式,參數(shù)也可以直接寫到url中
- params:url參數(shù)舀瓢,字典格式
- data: 請求數(shù)據(jù)廷雅,字典或字符串格式
- headers: 請求頭,字典格式
- cookies: 字典格式京髓,可以通過攜帶cookies繞過登錄
- files: 字典格式航缀,用于混合表單(form-data)中上傳文件
- auth: Basic Auth授權(quán),數(shù)組格式 auth=(user,password)
- timeout: 超時時間(防止請求一直沒有響應(yīng)堰怨,最長等待時間)芥玉,數(shù)字格式,單位為秒
這些方法都源于一個通用的請求方法requests.request(method, url, **kwargs)
备图。這個通用的方法通過必選參數(shù)method來指定使用的請求動作灿巧。字符串格式,不區(qū)分大小寫揽涮,即requests.get(url)相當于requests.request('get', url)抠藕。
因此我們可以用同樣結(jié)構(gòu)的的數(shù)據(jù)來組裝任何的HTTP請求,示例如下蒋困。
import requests
res = request.request(
method='post', # 也可以只寫'post',
url='https://httpbin.org/post', # 也可以只寫'https://httpbin.org/post',
headers={},
data={'name': '臨淵', 'password': '123456'}
)
print(res.text)
請求中也可以根據(jù)需求添加其他參數(shù)盾似,這一組組鍵值對參數(shù)可以使用一個統(tǒng)一的字典來表示,即:
req = {
'method': 'post',
'url': 'https://httpbin.org/post',
'headers: {},
'data': {'name': '臨淵', 'password': '123456'}
}
然后通過**req
字典解包家破,可以將一個字典參數(shù)req重新還原為其中的4組參數(shù)颜说,因此上例子可以改為购岗。
import requests
req = {
'method': 'post',
'url': 'https://httpbin.org/post',
'headers: {},
'data': {'name': '臨淵', 'password': '123456'}
}
res = request.request(**req)
print(res.text)
這樣做的好處是可以將任何類型的HTTP請求數(shù)據(jù)配置到數(shù)據(jù)文件中(如JSON或Yaml文件)汰聋,然后將數(shù)據(jù)轉(zhuǎn)為字典直接發(fā)送。示例如下喊积。
data.json文件內(nèi)容:
[
{
"method": "get",
"url": "https://httpbin.org/get"
},
{
"method": "post",
"url": "https://httpbin.org/post",
"headers": {},
"data": {"name": "臨淵", "password": "123456"}
}
]
發(fā)送請求腳本如下:
import requests
import json
with open('data.json', encoding='utf-8') as f:
datas = json.load(f) # 將JSON文件轉(zhuǎn)為字典
for req in datas:
res = requests.request(**req)
print(res.text)
SSL證書驗證
requests在請求HTTPS接口時烹困,默認驗證SSL證書躲胳,請求方法中默認參數(shù)為verify=True
蔫巩,如果想要關(guān)閉證書驗證叽讳,可以設(shè)置為False壳繁,示例如下。
requests.get('https://www.baidu.com', verify=False)
不自動重定向
當遇到重定向接口枯饿,requests默認跟隨重定向酝锅,返回所重定向接口的響應(yīng)對象(<Response [200]>),對于一些單點登錄后轉(zhuǎn)向的接口奢方,有時我們需要獲取原接口響應(yīng)中的token信息搔扁,則需要使用allow_redirects=False
關(guān)閉自動重定向,使用方法如下蟋字。
import requests
res = requests.get('https://httpbin.org/status/302')
print(res)
res = requests.get('https://httpbin.org/status/302', allow_redirects=False)
print(res)
第一個自動跟隨重定向稿蹲,返回<Response [200]>,關(guān)閉重定向后返回<Response [302]>鹊奖。
代理設(shè)置
requests支持使用代理苛聘,對于HTTP和HTTPS分別使用不同的代理,使用方式如下忠聚。
import requests
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)
超時設(shè)置
requests支持對請求設(shè)置超時時間设哗,以防止請求長時間無響應(yīng)而阻塞,設(shè)置方法如下两蟀。
import requests
requests.get('https://github.com', timeout=5) # 設(shè)置整體的超時時間5s
requests.get('https://github.com', timeout=(3, 2)) # 分別設(shè)置連接和下載響應(yīng)內(nèi)容的超時時間3s,2s熬拒。
如果在超時時間內(nèi)未完成響應(yīng),則拋出TimeoutError
授權(quán)設(shè)置(身份認證)
授權(quán)是請求身份驗證的一些開放協(xié)議標準垫竞,授權(quán)協(xié)議很多澎粟,包括Basic Auth基礎(chǔ)授權(quán),Digist Auth摘要授權(quán)欢瞪,Oauth等活烙。
Basic Auth
Basic Auth基礎(chǔ)授權(quán)使用用戶名和密碼來驗證身份,在requests中使用方法如下遣鼓。
import requests
requests.get('https://api.github.com/user', auth=('githab賬號', '密碼'))
OAuth2.0
需要使用requests-oauthlib啸盏,參考鏈接:https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html
會話保持及默認配置
會話Session一般指客戶端和服務(wù)端的一次連接交互過程。在使用requests.get()等方法時骑祟,每次會建立一個新的會話與服務(wù)器進行連接回懦。這樣不便于保持會話(如登錄)狀態(tài),如果想要保持會話狀態(tài)次企,可以使用同一個會話對象來請求所有接口怯晕,示例如下。
import requests
s = requests.Session() # 新建一個會話對象
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789') # 使用該會話對象請求
r = s.get("http://httpbin.org/cookies") # 使用同樣的會話對象請求
print(r.text)
會話對象還可以用來設(shè)置默認的請求頭等HTTP配置缸棵,示例如下舟茶。
s = requests.Session()
s.auth = ('user', 'pass') # 在會話中設(shè)置默認授權(quán)
s.headers.update({'x-test': 'true'}) # 在會話中設(shè)置默認請求頭
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'}) # 默認請求頭也會被發(fā)送
預(yù)制請求Preprared-request
假設(shè)有多個請求需要先準備好,再逐個發(fā)送,可以使用requests.Request()對象的prepare()方法生成預(yù)制請求對象吧凉,然后使用會話發(fā)送即可隧出,示例如下。
import requests
s = request.Session()
req1 = requests.Request('GET', 'https://httpbin.org/get').prepare()
req2 = requests.Request('POST', 'https://httpbin.org/post', data={'a':1}).prepare()
s.send(req1) # 發(fā)送預(yù)制請求
s.send(req2, headers={'x-test': 'true'}) # 支持添加額外項
使用適配器
適配器用于對匹配到的指定形式的請求做特殊處理阀捅,可以直接使用requests.adapters中的HTTPAdapter給定響應(yīng)參數(shù)胀瞪,也可以繼承HTTPAdapter或BaseAdapter自定義處理方式,示例如下饲鄙。
import requests
s = requests.Session()
a = requests.adapters.HTTPAdapter(max_retries=3) # 設(shè)置最大重試3次
s.mount('http://', a) # 對該會話所有http://開頭的請求使用
詳細可參考API:HTTPAdapter 及 BaseAdapter
并發(fā)請求
requests本身并不支持異步赏廓。想要并發(fā)請求常用的有多線程多進程或gevent方式。
多線程
直接使用threading的Thread對象即可傍妒,通過target指定要運行的方法幔摸,示例如下。
import requests
from threading import Thread
def print_res(res, *args, **kwargs):
print(res.text)
s = requests.Session()
s.hooks={'response': print_res}
t1 = Thread(target=s.get, args=('https://httpbin.org/get',)) # 指定線程運行方法
t2 = Thread(target=s.post, args=('https://httpbin.org/post',), kwargs={'data': {'a': 1}})
t1.start() # 啟動線程
t2.start()
t1.join() # 連接主線程
t2.join()
默認線程運行無法獲取target函數(shù)的運行結(jié)果颤练,這里給會話添加了默認hooks方法既忆,來打印響應(yīng)文本(也可以通過自定義請求方法來實現(xiàn))。
如果想獲取線程結(jié)果嗦玖,需要繼承Thread并編寫自己的線程處理類患雇,示例如下。
import requests
from threading import Thread
class MyThread(Thread):
def __init__(self, func, *args, **kwargs): # 改變線程的使用方式宇挫,可以直接傳遞函數(shù)方法和函數(shù)參數(shù)
super(MyThread, self).__init__()
self.func = func
self.args = args
self.kwargs = kwargs
self.result = None
def run(self):
self.result = self.func(*self.args, **self.kwargs) # 為線程添加屬性result存儲運行結(jié)果
t1 = MyThread(requests.get, 'https://httpbin.org/get')
t2 = MyThread(requests.post, 'https://httpbin.org/post', data={'a':1})
t1.start()
t2.start()
t1.join()
t2.join()
print(t1.result) # 響應(yīng)對象
print(t2.result.text) # 響應(yīng)對象的text屬性即響應(yīng)文本
使用gevent
pip install gevent
示例如下苛吱。
from gevent import monkey;monkey.patch_all() # 要放import requests上面
import requests
import gevent
g1 = gevent.spawn(requests.get, 'https://httpbin.org/get')
g2 = gevent.spawn(requests.post, 'https://httpbin.org/post', data={'a': 1})
gevent.joinall([g1, g2])
print(g1.value) # 響應(yīng)對象
print(g2.value)
使用grequests
pip install grequests
grequests封裝了gevent和requests方法,用起來更簡單器瘪,示例如下翠储。
import grequests
req_list = [
grequests.get('https://httpbin.org/get', ),
grequests.post('https://httpbin.org/post', data={'a': 1})
]
res_list = grequests.map(req_list)
print(res_list)
響應(yīng)處理
res是請求返回的響應(yīng)對象(變量名隨意)。res.text會自動將二進制格式的響應(yīng)數(shù)據(jù)橡疼,使用默認編碼轉(zhuǎn)為文本(字符串)格式援所。
res響應(yīng)對象包含各種響應(yīng)的信息,常用的如下欣除。
- res.content:二進制響應(yīng)數(shù)據(jù)
- res.text:將二進制響應(yīng)數(shù)據(jù)按默認編碼轉(zhuǎn)為文本(字符串格式)
- res.json():將JSON格式響應(yīng)文本(res.text)按轉(zhuǎn)為字典(W∈谩!历帚!非JSON格式響應(yīng)文本滔岳,使用此方法會報JSONDecoderError)
- res.status_code:狀態(tài)碼
- res.reason:狀態(tài)碼說明
- res.headers:響應(yīng)頭
- res.cookies:響應(yīng)Cookies(響應(yīng)Cookies中有時候不能包含所有響應(yīng)頭的Set-Cookies內(nèi)容,可以通過解析響應(yīng)頭獲韧炖巍)
- res.encoding:當前解碼格式谱煤,可以通過修改req.encoding來解決一部分亂碼問題
- res.apparent_encoding:明顯編碼,使用chardet庫對響應(yīng)數(shù)據(jù)分析出的編碼格式
亂碼處理
當res.encoding
解碼格式和res.apparent_encoding
明顯編碼格式不一致時卓研,便可能出現(xiàn)亂碼趴俘,如請求百度首頁,打印res.text會發(fā)現(xiàn)有亂碼奏赘,重新設(shè)置res.encoding
為明顯編碼的格式寥闪,再次打印res.text便可以修復(fù)亂碼。示例如下磨淌。
import requests
res = requests.get('https://www.baidu.com/')
print(res.text) # 有亂碼
print('解碼格式', res.encoding) # 解碼格式 ISO-8859-1
print('明顯編碼', res.apparent_encoding) # 明顯編碼 utf-8
res.encoding = res.apparent_encoding # 修改解碼格式
print(res.text) # 亂碼解決
文件下載(流式下載)
對應(yīng)資源類接口(如圖片鏈接)疲憋,想要保持文件,可以直接使用res.content按二進制保存文件即可梁只,示例如下缚柳。
import requests
res = requests.get('https://upload.jianshu.io/users/upload_avatars/7575721/5339c9d6-be6b-47cf-87cc-c0517467c6bc.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240')
with open('avatar.png', 'wb') as f:
f.write(res.content)
對應(yīng)較大的文件,可是使用流式下載搪锣,示例如下秋忙。
import requests
res = requests.get('https://upload.jianshu.io/users/upload_avatars/7575721/5339c9d6-be6b-47cf-87cc-c0517467c6bc.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240')
with open('avatar.png', 'wb') as f:
for data in res.iter_content(128): # 按128字節(jié)分塊保存
f.write(data)
with open('avatar.png', 'wb') as f:
for data in res.iter_content(128): # 按128字節(jié)分塊保存
f.write(data)
Hooks使用
Hooks即鉤子方法,用于在某個框架固定的某個流程執(zhí)行是捎帶執(zhí)行(鉤上)某個自定義的方法构舟。
requests庫只支持一個response的鉤子灰追,即在響應(yīng)返回時可以捎帶執(zhí)行我們自定義的某些方法」烦可以用于打印一些信息弹澎,做一些響應(yīng)檢查或想響應(yīng)對象中添加額外的信息,示例如下努咐。
import requests
url = 'https://httpbin.org/post'
def verify_res(res, *args, **kwargs):
print('url', res.url)
res.status='PASS' if res.status_code == 200 else 'FAIL'
res = requests.get(url, data=data, hooks={'response': verify_res})
print(res.text)
print(res.status)
執(zhí)行結(jié)果如下苦蒿。
https://httpbin.org/post
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>
FAIL
verfiy_res是我們自定義的方法,第一個參數(shù)為響應(yīng)對象渗稍,后面kwargs里是請求的一些配置佩迟。鉤子方法不能返回響應(yīng)對象以外的有意義值,否則會破壞后面對響應(yīng)對象的處理竿屹。
由于該接口只支持post請求音五,使用get請求時響應(yīng)狀態(tài)碼為405(請求方法不被允許),因此響應(yīng)對象被添加的status的值為FAIL羔沙。