目錄
前言
一、文件上傳
二、Cookies
三、會話維持
四、SSL證書驗證
五、代理設置
六、超時設置
七、身份認證
八、Prepared Request
前言
????????上一節,我們認識了requests庫的基本用法,像發起GET、POST請求,以及了解Response對象是什么。這一節,咱們接著講講requests庫的一些高級用法,比如怎么上傳文件,如何設置Cookies和代理 。?
一、文件上傳
????????用requests,不光能模擬提交普通數據,實現文件上傳也不在話下,而且操作起來很容易。?
示例代碼如下:
import requests
files = {'file': open('favicon.ico', 'rb')}
r = requests.post("http://httpbin.org/post", files=files)
print(r.text)
??????? 要是之前保存過favicon.ico文件,這段代碼就能拿它模擬文件上傳。其中 httpbin.org 是一個基于 Python 和 Flask 開發的 HTTP 請求與響應服務網站。得注意,favicon.ico得和運行的腳本放在同一個文件夾里。要是想用其他文件模擬,改改代碼就行。?
運行上述代碼,得到類似如下結果:
{
? "args": {},
? "data": "",
? "files": {
??? "file": "data:application/octet-stream;base64,AAABAAEAgIAAAAAAIAAoCAEA......
? },
? "form": {},
? "headers": {
??? "Accept": "*/*",
??? "Accept-Encoding": "gzip, deflate",
??? "Content-Length": "67793",
??? "Content-Type": "multipart/form-data; boundary=80503f6283733c2f23804bb45111c889",
??? "Host": "httpbin.org",
??? "User-Agent": "python-requests/2.31.0",
??? "X-Amzn-Trace-Id": "Root=1-67f481ae-65a8a7c32119664b07375e48"
? },
? "json": null,
? "origin": "14.18.236.76",
? "url": "http://httpbin.org/post"
}
????????上面展示的結果,省略了一些內容。網站給出的響應里有files字段,form字段卻是空的。這就說明,在文件上傳時,會通過專門的files字段來標記上傳的文件。
二、Cookies
????????以前用urllib處理Cookies,代碼寫起來挺麻煩。但requests不一樣,獲取和設置Cookies簡單多了,操作一步就能搞定。下面通過實例,看看怎么用requests獲取Cookies:?
import requests
r = requests.get("https://www.baidu.com")
print(r.cookies)
for key, value in r.cookies.items():print(key + '=' + value)
運行代碼,結果如下:
<RequestsCookieJar[<Cookie BD0RZ=27315 for .baidu.com/>, <Cookie bsi=135335943568134141940014NN20303C0FNNNforw.baidu.com/>]>
BDORZ=27315
bsi=135335943568134141940014NN2030302FNNNO
????????先調用cookies屬性,就能得到Cookies,它的數據類型是RequestCookieJar。然后用items()方法把它轉成由元組組成的列表,一個個遍歷輸出每個Cookie的名稱和值,這樣就完成了Cookie的遍歷解析。?
????????用Cookie能維持登錄狀態。就拿知乎來說,登錄知乎后,把Headers里的Cookie內容復制出來(也可以換成你自己的Cookie),設置到Headers里再發送請求,下面是示例代碼:
cookie = "_zap=5852a467-84d5-48cd-b719-20cd08603c31; d_c0=a9CTVTvcRBqPTm6b5PunENpMjXLxX-7IGgk=|1744077733; captcha_session_v2=2|1:0|10:1744077734|18:captcha_session_v2|88:QkNNRGpaLzFMQ3NncGFVRXIxb1pSTXZlb3Axb3pCaGNibzlPRFI4cHhEUlpsU3ZyVUF5b0xGL2haSE1oS0xlZw==|d22291e8cfbad9e62e3ecf5b5879bd681e0dc56a87772f4b0a1bd674bfb6dc53; __snaker__id=1aUlWy7fvUjwSn3n; gdxidpyhxdE=%2BtQHnNHABo%2BnC7cQZNp5uprsnqVoGdyWn2QbuZGkZQ4K0Nio4pOnXNCRdPPZGh%2BOAJ8CYYhNxL%2B9bl%5CMngU%5Cb16zCg3nm9eIK%5C6dD0g%2BwE6q%2BUlwgi0dAj6TOMH5%2FqRH%5CYay%2BezxLU01dKTr4k9QPsXGtdRZ%2BkD6xU%5CxS6RKyHTSXxlh%3A1744078635509; q_c1=6573efe200e34e6e9cb0469042bfafde|1744077773000|1744077773000; z_c0=2|1:0|10:1744077775|4:z_c0|92:Mi4xUHZCWVJnQUFBQUJyMEpOVk85eEVHaGNBQUFCZ0FsVk56ZEhoYUFCU1V6WFFiOURmc3VsUnoyd0NtYUdYdTl6N1dB|29591bd5fc696067e49e9509e5fac6fd4663498734ece22be113ccb401123931; _xsrf=ee0ea1ed-31f9-45ab-a923-fc97da5d7663; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1744077734,1744104477; HMACCOUNT=29A6B844A9199C82; SESSIONID=alwM2PqMj7oxC3sRO72GFRwFMAnqV5ntSfBTuVLEheH; JOID=UV0WC0NgDf9xcWbsRRC3LEFGsaVfB2eDPEgwmwMBPJYCMl6ZEX3sAB18ZuFOibPgrBwBunupBMfuYiuxq0TeVS8=; osd=UVoSBkxgCvt8fmbrQR24LEZCvKpfAGOOM0g3nw4OPJEGP1GZFnnhDx17YuxBibTkoRMBvX-kC8fpZia-q0PaWCA=; Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49=1744104666; tst=v; BEC=8b4a1b0a664dd5d88434ef53342ae417; unlock_ticket=AOBXjeH6uhYXAAAAYAJVTeLz9Gd8qlW4_xKazEBGBzAktCSlnf9y0w=="
import requestsheaders = {'Cookie': cookie,'Host': 'www.zhihu.com','User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0sX10 11 4)AppleWebKit/537.36(KHTML, like Gecko)Chrome/53.0.2785.116 Safari/537.36'
}
r = requests.get('https://www.zhihu.com/zvideo', headers=headers)
print(r.text)
????????程序運行后,得到的結果里有登錄后的頁面內容,這就說明成功登錄了。
????????除了前面的方法,我們還能通過cookies參數來設置Cookie。不過,這種方式得先構造RequestsCookieJar對象,還要對獲取到的cookies進行分割,操作相對麻煩。下面來看示例:?
import requests
cookie = "_zap=5852a467-84d5-48cd-b719-20cd08603c31; d_c0=a9CTVTvcRBqPTm6b5PunENpMjXLxX-7IGgk=|1744077733; captcha_session_v2=2|1:0|10:1744077734|18:captcha_session_v2|88:QkNNRGpaLzFMQ3NncGFVRXIxb1pSTXZlb3Axb3pCaGNibzlPRFI4cHhEUlpsU3ZyVUF5b0xGL2haSE1oS0xlZw==|d22291e8cfbad9e62e3ecf5b5879bd681e0dc56a87772f4b0a1bd674bfb6dc53; __snaker__id=1aUlWy7fvUjwSn3n; gdxidpyhxdE=%2BtQHnNHABo%2BnC7cQZNp5uprsnqVoGdyWn2QbuZGkZQ4K0Nio4pOnXNCRdPPZGh%2BOAJ8CYYhNxL%2B9bl%5CMngU%5Cb16zCg3nm9eIK%5C6dD0g%2BwE6q%2BUlwgi0dAj6TOMH5%2FqRH%5CYay%2BezxLU01dKTr4k9QPsXGtdRZ%2BkD6xU%5CxS6RKyHTSXxlh%3A1744078635509; q_c1=6573efe200e34e6e9cb0469042bfafde|1744077773000|1744077773000; z_c0=2|1:0|10:1744077775|4:z_c0|92:Mi4xUHZCWVJnQUFBQUJyMEpOVk85eEVHaGNBQUFCZ0FsVk56ZEhoYUFCU1V6WFFiOURmc3VsUnoyd0NtYUdYdTl6N1dB|29591bd5fc696067e49e9509e5fac6fd4663498734ece22be113ccb401123931; _xsrf=ee0ea1ed-31f9-45ab-a923-fc97da5d7663; Hm_lvt_98beee57fd2ef70ccdd5ca52b9740c49=1744077734,1744104477; HMACCOUNT=29A6B844A9199C82; SESSIONID=alwM2PqMj7oxC3sRO72GFRwFMAnqV5ntSfBTuVLEheH; JOID=UV0WC0NgDf9xcWbsRRC3LEFGsaVfB2eDPEgwmwMBPJYCMl6ZEX3sAB18ZuFOibPgrBwBunupBMfuYiuxq0TeVS8=; osd=UVoSBkxgCvt8fmbrQR24LEZCvKpfAGOOM0g3nw4OPJEGP1GZFnnhDx17YuxBibTkoRMBvX-kC8fpZia-q0PaWCA=; Hm_lpvt_98beee57fd2ef70ccdd5ca52b9740c49=1744104666; tst=v; BEC=8b4a1b0a664dd5d88434ef53342ae417; unlock_ticket=AOBXjeH6uhYXAAAAYAJVTeLz9Gd8qlW4_xKazEBGBzAktCSlnf9y0w=="
cookies = cookie
jar = requests.cookies.RequestsCookieJar()
headers = {'Host': 'www.zhihu.com','User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0sX10 11 4)AppleWebKit/537.36 (KHTML, like Gecko)Chrome/53.0.2785.116 afari/537.36'
}
for cookie in cookies.split(';'):key, value = cookie.split('=', 1)jar.set(key, value)
r = requests.get("http://www.zhihu.com", cookies=jar, headers=headers)
print(r.text)
????????首先,創建一個RequestsCookieJar對象。接著,把復制來的cookies進行分割,利用set()方法,為每個Cookie設置好對應的key和value。之后,調用requests庫的get()方法,將處理好的cookies作為參數傳入。由于知乎網站自身的訪問規則限制,headers參數是必須設置的,但不用在headers里再單獨設置cookie字段。經過測試,用這種方法同樣能順利登錄知乎。?
三、會話維持
????????當我們用requests的get()或者post()方法模擬網頁請求時,每次請求就相當于開啟了一個新會話,這就好比用兩個不同的瀏覽器打開不同頁面。舉個例子,假如我們先用post()方法登錄網站,接著用get()方法去請求個人信息頁面,這兩次請求就像是在兩個獨立的瀏覽器中操作,所以沒辦法成功獲取個人信息。
???????? 有的朋友可能會想,通過設置相同的cookies能不能解決這個問題呢?確實可以,不過操作起來特別麻煩。其實,更好的辦法是維持同一會話,這就像在同一個瀏覽器里打開新的選項卡一樣。要實現這一點,我們可以借助Session對象。Session對象使用起來很方便,它不僅能維護會話,還能自動幫我們處理cookies相關問題。下面來看具體示例:
import requests
requests.get('http://httpbin.org/cookies/set/number/123456789')
r = requests.get('http://httpbin.org/cookies')
print(r.text)
????????上述代碼請求了測試網址http://httpbin.org/cookies/set/number/123456789設置cookie,名稱為number,內容是123456789,隨后請求http://httpbin.org/cookies獲取當前Cookies。運行結果如下:
{"cookies": {}
}
????????可見,直接請求無法獲取設置的Cookies。使用Session對象再次嘗試:
import requests
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)
運行結果如下:
{"cookies": {"number": "123456789"}
}
????????這下成功獲取數據了!從這就能看出,同一會話和不同會話存在明顯差別。在實際應用里,Session用得特別多,尤其是模擬登錄成功后,進行下一步操作的時候。比如說,模擬在同一個瀏覽器中,打開同一網站的不同頁面。后面專門有章節,會對這部分內容進行詳細講解。?
四、SSL證書驗證
????????requests帶有證書驗證功能。當我們發送HTTP請求時,它會對SSL證書進行檢查。通過verify參數,就能決定要不要執行這項檢查。要是不設置verify參數,它默認是True,也就是會自動驗證證書。?
??????? 之前說過,很久以前的12306網站的證書不被官方CA機構認可,訪問時就會出現證書驗證錯誤。
下面用requests做個測試:?
import requests
response = requests.get("https://www.12306.cn")
print(response.status_code)
運行結果如下:
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'tls process server certificate', 'certificate verify failed')],)")
????????程序提示SSLError錯誤,這說明證書驗證沒通過。當我們請求HTTPS網站,要是證書驗證出了問題,就會彈出這個錯誤。要是想避開這個錯誤,把verify參數設為False就行。具體代碼如下:?
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
????????運行后會打印出請求成功的狀態碼,但會報出警告:
/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py:852:InsecureRequestWarning: UnverifiedHTTPS request is being made, Adding certificate verification is strongly advised. See:https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warningsInsecureRequestWarning)
200
????????可通過設置忽略警告或捕獲警告到日志的方式屏蔽該警告。設置忽略警告:
import requests
from requests.packages import urllib3
urllib3.disable_warnings()
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
捕獲警告到日志:
import logging
import requests
logging.captureWarnings(True)
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
????????除了上面的方法,我們還能指定本地證書當作客戶端證書。這個本地證書,既可以是一個同時包含密鑰和證書的文件,也能是一個包含兩個文件路徑的元組 :?
import requests
response = requests.get('https://www.12306.cn', cert=('/path/server.crt', '/path/key'))
print(response.status_code)
????????上面這些代碼只是用來演示的。在實際使用的時候,你得有crt和key這兩個文件,并且要把它們的正確路徑指出來。還有一點要注意,本地私有證書的key必須處于解密狀態,要是key是加密狀態,程序不支持。?
五、代理設置
????????測試某些網站時,少量請求能正常獲取內容,但大規模爬取時,頻繁請求可能導致網站彈出驗證碼、跳轉到登錄認證頁面,甚至封禁客戶端IP。為避免這種情況,需設置代理,可通過proxies參數實現,示例如下:
import requests
proxies = {"http": "http://10.10.1.10:3128","https": "http://10.10.1.10:1080"
}
requests.get("https://www.taobao.com", proxies=proxies)
????????上述代理可能無效,需替換為有效代理進行試驗。若代理需要使用HTTP Basic Auth,可使用類似http://user:password@host:port的語法設置代理,示例如下:
import requests
proxies = {"http": "http://user:password@10.10.1.10:3128/"
}
requests.get("https://www.taobao.com", proxies=proxies)
????????除基本的HTTP代理外,requests還支持SOCKS協議的代理。使用前需安裝socks庫:
pip3 install 'requests[socks]'
????????安裝后即可使用SOCKS協議代理,示例如下:
import requests
proxies = {'http':'socks5://user:password@host:port','https':'socks5://user:password@host:port'
}
requests.get("https://www.taobao.com", proxies=proxies)
六、超時設置
????????要是你自己電腦的網絡不好,或者服務器那邊響應特別慢,甚至壓根沒反應,那你可能要等很久才能收到響應,弄不好最后還會報錯。為了避免一直傻等,我們可以設定一個超時時間。意思就是,過了這個時間還沒收到服務器的回應,程序就報錯。這可以通過timeout參數來設置,例子如下:?
import requests
r = requests.get("https://www.taobao.com", timeout =1)
print(r.status_code)
????????上面代碼里把超時時間設成了1秒,要是1秒內沒收到響應,就會拋出異常。請求其實分兩個階段,一個是連接服務器(connect),另一個是讀取數據(read)。剛才設置的timeout是這兩個階段加起來的總超時時間。要是你想分別給這兩個階段設置超時時間,可以傳一個元組進去:?
r = requests.get('https://www.taobao.com', timeout=(5, 11.30))
????????若想永久等待,可將timeout設置為None,或不設置(默認值為None),用法如下:
r = requests.get('https://www.taobao.com', timeout=None)
????????或直接不加參數:
r = requests.get('https://www.taobao.com')
七、身份認證
????????訪問網站時可能遇到認證頁面,requests自帶身份認證功能,示例如下:
import requests
from requests.auth import HTTPBasicAuth
r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'))
print(r.status_code)
????????若用戶名和密碼正確,請求時自動認證成功,返回200狀態碼;若認證失敗,返回401狀態碼。直接傳遞HTTPBasicAuth類作為參數較繁瑣,requests提供了更簡便的寫法,直接傳一個元組,它會默認使用HTTPBasicAuth類進行認證,代碼可簡寫為:
import requests
r = requests.get('http://localhost:5000', auth=('username', 'password'))
print(r.status_code)
????????此外,requests還提供其他認證方式,如OAuth認證。使用OAuth認證需安裝oauth包,安裝命令如下:
pip3 install requests_oauthlib
????????使用OAuth1認證的方法如下:
import requests
from requests_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url, auth=auth)
????????更多詳細功能可參考requests_oauthlib的官方文檔https://requests-oauthlib.readthedocs.org/。
八、Prepared Request
????????在介紹urllib時,可將請求表示為數據結構,各參數通過一個對象表示。requests中也有類似功能,對應的數據結構是Prepared Request。示例如下:
from requests import Request, Session
url = "http://httpbin.org/post"
data = {'name': 'germey'}
headers = {'User-Agent': 'Mozilla/5.0(Macintosh; Intel Mac0sX10 11 4)AppleWebKit/537.36 (KHTML, like Gecko)Chrome/53.0.2785.116 Safari/537.36'
}
s = Session()
req = Request('POST', url, data=data, headers=headers)
prepped = s.prepare_request(req)
r = s.send(prepped)
print(r.text)
????????引入Request后,用url、data和headers參數構造Request對象,再調用Session的prepare_request()方法將其轉換為Prepared Request對象,最后調用send()方法發送請求。運行結果如下:
{"args": {},"data": "","files": {},"form": {"name": "germey"},"headers": {"Accept": "*/*","Accept-Encoding": "gzip, deflate","Connection": "close","Content-Length": "11","Content-Type": "application/x-www-form-urlencoded","Host": "httpbin.org","User-Agent": "Mozilla/5.0(Macintosh; Intel Mac 0sX10 11 4)AppleWebKit/537.36 (KHTML, like Gecko)Chrome/53.0.2785.116 Safari/537.36"},"json": null,"origin": "182.32.203.166","url": "http://httpbin.org/post"
}
????????從結果能看出,我們通過這種方式實現了和普通POST請求一樣的效果。有了Request對象后,我們可以把每個請求當成一個獨立個體。這在進行隊列調度時特別方便,后續我們會利用它來構建一個Request隊列。?
????????這一節給大家介紹了requests的一些高級用法,在后面實際項目中,這些用法會經常用到,大家要熟練掌握。要是還想了解更多requests的用法,可以查看它的官方文檔:http://docs.python-requests.org/ 。?
參考學習書籍:Python 3網絡爬蟲開發實戰