之前一期我講過怎麼利用scrapy去突破指紋,這一期來講講怎麼用python原生庫requests突破指紋。
首先,我們知道請求是基於urllib3實現的。為了修改與JA3相關的基礎參數,今天我們將在urllib3中修改一些東西。
我們知道,JA3指紋裡面,有一個很大的部分,叫做 CipherSuits。默認加密算法如下:
ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:!eNULL:!MD5
冒號將不同加密算法分割開來。每一種加密算法都對應著JA3字符串,只要修改順序,就可以得到不同的JA3字符串。
在 requests中,修改 Cipher Suits中的加密算法需要修改urllib3中的 ssl上下文,並實現 HTTP適配器(HTTP Adapter)。在這個適配器中,每次請求都會隨機地更換加密算法。但有一點需要注意!ANULL:!EULL:!不需要修改MD5,把它們放在最後。
所涉及的代碼如下:
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context
ORIGIN_CIPHERS = ('ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES')
class DESAdapter(HTTPAdapter):
def __init__(self, *args, **kwargs):
""" A TransportAdapter that re-enables 3DES support in Requests. """
CIPHERS = ORIGIN_CIPHERS.split(':')
random.shuffle(CIPHERS)
CIPHERS = ':'.join(CIPHERS)
self.CIPHERS = CIPHERS + ':!aNULL:!eNULL:!MD5'
super().__init__(*args, **kwargs)
def init_poolmanager(self, *args, **kwargs):
context = create_urllib3_context(ciphers=self.CIPHERS)
kwargs['ssl_context'] = context
return super(DESAdapter, self).init_poolmanager(*args, **kwargs)
def proxy_manager_for(self, *args, **kwargs):
context = create_urllib3_context(ciphers=self.CIPHERS)
kwargs['ssl_context'] = context
return super(DESAdapter, self).proxy_manager_for(*args, **kwargs)
一般來說,實現子類時,init__的第一行應該是 super ().init(* args,** kwargs),但是由於init_poolmanager 和 proxy_manager_for是復寫父類的兩種方法。因此,當我們隨機設置 CipherSuits時,需要將它放在 super ().init( args,* kwargs)**前面。
有了適配器,當我們使用 requests時,初始化會話,然後綁定到特定網站:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.71'}
s = requests.Session()
s.headers.update(headers)
for _ in range(3):
s.mount('https://ja3er.com', DESAdapter())
resp = s.get('https://ja3er.com/json').json()
print(resp)
其中,s.mount的第一個參數表示該適配器僅在以https://ja3er.com開頭的網址中生效。
運行效果如下:
圖片
可以看到,ja3_hash已經改變了,說明我們請求時的 JA3指紋已經發生了改變。