Python 如何突破反爬蟲指紋 JA3
這是「進(jìn)擊的Coder」的第 466?篇技術(shù)分享作者:kingname來源:未聞 Code
“
閱讀本文大概需要 6 分鐘。
在前幾天的文章《為什么隨機(jī) IP、隨機(jī) UA 也逃不掉被反爬蟲的命運(yùn)》里面,我介紹了 JA3 指紋算法。這個(gè)算法可以在你改掉 IP 和 UA 的情況下依然識(shí)別到你。
今天,我們來介紹如何在 Python 里面,使用 requests 請(qǐng)求網(wǎng)站的時(shí)候,修改 JA3指紋。
requests 是基于 urllib3 實(shí)現(xiàn)的。要修改 JA3 相關(guān)的底層參數(shù),所以我們今天要修改 urllib3 里面的東西。
我們知道 JA3 指紋里面,很大的一塊就是 Cipher Suits,也就是加密算法。而 requests 里面默認(rèn)的加密算法如下:
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
冒號(hào)分割了不同的加密算法。這些加密算法每一種順序其實(shí)就對(duì)應(yīng)了一個(gè) JA3 指紋字符串,只要我們修改這個(gè)順序,就能得到不同的JA3字符串。
在 requests 里面,要修改 Cipher Suits 中的加密算法,需要修改 urllib3 里面的 ssl ?上下文,并實(shí)現(xiàn)一個(gè)新的 HTTP 適配器 (HTTPAdapter)。在這個(gè)適配器里面,我們?cè)诿看握?qǐng)求的時(shí)候,隨機(jī)更換加密算法。但需要注意的是!aNULL:!eNULL:!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)
在一般情況下,當(dāng)我們實(shí)現(xiàn)一個(gè)子類的時(shí)候,__init__的第一行應(yīng)該是super().__init__(*args, **kwargs),但是由于init_poolmanager和proxy_manager_for是復(fù)寫了父類的兩個(gè)方法,這兩個(gè)方法是在執(zhí)行super().__init__(*args, **kwargs)的時(shí)候就執(zhí)行的。所以,我們隨機(jī)設(shè)置 Cipher Suits 的時(shí)候,需要放在super().__init__(*args, **kwargs)的前面。
有了適配器以后,我們使用 requests 時(shí),初始化一個(gè) Session,然后把這個(gè)適配器綁定到特定的網(wǎng)站上:
import?requests
headers?=?{'User-Agent':?'Mozilla/5.0?(Macintosh;?Intel?Mac?OS?X?10_15_7)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/92.0.4515.131?Safari/537.36?Edg/92.0.902.67'}
s?=?requests.Session()
s.headers.update(headers)
for?_?in?range(5):
????s.mount('https://ja3er.com',?DESAdapter())
????resp?=?s.get('https://ja3er.com/json').json()
????print(resp)
其中,s.mount的第一個(gè)參數(shù)表示這個(gè)適配器只在https://ja3er.com開頭的網(wǎng)址中生效。
運(yùn)行效果如下圖所示:

可以看到,ja3_hash已經(jīng)改變了,說明我們請(qǐng)求時(shí)的 JA3 指紋已經(jīng)發(fā)生了改變。

End
「進(jìn)擊的Coder」專屬學(xué)習(xí)群已正式成立,搜索「CQCcqc4」添加崔慶才的個(gè)人微信或者掃描下方二維碼拉您入群交流學(xué)習(xí)。
及時(shí)收看更多好文
↓↓↓
崔慶才的「進(jìn)擊的Coder」知識(shí)星球已正式成立,感興趣的可以查看《我創(chuàng)辦了一個(gè)知識(shí)星球》了解更多內(nèi)容,歡迎您的加入:
