摄影:产品经理
好菜上桌
在前几天的文章《为什么随机 IP、随机 UA 也逃不掉被反爬虫的命运》里面,我介绍了 JA3指纹算法。这个算法可以在你改掉 IP 和 UA 的情况下依然识别到你。
昨天的文章《一日一技:Golang 如何突破 JA3?》里面,我介绍了如何在 Golang 里面突破 JA3算法,实现随意修改 JA3指纹的效果。
今天,我们来介绍如何在 Python 里面,使用 requests 请求网站的时候,修改 JA3指纹。
requests 是基于 urllib3实现的。要修改 JA3相关的底层参数,所以我们今天要修改 urllib3里面的东西。
我们知道 JA3指纹里面,很大的一块就是 Cipher Suits,也就是加密算法。而 requests 里面默认的加密算法如下:
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适配器(HTTPAdapter)。在这个适配器里面,我们在每次请求的时候,随机更换加密算法。但需要注意的是!aNULL:!eNULL:!MD5
就不用修改了,让他们保持在最后。
涉及到的代码如下:
代码语言:javascript复制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
是复写了父类的两个方法,这两个方法是在执行super().__init__(*args, **kwargs)
的时候就执行的。所以,我们随机设置 Cipher Suits 的时候,需要放在super().__init__(*args, **kwargs)
的前面。
有了适配器以后,我们使用 requests 时,初始化一个 Session,然后把这个适配器绑定到特定的网站上:
代码语言:javascript复制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
的第一个参数表示这个适配器只在https://ja3er.com
开头的网址中生效。
运行效果如下图所示:
可以看到,ja3_hash
已经改变了,说明我们请求时的 JA3指纹已经发生了改变。
JA3里面,还有参数Extensions
、EllipticCurves
、EllipticCurvePointFormats
,我还不知道怎么修改。如果有知道的同学,可以留言告诉我。