昨天,咱們更多的討論了request的基礎API,讓咱們對它有了基礎的認知。學會上一課程,咱們已經能寫點基本的爬蟲了。可是還不夠,由於,不少站點是須要登陸的,在站點的各個請求之間,是須要保持回話狀態的,有的站點還須要證書驗證,等等這一系列的問題,咱們將在今天這一環節,加以討論。html
會話:session,就是你點進這個站點後,由瀏覽器與服務器之間保持的一次鏈接。此次鏈接裏面,你跳轉頁面,或發起其餘請求,服務器要求某些數據驗證。服務器不會叫你在每次跳轉時候進行驗證,而是用已驗證的結果進行跳轉。這樣就節省服務器資源,底層的TCP鏈接也會被重用。python
跨請求保持數據(客戶端存數據):git
s = requests.Session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get("http://httpbin.org/cookies")
print(r.text)github
回話提供請求默認數據(數據會被存到服務端):json
s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})
# 'x-test' 和 'x-test2' 一塊兒發送給url
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})api
注意:即使使用了session,方法級別的參數,仍然不會再跨請求保持。瀏覽器
如下代碼,另個請求分別有本身的cookies安全
s = requests.Session()
r = s.get('http://httpbin.org/cookies')
print(r.text)
# '{"cookies": {}}'
r = s.get('http://httpbin.org/cookies', cookies={'from-my': 'browser'})
print(r.text)
# '{"cookies": {"from-my": "browser"}}'服務器
會話上下文管理器:是指用with 塊限定會話對象的使用範圍cookie
with requests.Session() as s:
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
任什麼時候候,咱們往服務器發消息,都會返回一個response的響應對象,同時,還能得到咱們本身建立的request對象
r = requests.get('http://en.wikipedia.org/wiki/Monty_Python')
print (r.headers)
print (r.request.headers)
在發送請求以前,須要對body獲header作一些額外處理,使用以下方法:
from requests import Request, Session
s = Session()
req = Request('GET', url,
data=data,
headers=header
)
#要想獲取有狀態的請求,使用:
prepped = s.prepare_request(req)
而不是使用:
prepped = req.prepare()
# do something with prepped.body
# do something with prepped.headers
resp = s.send(prepped,
stream=stream,
verify=verify,
proxies=proxies,
cert=cert,
timeout=timeout
)
print(resp.status_code)
requests.get('https://requestb.in')
requests.get('https://github.com', verify=True)
requests.get('https://github.com', verify='/path/to/certfile')
# 將驗證路徑保持在會話中
s = requests.Session()
s.verify = '/path/to/certfile'
# 忽略對SSL的驗證
requests.get('https://kennethreitz.org', verify=False)
requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
#證書在會話中
s = requests.Session()
s.cert = '/path/client.cert'
注意:本地證書的私有 key 必須是解密狀態。目前,Requests 不支持使用加密的 key。
這裏補充一句:當登陸12306時喊你要安裝證書。當向有證書驗證要求的站點,就使用上面的代碼
Requests 默認附帶了一套它信任的根證書,來自於 Mozilla trust store。然而它們在每次 Requests 更新時纔會更新。這意味着若是你固定使用某一版本的 Requests,你的證書有可能已經 太舊了。
從 Requests 2.4.0 版以後,若是系統中裝了 certifi 包,Requests 會試圖使用它裏邊的 證書。這樣用戶就能夠在不修改代碼的狀況下更新他們的可信任證書。
import requests
# 默認狀況下,發起請求會同時下載響應頭和響應體(就是響應內容)
tarball_url = 'https://github.com/kennethreitz/requests/tarball/master'
# 若是將stream=True 則會推遲響應內容的下載
r = requests.get(tarball_url, stream=True)
# 這裏就是:知足某種條件纔去下載
if int(r.headers['content-length']) < TOO_LONG:
content = r.content
# 在請求中把 stream 設爲 True,Requests 沒法將鏈接釋放回鏈接池,
# 除非消耗了全部的數據,或者調用了 Response.close。
# 這樣會帶來鏈接效率低下的問題。若是在使用 stream=True 的同時還在部分讀取請求的 body(或者徹底沒有讀取 body),
# 那麼就應該使用 with 語句發送請求,這樣能夠保證請求必定會被關閉
with requests.get('http://httpbin.org/get', stream=True) as r:
# 這裏處理響應
content = r.content
同一會話內的持久鏈接是徹底自動處理的,同一會話內你發出的任何請求都會自動複用恰當的鏈接。
注意:只有全部的響應體數據被讀取完畢鏈接纔會被釋放回鏈接池;因此確保將 stream 設置爲 False 或讀取 Response 對象的 content 屬性。
Requests支持流式上傳,這容許發送大的數據流或文件,而無需先把它們讀入內存。要使用流式上傳,需,爲請求體,提供一個類文件對象便可:
with open('massive-body') as f:
requests.post('http://some.url/streamed', data=f)
注意的問題:
最好使用二進制模式打開文件。這是由於 requests 有默認設置 header 中的 Content-Length,在這種狀況下該值會被設爲文件的字節數。若是用文本模式打開文件,就可能碰到錯誤
對於出去和進來的請求,Requests 也支持分塊傳輸編碼。要發送一個塊編碼的請求,僅需爲請求體提供一個生成器(或任意沒有具體長度的迭代器)
def gen():
yield 'hi'
yield 'there'
requests.post('http://some.url/chunked', data=gen())
注意:
對於分塊的編碼請求,最好使用 Response.iter_content() 對其數據進行迭代。在理想狀況下,request 會設置 stream=True,這樣就能夠經過調用 iter_content 並將分塊大小參數設爲 none,從而進行分塊的迭代。若是要設置分塊的最大致積,能夠把分塊大小參數設爲任意整數。
說白了,就是段點續傳
妹的,報了一堆錯。先記錄在這,後面遇到了再研究
上傳圖片
import requests
url = 'http://httpbin.org/post'
multiple_files = [
('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
r = requests.post(url, files=multiple_files)
print (r.text)
import requests
def print_url(r, *args, **kwargs):
print(r.url)
# Requests有一個鉤子系統,你能夠用來操控部分請求過程,或信號事件處理。
# 在產生響應以前調用print_url 就是server響應以前的回調
hooks = dict(response=print_url)
r = requests.get('http://httpbin.org', hooks=dict(response=print_url))
print (r.text)
也沒看有什麼用
import requests
from requests.auth import AuthBase
class PizzaAuth(AuthBase):
def __init__(self, username):
# setup any auth-related data here
self.username = username
def __call__(self, r):
# modify and return the request
r.headers['X-Pizza'] = self.username
return r
#注意:auth參數必須是一個可調用對象 實現了 __call__ 方法
requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))
做用就是在請求發出以前,有機會修改請求
import requests
r = requests.get('http://httpbin.org/stream/20', stream=True)
for line in r.iter_lines():
# filter out keep-alive new lines
if line:
decoded_line = line.decode('utf-8')
print(json.loads(decoded_line))
就是將請求參數設置stream=True
import requests
r = requests.get('http://httpbin.org/stream/20', stream=True)
#當使用 decode_unicode=True 在 Response.iter_lines() 或 Response.iter_content() 中#時,須要提供一個編碼方式,以防服務器沒有提供默認回退編碼,從而致使錯誤
if r.encoding is None:
r.encoding = 'utf-8'#設置編碼方式
for line in r.iter_lines(decode_unicode=True):
if line:
print(json.loads(line))
#注意:iter_lines 不保證重進入時的安全性。屢次調用該方法 會致使部分收到的數據丟失。#若是要在多處調用它,就應該使用生成的迭代器對象:
lines = r.iter_lines()
# 保存第一行以供後面使用,或者直接跳過
first_line = next(lines)
for line in lines:
print(line)
不曉得各位看懂什麼是流式請求沒。指的不是請求是流,而是請求返回的數據流。返回一點即取一點。而不是普通的是返回完成後在取內容
import requests
proxies = {
"http": "http://10.10.1.10:3128",
"https": "http://10.10.1.10:1080",
}
requests.get("http://example.org", proxies=proxies)
代理:就是你訪問一個網站,其實並非你直接訪問的,而是你發請求給A機器,A機器取請求B機器。B返回給A,A再返回給你。代理就是中間人的意思。爲何須要代理?由於:反爬蟲網站通常使用IP來識別一個機器。總是一個IP再不停訪問網站,該網站就會把這個IP拉入黑名單,不容許訪問。這時,就須要不少IP再擾亂反爬蟲工具的思惟,避免封IP。
除了基本的 HTTP 代理,Request 還支持 SOCKS 協議的代理。這是一個可選功能,若要使用, 你須要安裝第三方庫。
在後面的項目實戰中,咱們將會使用
GET、OPTIONS、HEAD、POST、PUT、PATCH、DELETE
GET POST最經常使用
PUT,DELETE在調用rest的接口時,也會用到
有些服務器不接受GET POST等,要求用自定義的,就使用以下方法
r = requests.request('MKCOL', url, data=data)
r.status_code
許多 HTTP API 都有響應頭連接字段的特性,它們使得 API 可以更好地自我描述和自我顯露。
好比在使用分頁的狀況下最有用
url = 'https://api.github.com/users/kennethreitz/repos?page=1&per_page=10'
r = requests.head(url=url)
print(r.headers['link'])
print(r.links["next"])
使用默認的傳輸適配器,Requests 不提供任何形式的非阻塞 IO。 Response.content 屬性會阻塞,直到整個響應下載完成。若是你須要更多精細控制,該庫的數據流功能(見 流式請求) 容許你每次接受少許的一部分響應,不過這些調用依然是阻塞式的。
若是你對於阻塞式 IO 有所顧慮,還有不少項目能夠供你使用,它們結合了 Requests 和 Python 的某個異步框架。典型的優秀例子是 grequests 和 requests-futures。
在某些特殊狀況下你也許須要按照次序來提供 header,若是你向 headers 關鍵字參數傳入一個OrderedDict,就能夠向提供一個帶排序的 header。然而,Requests 使用的默認 header 的次序會被優先選擇,這意味着若是你在 headers 關鍵字參數中覆蓋了默認 header,和關鍵字參數中別的 header 相比,它們也許看上去會是次序錯誤的。
若是這個對你來講是個問題,那麼用戶應該考慮在 Session 對象上面設置默認 header,只要將 Session 設爲一個定製的 OrderedDict 便可。這樣就會讓它成爲優選的次序。
以上內容,是request的高級耍法,也不算好高級。接下來的一週,咱們將着重討論如何解析網頁。固然,這一部份內容不會包含JS生成的HTML。這個咱們將會在項目實戰環節再討論。
轉載:https://zhuanlan.zhihu.com/p/30251855