跳至內容

用戶:Xyy23330121/Python/HTTP 請求

來自維基學院


本章節,我們將主要講解如何使用 Python 自動訪問互聯網內容。本章節默認讀者對 HTML 有基本的了解,知道如何使用 正則表達式 等方式,從 HTML 代碼中提取信息。

Python 內置的模塊 urllib 提供了對互聯網內容進行請求的方法。而第三方模塊 requests 提供了比 urllib 更好用的方法。本章講解的內容將基於 requests,而非 urllib。

對於使用從 python.org 下載的 Windows 版本的 Python,我們可以從命令行程序中,輸入以下代碼以安裝 request。

python -m pip install requests

如果連結不暢,或者下載失敗。可以用以下代碼,改為從清華大學提供的託管伺服器中下載 requests。

python -m pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

基本請求

[編輯 | 編輯原始碼]

訪問互聯網的基本方式是進行 HTTP 請求。如果讀者僅是訪問網頁,無需登錄網站賬號等,只要看本章節內容即可。

import requests
r = requests.get("https://zh.wikiversity.org")

requests.get() 等請求函數會進行一次請求,並返回請求得到的結果。

響應結果

[編輯 | 編輯原始碼]

對於結果對象 rr.content會包含所得到的響應內容。該響應內容是二進制的 bytes 對象。在實際使用時,並不會直接使用二進制的內容,而是通過 HTTP 頭中的信息,將二進制內容按指定的編碼解碼後、再進行處理。

requests 會按照 HTTP 頭中的信息自動設置編碼方式。讀者也可以自行設置編碼方式。

r = requests.get("https://www.baidu.com/s?wd=URL")
print(r.encoding) #输出自动的编码方式
r.encoding = "ascii" #将编码方式改为 ascii

響應結果文本

[編輯 | 編輯原始碼]

類似 r.content,r.text會包含所得到的結果的文本形式。它等同於

r.content.decode(encoding = r.encoding, errors = "ignore")

如果請求的是一個 HTML 頁面,則返回的會是該 HTML 頁面的原始碼,比如:

r = requests.get("https://www.baidu.com/s?wd=URL")
print(r.text)

輸出為:

<html>
<head>
        <script>
                location.replace(location.href.replace("https://","http://"));
        </script>
</head>
<body>
        <noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body>
</html>

如果請求的並非 HTML 頁面,此方法也會原樣返回得到的內容。比如以下方式請求得到的是一個 json 文本序列。

s = requests.get("https://api.bilibili.com/x/web-show/page/header")
print(s.text)

輸出為:

{"code":-400,"message":"Key: 'ResourceID' Error:Field validation for 'ResourceID' failed on the 'min' tag","ttl":1}

json 響應結果

[編輯 | 編輯原始碼]

使用 r.json() 即可將獲得的 json 結果轉換為對應的 Python 對象。該方法等同於:

import json
json.loads(r.text)

原始響應結果

[編輯 | 編輯原始碼]

在極少數情況,讀者可能需要使用原始的響應結果。r.raw 會輸出原始的響應結果對象。如果要使用原始響應結果,在請求時,需要添加 stream 參數,比如:

r = requests.get("https://www.baidu.com/s?wd=URL", stream=True)

原始的響應結果對象是第三方庫 urllib3 中的一個 urllib3.response.HTTPResponse 對象。這裏不多贅述。

高級請求

[編輯 | 編輯原始碼]

除了基本方式之外,HTTP 能傳輸更多信息。如果要實現更高級的功能,就要了解 HTTP 能傳輸的信息內容。本章將講解 HTTP 請求中包含的內容,以及如何用 requests 模塊中的方法來傳輸這些信息。

URL參數

[編輯 | 編輯原始碼]

在 HTTP 標準中, URL 是可以包含參數的。比如:

r = requests.get("https://www.baidu.com/s?wd=URL")

除上述由讀者在 URL 中添加參數之外,requests 還可以將參數自動加入到 URL 裏面並進行請求:

payload = {"wd" : "URL"}
r = requests.get("https://www.baidu.com/s", params = payload)
print(r.url) #输出:https://www.baidu.com/s?wd=URL

請求的標頭

[編輯 | 編輯原始碼]

HTTP 請求中,HTTP 標頭(HTTP header)會包含比如「發送請求的客戶端名稱」、「傳輸的編碼方式」等等。具體參見 HTTP 標頭

以「客戶端名稱」為例,想要自定義發送的客戶端名稱,可以用如下方式:

head = {'user-agent': 'my-app/0.0.1'}
r = requests.get("https://www.baidu.com/", headers = head)

需要注意的是:

  • 標頭字典中的所有值必須是字符串、bytes 或者 Unicode 信息。最好不要使用 Unicode 信息。
  • 自定義標頭的優先級低於其它可確定的信息源,比如:
    • (如果有)netrc 配置文件的內容
    • (如果有)auth 參數的內容。
    • (如果有)在 URL 中提供的代理憑據。標頭 Proxy-Authorization 將被 URL 中提供的代理憑據覆蓋。
    • (可以確定信息長度時)標頭 Content-Length 會被修改為確定的信息長度。
    • 其它類似的信息源。
關於 httpbin.org
httpbin.org 是一個公益性的網站。向 httpbin.org 域名下的對應 URL 發送請求時。請求中的所有信息會在返回信息的正文中出現。善用這一工具有助於讀者了解如何發送請求。
詳細使用方式參見 https://httpbin.org/#/

cookie 是 HTTP 標頭中的一個字段。

網頁常用 cookies 來記錄用戶的情況。瀏覽器在請求網頁時,也會發送上次瀏覽網頁時留下的 cookies,以實現自動登錄等功能。一個 cookie 會包含以下的內容:

  • 域名
  • 路徑
  • 過期時間

在使用時,我們可以用字典,只傳遞「鍵」和「值」,讓其它內容自動填充。比如:

url = 'https://httpbin.org/cookies'
cookies = {'name': 'value'}
r = requests.get(url, cookies=cookies)
print(r.text)

也可以使用 requests.cookies.RequestsCookieJar 實例來傳遞更多內容。比如:

#创建存储 cookies 的对象。
jar = requests.cookies.RequestsCookieJar()

#添加两个 cookie
jar.set('name1', 'value1', domain='httpbin.org', path='/cookies')
jar.set('name2', 'value2', domain='httpbin.org', path='/')

#请求时传递 cookies。
url = 'https://httpbin.org/cookies'
r = requests.get(url, cookies=jar)
print(r.text)

對於需要登錄的網頁,讀者可以使用比如 EditThisCookie 之類的瀏覽器擴展程序來查看並複製自己瀏覽網頁產生的 cookies,將其詳細信息複製,並用 requests 模塊發送。此時請求的結果,等同於讀者登錄賬號之後的請求結果。

身份驗證信息

[編輯 | 編輯原始碼]

HTTP 協議支持自定義的身份驗證信息。該信息的內容應當由伺服器和用戶自行約定。並會覆蓋位於標頭的 Authorization 信息。

該內容應當為一個多個不含空格的ASCII字符串組成的元組,可以使用以下方式插入該信息。

auth = ("Basic","QWxhZGRpbjpvcGVuIHNlc2FtZQ==")

url = 'https://httpbin.org/get'
r = requests.get(url, auth=auth)

響應延時

[編輯 | 編輯原始碼]

在請求時,有時會需要一些時間才能正確接收到伺服器返回的信息;也可能請求丟失、伺服器未響應。設定響應延時可以解決這些問題。

響應延時是以秒為單位的浮點數或形如 (连接延时, 读取延时) 的浮點數元組。使用方式為:

url = 'https://httpbin.org/get'
r = requests.get(url, timeout=0.1)

https 重定向

[編輯 | 編輯原始碼]

許多網頁都會把 http 的內容重定向到 https 來處理。雖然禁止重定向沒有什麼好處,我們是可以禁止這樣的重定向的。比如:

r = requests.get('http://github.com/', allow_redirects=False)

代理伺服器

[編輯 | 編輯原始碼]

可以設置經由代理伺服器發送請求。方式如下:

proxies = {
  'http': 'http://10.10.1.10:3128',
  'https': 'http://10.10.1.10:1080',
}
requests.get('http://example.org', proxies=proxies)

如果在請求時設置了 stream 參數為 True,則返回的內容會是一個「二進制流」。

r = requests.get('https://github.com', stream=True)

if r.encoding is None:
    r.encoding = 'utf-8'

for line in r.iter_lines(): #像二进制流一样处理返回的内容
    line.decode(r.encoding)

在互相傳遞信息時,需要驗證雙方的身份。互聯網通過公私鑰加密建立了「證書」機制,通過證書可以驗證溝通雙方的身份。其具體內容可以參見 數字證書 。讀者在進行交流時,也需要驗證證書。

伺服器驗證

[編輯 | 編輯原始碼]

在進行請求時,可以對伺服器返回的 TLS 證書進行驗證。

不進行驗證
[編輯 | 編輯原始碼]

默認總會驗證伺服器返回的 TLS 證書。如果要不進行驗證,可以增加以下參數。

requests.get('http://example.org', verify=False)
要求證書路徑
[編輯 | 編輯原始碼]

如果 verify 參數為一個字符串,則只有伺服器使用路徑包含 verify 的證書時,才會通過驗證。比如:

requests.get('https://github.com', verify='/path/to/certfile')

客戶端驗證

[編輯 | 編輯原始碼]

可以為自己的客戶端設置證書。

如果給 cert 參數輸入字符串,則應當輸入指向 ssl 客戶端認證文件(.pem 文件)的路徑。如果輸入元組,則應當傳入形如 ("cert","key") 的元組。

更多請求

[編輯 | 編輯原始碼]

上面章節提到的請求,均為「GET」請求,只用於讀取資料。如果要進行其它操作,則需要使用其它的請求。要了解所有的請求方式,參見 HTTP 請求方法

requests 實現了以下幾種請求方式:

  • GET,讀取資料。
  • HEAD,獲得資料的元信息,而不獲取整個資料。
  • OPTIONS,使伺服器傳回該資源支持的所有HTTP請求方法。
  • POST,上傳新數據。
  • PUT,上傳並修改。
  • DELETE,刪除數據。
  • PATCH,將局部更改應用到資源。

在 requests 模塊中,這些請求方式支持 requests.get() 函數中支持的一切參數。以下將主要講解它們在使用時與 GET 的區別。

requests.head

[編輯 | 編輯原始碼]

requests.head() 函數向伺服器請求資料,但伺服器不會返回資料的正文部分。與 GET 不同,此函數默認禁用重定向。

r = requests.head('https://httpbin.org/get')

如果要啟用重定向,則需要:

r = requests.head('http://github.com/', allow_redirects=True)

requests.options

[編輯 | 編輯原始碼]

OPTIONS 請求的方法如下:

r = requests.options('https://httpbin.org/get')
print(r.headers['allow'])  #输出:OPTIONS, GET, HEAD

從以上輸出可以知道,該 URL 支持三種請求:OPTIONS、GET 和 HEAD。

requests.post

[編輯 | 編輯原始碼]

POST 請求用於上傳信息。

form 信息的發送

[編輯 | 編輯原始碼]

http 支持表單內容的發送。其基本使用方法如下:

payload = {'key': 'value'} #要上传的信息
r = requests.post('https://httpbin.org/post', data=payload)
print(r.text)

通過在 data 中傳入字典,可以將字典的信息以 form 的形式發送。

在 data 中,可以給同一個鍵設置多個值,此時可以用以下兩種方法:

payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
payload_dict = {'key1': ['value1', 'value2']}
r2 = requests.post('https://httpbin.org/post', data=payload_dict)

文件發送

[編輯 | 編輯原始碼]

如果要發送文件,則可以使用以下方式:

url = 'https://httpbin.org/post'
files = {'file': open('report.xls', 'rb')}

r = requests.post(url, files=files)
print(r.text)

其中,文件內容最好是二進制的,這樣有助於自動獲取請求中信息的大小。這樣發送的文件不包含文件名,也不包含處理方式。僅包含文件的內容。

如果要包含文件名、文件類型等信息,可以使用元組:

url = 'https://httpbin.org/post'
files = {'file': (
  'report.xls',
  open('report.xls', 'rb'),
  'application/vnd.ms-excel',
  {'Expires': '0'}
)}

r = requests.post(url, files=files)
print(r.text)

如果讀者希望,也可以直接將文本內容當成文件的內容並發送出去:

url = 'https://httpbin.org/post'
files = {'file': (
  'report.csv',
  'some,data,to,send\nanother,row,to,send\n'
)}

r = requests.post(url, files=files)
print(r.text)

流式發送

[編輯 | 編輯原始碼]

對於較大的文件,可能無法在一個請求中完整發送。此時,需要使用流式發送:

with open('file_path', 'rb') as f:
    requests.post("http://some.url/streamed", data = f)

這種流式發送,需要所請求的伺服器有對應的支持。

同時發送多個文件

[編輯 | 編輯原始碼]

使用以下方法,可以同時發送多個文件:

url = 'https://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)
r.text

在發送文件時,建議用二進制模式打開文件。

requests.put

[編輯 | 編輯原始碼]
r = requests.put('https://httpbin.org/put', data={'key': 'value'})

requests.delete

[編輯 | 編輯原始碼]
r = requests.delete('https://httpbin.org/delete')

requests.patch

[編輯 | 編輯原始碼]
r = requests.patch('https://httpbin.org/put', data={'key': 'value'})

請求的鈎子

[編輯 | 編輯原始碼]

requests 模塊的請求支持使用鈎子。目前支持的鈎子僅有一個:"response"。這個鈎子會在請求得到響應時觸發,使用方法如下:

def print_url(r):
    print(f"向 {r.url} 发出了请求")

r = requests.get('https://httpbin.org/', hooks={'response': print_url})

程序輸出為:

 https://httpbin.org/ 发出了请求

在得到響應後,鈎子指定的函數會被傳入一個響應結果對象。

特別的,如果 "response" 鈎子所指定的函數返回值不為 None,則 requests.get() 函數會返回鈎子函數所返回的內容,比如以下的鈎子函數:

def record_hook(r, *args, **kwargs):
    r.hook_called = True
    return r

高級響應結果處理

[編輯 | 編輯原始碼]

任何請求所得的回應,都是一個 request.Response 對象。本章節將講解該對象的方法。

響應結果的標頭

[編輯 | 編輯原始碼]

讀者可以通過以下方式,讀取網站返回時使用的 標頭

r = requests.get(url)
print(r.headers)

標頭會以字典形式返回。

響應結果的 cookies

[編輯 | 編輯原始碼]

讀者可以通過以下方式,讀取網站返回的 cookies。

r = requests.get(url)
print(r.cookies)

此方法返回的是一個 requests.cookies.RequestsCookieJar 實例,該實例有以下方法:

響應延時

[編輯 | 編輯原始碼]
r = requests.get(url)
print(r.elapsed)

響應延時是發送請求到收到響應之間的時間間隔,為一個 datetime.timedelta 對象。

重定向歷史記錄

[編輯 | 編輯原始碼]
r = requests.get(url)
print(r.history)

在進行請求時,requests 模塊會自動進行重定向。通過這種方法可以獲得重定向的歷史記錄列表,列表的元素為每次重定向時的 request.Response 對象。

HTTP 狀態碼

[編輯 | 編輯原始碼]
r = requests.get(url)
print(r.status_code) #状态码,比如 404、200 等
print(r.reason)      #状态字符串,比如"Not Found"等

會話(Session)是一種高級對象。它會自動保存網站所發送的 cookie 並在下一次請求時向對應的網站提供。它同時還提供了連接池和連接配置。

創建 Session 和使用

[編輯 | 編輯原始碼]

以下程序創建了一個 Session,並用該 Session 發送了一個 GET 請求:

import requests
s = requests.Session()
s.get('https://httpbin.org/get')

也可以用上下文管理器的方式來使用 Session:

import requests
with requests.Session() as s:
    s.get('https://httpbin.org/get')

Session 支持上面列出的一切請求方式。使用 Session 發送請求時所使用的「方法名稱」和「方法參數」,和使用上面列出的函數來發送請求時的「函數名稱」和「函數參數」是一樣的。

Session 包含的設置

[編輯 | 編輯原始碼]

對於經過以下代碼所創建的 Session:

import requests
session = requests.Session()

該對象在進行請求時,會自動在請求中輸入各種參數。以下是參數的列表:

屬性 說明 備註
session.cookies cookie 一個 RequestsCookieJar 實例,和上面 #cookies 章節一致。
可以修改成其它的、cookielib.CookieJar 的子類型的實例。
session.auth 身份驗證信息 和上面 #身份驗證信息 章節一致。
session.verify 伺服器驗證 和上面 #伺服器驗證 章節一致。
session.cert 客戶端驗證 和上面 #客戶端驗證 章節一致。
session.headers 標頭信息 和上面 #請求的標頭 章節一致。
session.hooks 鈎子 和上面 #請求的鈎子 章節一致。
session.proxies 代理伺服器 和上面 #代理伺服器 章節一致。
session.stream 和上面 #流 章節一致。
session.max_redirects 最大重定向次數 可以設置重定向的最大次數,默認為 30。
session.params 請求參數 在請求時會被自動加入的,所有參數的字典。

除上述屬性之外,requests.Sessions 還有一些其它的方法,詳情參見文檔。