網絡請求是爬蟲工程師采集資料的重要手段之一。在PC端爬蟲中,網絡請求通常使用HTTP協定進行通信,通過發送HTTP請求擷取目标網站的資料。
爬蟲工程師需要掌握HTTP協定的基本知識,包括HTTP請求和響應的格式、常見的HTTP請求方法(如GET、POST等)、HTTP請求頭和響應頭的常見字段等。
在進行網絡請求時,爬蟲工程師通常使用HTTP用戶端庫,如Python中的requests庫、Java中的HttpClient等。這些庫封裝了HTTP協定的細節,提供了簡單易用的API,友善爬蟲工程師進行網絡請求。
爬蟲工程師還需要了解一些反爬蟲技術,如User-Agent僞裝、IP代理等,以應對目标網站的反爬蟲政策。
文檔+筆記+了解接單+源碼擷取方式
一丶requests
1.requests源碼解析
對于爬蟲工程師來說,網絡請求是常用的資料采集方式之一。而Python的requests庫,作為一個高效且易用的HTTP請求庫,被爬蟲工程師廣泛使用。在深入學習requests庫前,建議先了解下其中的源碼實作。
requests庫是基于urllib3庫封裝的,是以在使用requests庫時需要先安裝對應的依賴庫urllib3。
接下來,我們通過分析requests庫的源代碼,來了解其中的一些實作細節。
首先是發送請求的實作,即requests庫中的Request類。Request類用于封裝發送請求的參數,并通過一個Session對象發送請求并傳回響應。以下是Request類的核心代碼:
class Request:
@staticmethod
def send(session, method, url, **kwargs):
# ...
resp = session.request(method=method, url=url, **kwargs)
return resp
我們可以看到,Request類中的send方法調用了Session對象的request方法,這個方法是整個庫中負責發送請求和傳回響應的核心方法。以下是Session類中request方法的核心代碼:
class Session:
def request(self, method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None,
timeout=None, allow_redirects=True, proxies=None, hooks=None, stream=None, verify=None, cert=None,
json=None):
# ...
return self.send(prep, **send_kwargs)
我們可以看到,Session對象的request方法的參數和關鍵字參數與HTTP請求的相關部分一一對應,其中最重要的是prep參數(即經過預處理的Request對象),它包含了請求的相關資訊,如請求方法,請求頭,請求體等。Session對象的request方法最終調用了self.send方法,即發送HTTP請求并傳回響應。
requests庫實作了帶有各種HTTP請求方法的函數接口,如requests.get()、requests.post()等,這些接口在内部會自動建立一個Session對象,然後調用Session對象的request方法,進而傳回請求響應。
總體來說,requests是一個功能強大的HTTP請求庫,它的源代碼實作清晰、易于閱讀和了解,掌握其中的實作細節可以幫助我們更好的使用這個庫。
2.requests常用方法
requests是一個Python第三方庫,用于發送HTTP請求。以下是requests常用方法:
- requests.get(url, params=None, **kwargs):發送GET請求,url為請求的URL位址,params為請求參數,kwargs為其他可選參數。
- requests.post(url, data=None, json=None, **kwargs):發送POST請求,url為請求的URL位址,data為請求資料,json為請求的JSON資料,**kwargs為其他可選參數。
- requests.put(url, data=None, **kwargs):發送PUT請求,url為請求的URL位址,data為請求資料,**kwargs為其他可選參數。
- requests.delete(url, **kwargs):發送DELETE請求,url為請求的URL位址,**kwargs為其他可選參數。
- requests.head(url, **kwargs):發送HEAD請求,url為請求的URL位址,**kwargs為其他可選參數。
- requests.options(url, **kwargs):發送OPTIONS請求,url為請求的URL位址,**kwargs為其他可選參數。
- requests.request(method, url, **kwargs):發送自定義請求,method為請求方法,url為請求的URL位址,**kwargs為其他可選參數。
- requests.session():建立一個Session對象,用于保持會話狀态。
- requests.get(url, headers=headers):發送GET請求,并設定請求頭。
- requests.get(url, cookies=cookies):發送GET請求,并設定請求的Cookies。
- requests.get(url, proxies=proxies):發送GET請求,并設定代理伺服器。
- requests.get(url, timeout=timeout):發送GET請求,并設定逾時時間。
- requests.get(url, verify=verify):發送GET請求,并設定SSL證書驗證。
- requests.get(url, allow_redirects=allow_redirects):發送GET請求,并設定是否允許重定向。
- requests.get(url, stream=stream):發送GET請求,并設定是否使用流式傳輸
3.data/json/param參數傳遞
在使用requests庫發送網絡請求時,我們可以通過傳遞不同的參數來實作不同的請求方式和資料傳遞方式。常用的參數包括data、json和params。
1.data參數
data參數用于傳遞表單資料,通常用于POST請求。它可以是一個字典,也可以是一個字元串。如果是字典,requests會自動将其轉換為表單形式;如果是字元串,則需要手動指定Content-Type為application/x-www-form-urlencoded。
示例代碼:
import requests
data = {
'username': 'admin',
'password': '123456'
}
response = requests.post('http://www.example.com/login', data=data)
2.json參數
json參數用于傳遞JSON格式的資料,通常用于POST請求。它可以是一個字典,也可以是一個字元串。如果是字典,requests會自動将其轉換為JSON格式;如果是字元串,則需要手動指定Content-Type為application/json。
示例代碼:
import requests
data = {
'username': 'admin',
'password': '123456'
}
response = requests.post('http://www.example.com/login', json=data)
3.params參數
params參數用于傳遞URL參數,通常用于GET請求。它可以是一個字典,也可以是一個字元串。如果是字典,requests會自動将其轉換為URL參數;如果是字元串,則需要手動拼接URL。
示例代碼:
import requests
params = {
'page': 1,
'size': 10
}
response = requests.get('http://www.example.com/articles', params=params)
4.隧道代理使用
隧道代理是一種通過隧道連接配接到代理伺服器的方式來進行網絡請求的方法。這種方式可以幫助我們隐藏真實的IP位址,提高爬蟲的穩定性和安全性。
使用隧道代理需要先購買代理服務,然後在代碼中設定代理伺服器的IP位址和端口号。以下是一個使用隧道代理的示例代碼:
import requests
proxy = {
'http': 'http://代理伺服器IP位址:端口号',
'https': 'https://代理伺服器IP位址:端口号'
}
url = 'https://www.example.com'
response = requests.get(url, proxies=proxy)
print(response.text)
在上面的代碼中,我們首先定義了一個代理字典,包含了http和https兩種協定的代理伺服器位址和端口号。然後使用requests庫的get方法發送請求時,将代理字典作為proxies參數傳入即可。
需要注意的是,使用隧道代理可能會降低請求速度,而且代理服務的品質也會影響到爬蟲的效果。是以,在選擇代理服務時需要謹慎,建議選擇穩定可靠的服務商。
5.證書異常處理
在進行網絡請求時,有些網站會進行證書認證以確定資料的安全。如果requests庫在進行SSL證書驗證時遇到了問題,會抛出“證書驗證異常(Certificate Verification Error)”的異常。這個異常通常是由于請求響應的SSL證書無效或不受信任導緻的。
以下是requests庫中處理證書異常的方法:
1.忽略證書驗證
在使用requests庫進行網絡請求時,可以通過設定verify參數為False來忽略SSL證書驗證。這個方法會禁止requests庫對證書進行驗證,而是采用不安全的方式進行通信,是以在進行敏感操作時應慎重使用。
例如:
response = requests.get('https://example.com', verify=False)
2.設定證書檔案
可以通過設定cert參數來指定一個證書檔案,在請求時使用該證書進行驗證。這個方法需要事先獲得一個有效的證書檔案,如果無法提供有效證書則無法進行安全通信。
例如:
response = requests.get('https://example.com', cert=('path/to/cert.crt', 'path/to/key'))
3.添加自定義證書
可以通過requests庫提供的certifi庫在運作時初始化一個自定義證書,進而進行證書驗證。這種方式需要提供證書的SHA256指紋值,并将其添加到requests庫已有的證書清單中。
例如:
import certifi
cert = certifi.where()
fingerprints = {'example.com': 'A1:B2:C3:...', ...}
with open(cert, 'a') as f:
for host, fingerprint in fingerprints.items():
f.write(f'{host} {fingerprint}\n')
response = requests.get('https://example.com', verify=True)
在以上代碼中,certifi.where()用于擷取目前Python環境中的證書路徑,然後将每個需要驗證的主機和其證書的SHA256指紋添加到證書檔案中。
綜上,要避免證書異常需要注意常見的安全規則,如設定SSL證書驗證、使用CA頒發的證書、對外不開放不安全的通信端口等。需要快速掃描裝置,確定元件更新到最新版本,在安全上下文中測試企業所依賴的所有服務并采用有力的加密技術以支援加密通信。
二丶httpx
1.httpx源碼解析
httpx是一個Python異步HTTP用戶端庫,它提供了簡單易用的API,支援異步和同步請求,支援HTTP/1.1和HTTP/2協定,支援代理、SSL/TLS、Cookie等功能。下面我們來看一下httpx的源碼解析。
httpx的核心代碼在client.py檔案中,其中最重要的是Client類。Client類是httpx的主要接口,它提供了發送HTTP請求的方法,如get、post、put、delete等。下面是Client類的定義:
class Client:
def __init__(
self,
timeout=UNSET,
follow_redirects=UNSET,
max_redirects=UNSET,
verify=UNSET,
cert=UNSET,
trust_env=UNSET,
http2=UNSET,
backend=UNSET,
default_headers=UNSET,
base_url=UNSET,
app=UNSET,
auth=UNSET,
cookies=UNSET,
allow_redirects=UNSET,
proxies=UNSET,
dispatch=UNSET,
limits=UNSET,
pool_limits=UNSET,
retry=UNSET,
trust_env_proxies=UNSET,
headers=UNSET,
**extra_options,
):
...
Client類的構造函數接受很多參數,這些參數可以用來配置httpx的行為。其中比較重要的參數包括:
- timeout:請求逾時時間。
- follow_redirects:是否自動跟随重定向。
- max_redirects:最大重定向次數。
- verify:是否驗證SSL證書。
- cert:用戶端證書。
- trust_env:是否信任環境變量。
- http2:是否啟用HTTP/2協定。
- backend:HTTP用戶端後端。
- default_headers:預設請求頭。
- base_url:基礎URL。
- app:ASGI應用程式。
- auth:HTTP認證。
- cookies:請求Cookie。
- allow_redirects:是否允許重定向。
- proxies:代理伺服器。
- dispatch:請求分發器。
- limits:請求限制。
- pool_limits:連接配接池限制。
- retry:請求重試。
- trust_env_proxies:是否信任環境變量中的代理伺服器。
- headers:請求頭。
Client類的方法包括:
- request:發送HTTP請求。
- get:發送GET請求。
- post:發送POST請求。
- put:發送PUT請求。
- delete:發送DELETE請求。
- head:發送HEAD請求。
- options:發送OPTIONS請求。
- patch:發送PATCH請求。 這些方法都是基于request方法實作的,隻是參數不同。下面是request方法的定義:
async def request(
self,
method,
url,
*,
params=UNSET,
data=UNSET,
json=UNSET,
headers=UNSET,
cookies=UNSET,
files=UNSET,
auth=UNSET,
timeout=UNSET,
allow_redirects=UNSET,
cert=UNSET,
verify=UNSET,
stream=UNSET,
trust_env=UNSET,
max_redirects=UNSET,
http2=UNSET,
backend=UNSET,
dispatch=UNSET,
limits=UNSET,
pool_limits=UNSET,
retry=UNSET,
trust_env_proxies=UNSET,
**options,
):
...
request方法接受很多參數,包括HTTP請求方法、URL、請求參數、請求體、請求頭、請求Cookie、檔案、HTTP認證、請求逾時時間、是否允許重定向、用戶端證書、是否驗證SSL證書、是否使用流式傳輸、是否信任環境變量、最大重定向次數、是否啟用HTTP/2協定、HTTP用戶端後端、請求分發器、請求限制、連接配接池限制、請求重試、是否信任環境變量中的代理伺服器等。
httpx的源碼比較清晰,代碼結構清晰,注釋詳細,易于閱讀和了解。如果你想深入了解httpx的實作原理,可以閱讀httpx的源碼。
2.httpx常用方法
httpx是一個Python的異步HTTP用戶端庫,它提供了許多常用的方法來發送HTTP請求和處理響應。以下是httpx常用的方法:
- get(url, params=None, **kwargs): 發送GET請求,url為請求的URL,params為請求參數,kwargs為其他可選參數,如headers、timeout等。
- post(url, data=None, json=None, **kwargs): 發送POST請求,url為請求的URL,data為請求資料,json為請求的JSON資料,kwargs為其他可選參數,如headers、timeout等。
- put(url, data=None, **kwargs): 發送PUT請求,url為請求的URL,data為請求資料,kwargs為其他可選參數,如headers、timeout等。
- delete(url, **kwargs): 發送DELETE請求,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- head(url, **kwargs): 發送HEAD請求,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- options(url, **kwargs): 發送OPTIONS請求,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- request(method, url, **kwargs): 發送自定義請求,method為請求方法,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- close(): 關閉httpx用戶端。
- request_stream(method, url, **kwargs): 發送流式請求,method為請求方法,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- request_raw(method, url, **kwargs): 發送原始請求,method為請求方法,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- request_bytes(method, url, **kwargs): 發送位元組請求,method為請求方法,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- request_json(method, url, **kwargs): 發送JSON請求,method為請求方法,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- request_text(method, url, **kwargs): 發送文本請求,method為請求方法,url為請求的URL,kwargs為其他可選參數,如headers、timeout等。
- request_files(method, url, files=None, **kwargs): 發送檔案請求,method為請求方法,url為請求的URL,files為上傳的檔案,kwargs為其他可選參數,如headers、timeout等。
- request_multipart(method, url, data=None, files=None, **kwargs): 發送多部分請求,method為請求方法,url為請求的URL,data為請求資料,files為上傳的檔案,kwargs為其他可選參數,如headers、timeout等。
3.httpx上下文處理
在httpx中,上下文處理是指在一個請求中,将一些共同的參數或配置資訊儲存在一個上下文對象中,以便在後續的請求中使用。這樣可以避免在每個請求中都重複設定相同的參數或配置資訊,提高代碼的可讀性和可維護性。
httpx中的上下文對象是一個字典,可以通過建立一個httpx.Context對象來擷取。在建立Context對象時,可以傳入一些預設的參數或配置資訊,這些資訊會被儲存在Context對象中,以便在後續的請求中使用。
下面是一些常用的httpx上下文處理方法:
1.建立Context對象
import httpx
context = httpx.Context()
2.設定預設的請求頭
context.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
})
3.設定代理
context.proxies = {
'http': 'http://127.0.0.1:8888',
'https': 'http://127.0.0.1:8888'
}
4.設定逾時時間
context.timeout = httpx.Timeout(10.0, read=20.0)
5.設定SSL驗證
context.verify = False
6.設定cookie
context.cookies['name'] = 'value'
7.設定認證資訊
context.auth = httpx.BasicAuth('username', 'password')
8.設定重試次數
context.retry = httpx.Retry(total=3, backoff_factor=0.3)
9.設定連接配接池
context.http2 = True
context.max_keepalive_connections = 100
context.max_connections = 100
10.在請求中使用Context對象
import httpx
context = httpx.Context()
context.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
})
with httpx.Client(context=context) as client:
response = client.get('https://www.example.com')
4.httpx異步請求
在進行網絡請求時,有時候需要進行異步請求,以提高效率和性能。httpx是一個支援異步請求的Python HTTP用戶端庫,可以使用async/await文法進行異步請求。
下面是一個使用httpx進行異步請求的示例:
import httpx
import asyncio
async def fetch(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
async def main():
urls = [
'https://www.baidu.com',
'https://www.google.com',
'https://www.bing.com'
]
tasks = [asyncio.create_task(fetch(url)) for url in urls]
results = await asyncio.gather(*tasks)
print(results)
if __name__ == '__main__':
asyncio.run(main())
在上面的示例中,我們定義了一個fetch函數,用于異步請求指定的URL,并傳回響應内容。然後在main函數中,我們定義了三個URL,使用asyncio.create_task建立了三個異步任務,并使用asyncio.gather等待所有任務完成,并列印結果。
需要注意的是,在使用httpx進行異步請求時,需要使用AsyncClient類,而不是普通的Client類。此外,需要使用async/await文法進行異步請求。