HTTP請求:Requests的進階使用方法淺析
1 背景
上篇文章講解了requests模塊的基礎(chǔ)使用,其中有g(shù)et、put、post等多種請求方式,使用data、json等格式做為請求參數(shù),在請求體中添加請求頭部信息的常見信息,如:headers、cookies,以及對請求響應(yīng)的處理方法。接下來講解一下requests的高級用法。
2 進階方法舉例
2.1 requests.request()
method:提交方式(get|post);
url:提交地址;
**kwargs:14個控制訪問的參數(shù);

常用的參數(shù)有:params、data、json、headers、cookies,已在上篇文章中介紹過了,感興趣的朋友,可以到上篇文章再回顧一下。以下將講解與示例其他參數(shù)的使用。
示例:
2.1.1 files
請求攜帶文件,如果有的請求需要上傳文件,可以用它來實現(xiàn)。
import requests
# 上傳文件
f= {"files": open("favicon.ico", "rb") }
data = {"name": "上傳文件"}
requests.request(
    method = 'POST', 
    url = 'http://127.0.0.1:8080/example/request',  
    data = data,
    files = f
)需注意:favicon.ico文件需和當(dāng)前腳本在同一目錄下,如果不在,可以將文件名稱修改為文件路徑
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
# 1、Basic Auth認(rèn)證
res = requests.request(
    method = 'GET',
    url = 'http://127.0.0.1:8080/example/request',
    auth = HTTPBasicAuth("username", "password")
)
res.encoding = "gbk"
print(res.status)  # 200
# 2、DIGEST 認(rèn)證
res = requests.request(
    method = 'GET',
    url = 'http://127.0.0.1:8080/example/request',
    auth = HTTPDigestAuth("username", "password")
)
res.encoding = "gbk"
print(res.status)  # 200http auth認(rèn)證的兩種方式,分別為Basic方式和Digest認(rèn)證,其中:
Basic Auth的優(yōu)點是提供簡單的用戶驗證功能,其認(rèn)證過程簡單明了,適合于對安全性要求不高的系統(tǒng)或設(shè)備中;同樣存在缺點:輸入的用戶名,密碼 base64編碼后會出現(xiàn)在Authorization里,很容易被解析出來。
那么Digest對比Basic認(rèn)證有什么不同呢?
- Digest思想,是使用一種隨機數(shù)字符串,雙方約定好對哪些信息進行哈希運算,即可完成雙方身份的驗證。Digest模式避免了密碼在網(wǎng)絡(luò)上明文傳輸,提高了安全性,但它依然存在缺點,例如認(rèn)證報文被攻擊者攔截到攻擊者可以獲取到資源。
 - DIGEST 認(rèn)證提供了高于 BASIC 認(rèn)證的安全等級,但是和 HTTPS 的客戶端認(rèn)證相比仍舊很弱。
 - DIGEST 認(rèn)證提供防止密碼被竊聽的保護機制,但并不存在防止用戶偽裝的保護機制。
 - DIGEST 認(rèn)證和 BASIC 認(rèn)證一樣,使用上不那么便捷靈活,且仍達不到多數(shù) Web 網(wǎng)站對高度安全等級的追求標(biāo)準(zhǔn)。因此它的適用范圍也有所受限。
 
2.1.2 timeout
請求和響應(yīng)的超時時間,在網(wǎng)絡(luò)響應(yīng)延遲或者無響應(yīng)時,可以通過設(shè)置超時時間,避免等待。
import requests
# 設(shè)置請求超時1秒,1秒后無響應(yīng),將拋出異常,1秒為connect和read時間總和
requests.request(
    method = 'POST',
    url = 'http://127.0.0.1:8080/example/request',
    json = {'k1' : 'v1', 'k2' : 'v2'},
    timeout = 1
)
# 分別設(shè)置connect和read的超時時間,傳入一個數(shù)組
requests.request(
    method = 'POST',
    url = 'http://127.0.0.1:8080/example/request',
    json = {'k1' : 'v1', 'k2' : 'v2'},
    timeout = (5, 15)
)
# 永久等待
requests.request(
    method = 'POST',
    url = 'http://127.0.0.1:8080/example/request',
    json = {'k1' : 'v1', 'k2' : 'v2'},
    timeout = None
    # 或者刪除timeout參數(shù)
)
# 捕捉超時異常
from requests.exceptions import ReadTimeout
try:
    res = requests.get('http://127.0.0.1:8080/example/request', timeout=0.1)
    print(res.status_code)
except ReadTimeout:
    print("捕捉到超時異常")2.1.3 allow_redirects
設(shè)置重定向開關(guān)。
>>> import requests
>>> r = requests.get('http://github.com')
>>> r.url
'https://github.com/'
>>> r.status_code
200
>>> r.history
[<Response [301]>]
# 如果使用GET、OPTIONS、POST、PUT、PATCH或DELETE,則可以使用allow_redirects參數(shù)禁用重定向
>>> r = requests.get('http://github.com', allow_redirects=False)
>>> r.status_code
301
>>> r.history
[]
# 用HEAD啟動重定向
>>> r = requests.head('http://github.com', allow_redirects=True)
>>> r.url
'https://github.com/'
>>> r.history
[<Response [301]>]
import requests
import re
# 第一次請求
r1=requests.get('https://github.com/login')
r1_cookie=r1.cookies.get_dict() #拿到初始cookie(未被授權(quán))
authenticity_token=re.findall(r'name="authenticity_token".*?value="(.*?)"',r1.text)[0] #從頁面中拿到CSRF TOKEN
# 第二次請求:帶著初始cookie和TOKEN發(fā)送POST請求給登錄頁面,帶上賬號密碼
data={
    'commit':'Sign in',
    'utf8':'?',
    'authenticity_token':authenticity_token,
    'login':'xxxxxx@qq.com',
    'password':'password'
}
# 測試一:沒有指定allow_redirects=False,則響應(yīng)頭中出現(xiàn)Location就跳轉(zhuǎn)到新頁面,
# r2代表新頁面的response
r2=requests.post('https://github.com/session',
             data=data,
             cookies=r1_cookie
             )
print(r2.status_code) # 200
print(r2.url) # 看到的是跳轉(zhuǎn)后的頁面
print(r2.history) # 看到的是跳轉(zhuǎn)前的response
print(r2.history[0].text) # 看到的是跳轉(zhuǎn)前的response.text
# 測試二:指定allow_redirects=False,則響應(yīng)頭中即便出現(xiàn)Location也不會跳轉(zhuǎn)到新頁面,
# r2代表的仍然是老頁面的response
r2=requests.post('https://github.com/session',
             data=data,
             cookies=r1_cookie,
             allow_redirects=False
             )
print(r2.status_code) # 302
print(r2.url) # 看到的是跳轉(zhuǎn)前的頁面https://github.com/session
print(r2.history) # []2.1.4 proxies
同添加headers方法一樣,代理參數(shù)是dict。
import requests
import re
def get_html(url):
    proxy = {
        'http': '120.25.253.234:812',
        'https' '163.125.222.244:8123'
    }
    heads = {}
    heads['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0'
    req = requests.get(url, headers=heads,proxies=proxy)
    html = req.text
    return html
def get_ipport(html):
    regex = r'<td data-title="IP">(.+)</td>'
    iplist = re.findall(regex, html)
    regex2 = '<td data-title="PORT">(.+)</td>'
    portlist = re.findall(regex2, html)
    regex3 = r'<td data-title="類型">(.+)</td>'
    typelist = re.findall(regex3, html)
    sumray = []
    for i in iplist:
        for p in portlist:
            for t in typelist:
                pass
            pass
        a = t+','+i + ':' + p
        sumray.append(a)
    print('代理')
    print(sumray)
if __name__ == '__main__':
    url = 'http://www.baidu.com'
    get_ipport(get_html(url))某些接口增加了防騷擾模式,對于大規(guī)模且頻繁的請求,可能會彈出驗證碼,或者跳轉(zhuǎn)到登錄驗證頁面,或者封禁IP地址,此時如果想要正常訪問,可以通過設(shè)置代理來解決這個問題。
除了基本的HTTP代理外,requests還支持SOCKS協(xié)議的代理。
# 安裝socks庫
pip3 install "requests[socks]"
# 進行代理
import requests
proxies = {
    'http': 'socks5://user:password@host:port',
    'https': 'socks5://user:password@host:port'
}
res = requests.get('http://www.baidu.com', proxies=proxies)
print(res.status)  # 2002.1.5 hooks
即鉤子方法,requests庫只支持一個response的鉤子,即在響應(yīng)返回時,可以捎帶執(zhí)行自定義方法。可以用于打印一些信息、做一些響應(yīng)檢查、或者向響應(yīng)中添加額外的信息。
import requests
url = 'http://www.baidu.com'
def verify_res(res, *args, **kwargs):
    print('url', res.url)
    res.status='PASS' if res.status_code == 200 else 'FAIL'
res = requests.get(url, hooks={'response': verify_res})
print(res.text) # <!DOCTYPE html><!--STATUS OK--><html> 
print(res.status) # PASS2.1.6 stream
獲取內(nèi)容立即下載開關(guān),response會將報文一次性全部加載到內(nèi)存中,如果報文過大,可以使用此參數(shù),迭代下載。
import requests
url="http://www.baidu.com"
r = requests.get(url, stream=True)
# 解析response_body,以\n分割
for lines in r.iter_lines():
    print("lines:", lines)
# 解析response_body,以字節(jié)分割
for chunk in r.iter_content(chunk_size=1024):
    print("chunk:", chunk)2.1.7 verify
認(rèn)證SSL證書開關(guān),當(dāng)發(fā)送HTTPS請求的時候,如果該網(wǎng)站的證書沒有被CA機構(gòu)信任,程序?qū)箦e,可以使用verify參數(shù)控制是否檢查SSL證書。
# 1、直接設(shè)置
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
# 2、請求時雖然設(shè)置了跳過檢查,但是程序運行時仍然會產(chǎn)生警告,警告中包含建議給我們的指定證書
# 可以通過設(shè)置,忽略屏蔽這個警告
from requests.packages import urllib3  # 如果報錯,則直接引入import urllib3
# 3、屏蔽警告
urllib3.disable_warnings()
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code) # 200
# 4、通過cert直接聲明證書
# 本地需要有crt和key文件(key必須是解密狀態(tài),加密狀態(tài)的key是不支持的),并指定它們的路徑,
response = requests.get('https://www.12306.cn',cert('/path/server.crt','/path/key'))
print(response.status_code) # 2002.2 requests庫的異常
如何判斷是否出現(xiàn)異常呢?
2.2.1 raise_for_status()
該方法在內(nèi)部判斷res.status_code是否等于200,不是則產(chǎn)生異常HTTPError示例:
# 1、HTTPError異常示例
import requests
from requests.exceptions import HTTPError
try:
    res = requests.post("http://127.0.0.1:8080/example/post")
    res.raise_for_status()
    # 等同于
    if res.status != 200:
        raise HTTPError
    return res
except HTTPError:
    return False2.2.2 ReadTimeout
該異常類型,將會捕捉到因請求/響應(yīng)超時的請求。
# Timeout超時異常
import requests
from requests.exceptions import ReadTimeout
try:
    res = requests.get('http://127.0.0.1:8080/example/post',timeout=0.5)
    print(res.status_code)
    return res
except ReadTimeout:
    print('timeout')2.2.3 RequestException
該異常類型,將會捕捉到因無請求引起的異常請求。
# RquestError異常
import requests
from requests.exceptions import RequestException
try:
    res = requests.get('http://127.0.0.1:8080/example/post')
    print(res.status_code)
    return res
except RequestException:
    print('reqerror')3 總結(jié)
看到這里,大家應(yīng)該明白了,requests庫是一個比urilib2模塊更加簡潔的第三方庫,它具有如下的特點:
- 支持HTTP連接保持和連接池
 - 支持使用cookie、session保持會話
 - 支持文件上傳
 - 支持自動響應(yīng)內(nèi)容的編碼
 - 支持國際化的URL和Post數(shù)據(jù)自動編碼
 - 支持自動實現(xiàn)持久連接keep-alive
 
因此,requests這個高度封裝的模塊,可以使我們的HTTP請求,變得更加人性化,使用它將可以輕而易舉的完成瀏覽器請求的任何操作,充分詮釋了它的口號:“HTTP for Humans”。
作者:京東物流 駱銅磊
來源:京東云開發(fā)者社區(qū)















 
 
 
 
 
 
 