JA3
- 最近在爬虫练习的时候遇到一个网站,特别神奇
- 可以识别是否开启了 FD 抓包工具,以及 Python 发出的请求。
- 一开始看了一头雾水,猜测是跟 FD 证书有关系。
- 可以通过以下这个网站进行查看 ja3
- 不管你的代理,请求头怎么换,他网页返回的
ja3_hash
都是不变的。
- 几经查询,发现了一个大佬 diāo máo发明了一个算法,就是本文主角——JA3 算法。
- 简单来说就是通过:分析 SSL/TLS 客户端请求的报头。
- TLS 是 SSL 的标准版本,名称为:传输层安全协议,如果没有这个协议,相当于在互联网裸奔。
- 因为 Python 的库的握手是有指纹的(包括 scrapy,aiohttp,httpx),而当请求被 FD 拦截转发之后,指纹就变为了 FD 的,所以通过这个算法,就可以识别正常的浏览器请求,和爬虫请求,以及抓包工具等中间人请求,
- 上有政策下有对策,经过不懈的 sousuo,还是找到解决方法。
- 但不建议所有网站都启用,因为会特别慢,每个请求都会重新生成一个新的指纹。
- 当然你可以全局只生成一次指纹,每次启动爬虫才生成一次。
创建独一无二的指纹
- 注意 这些只修改了 Python 表层 如果后端检测的比较深层次 还是可以检测到的
- 如果希望完全过掉 可能需要修改源码 或者改用 GO 语言
- 想要创建自己独一无二的指纹,只需要修改
ORIGIN_CIPHERS
内部加密的顺序
- 但代码内其实都有随机了,无关紧要。
request
import random
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context
ORIGIN_CIPHERS = ('DH+3DES:RSA+3DES:ECDH+AES256:DH+AESGCM:DH+AES256:DH+AES:ECDH+AES128:'
'DH+HIGH:RSA+AESGCM:ECDH+3DES:RSA+AES:RSA+HIGH:ECDH+AESGCM:ECDH+HIGH')
class DESAdapter(HTTPAdapter):
def __init__(self, *args, **kwargs):
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)
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'}
session = requests.Session()
session.headers.update(headers)
ssl = DESAdapter()
for _ in range(5):
session.mount('https://', adapter=ssl)
result = session.get('https://ja3er.com/json').json()
print(result)
aiohttp
这是我 copy 来的,但我的电脑没有办法执行,会报错证书错误
但把他的 url 换成别的就可以,不知道什么原因。
- 结果不懈的调试,终于解决报错问题。。。
import asyncio
import random
import ssl
import aiohttp
ORIGIN_CIPHERS = ('RSA+3DES:RSA+AES:RSA+AESGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:ECDH+HIGH:'
'DH+HIGH:DH+3DES:RSA+HIGH:DH+AES:ECDH+3DES')
class SSLFactory:
def __init__(self):
self.ciphers = ORIGIN_CIPHERS.split(":")
def __call__(self) -> ssl.SSLContext:
random.shuffle(self.ciphers)
ciphers = ":".join(self.ciphers)
ciphers = ciphers + ":!aNULL:!eNULL:!MD5"
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.set_ciphers(ciphers)
return context
sslgen = SSLFactory()
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'}
async def main():
async with aiohttp.ClientSession() as session:
for _ in range(5):
async with session.get("https://ja3er.com/json", headers=headers, ssl=sslgen()) as resp:
data = await resp.text()
print(data)
asyncio.get_event_loop().run_until_complete(main())
scrapy
目前 scrapy 还不知道怎么修改指纹
不过已经在 scrapy 项目留言。
目前解决办法就是写个中间件,用 aiohttp 的。
- 在 github 多次翻阅之后,发现只要修改一个配置参数就可以修改指纹
1.配置文件中直接修改
DOWNLOADER_CLIENT_TLS_CIPHERS = 'RSA+AES:RSA+3DES:RSA+AESGCM:ECDH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:ECDH+HIGH:DH+HIGH:DH+3DES:RSA+HIGH:DH+AES:ECDH+3DES:!aNULL:!eNULL:!MD5'
2.启动自动随机修改
def ssl():
ciphers = 'RSA+3DES:RSA+AES:RSA+AESGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256' \
':DH+AES256:ECDH+AES128:ECDH+HIGH:DH+HIGH:DH+3DES:RSA+HIGH:DH+AES:ECDH+3DES'.split(":")
random.shuffle(ciphers)
ciphers = ":".join(ciphers)
ciphers = ciphers + ":!aNULL:!eNULL:!MD5"
return ciphers
class PaSpider(scrapy.Spider):
name = 'pa'
start_urls = ['https://ja3er.com/json']
custom_settings = {
'DOWNLOADER_CLIENT_TLS_CIPHERS': ssl(),
}
def start_requests(self):
***