反爬机制是网站和服务器采用的方法,用于防止恶意爬虫和机器人访问其内容或资源。以下是一些常见的反爬机制:
请注意,这些反爬机制可能会影响合法的网络爬虫和数据采集。爬虫开发者需要遵守网站的规则和政策,确保他们的活动是合法的,并避免过度频繁地请求网站。
在编写 web 爬虫时,一些人可能误以为只需轮流更换代理 IP 和模仿浏览器的 Headers,就能成功规避网站的监测机制。但实际上,还有一个不容忽视的因素,那就是“浏览器指纹”。浏览器指纹是一种独特的识别码,它不会因更换 IP 或 User-Agent 而改变,而且即使不使用模拟浏览器的方式,比如使用 Golang 或 Python,它们仍然会留下自己特有的指纹。更重要的是,这些指纹在每次请求时也是恒定不变的。
因此,无论你如何努力模拟请求,只要你的请求带有特定的浏览器指纹、频繁地访问网站,网站就有可能将你的行为识别并采取封锁措施。这是需要认真考虑和处理的问题,因为网站的监测系统会关注多个因素,包括 IP、Headers 和浏览器指纹。
下面来演示一下使用代理的情况:
import requests
proxy_url = "http://xxxxxx"
# 创建代理配置
proxies = {
"http": proxy_url,
"https": proxy_url,
}
url = 'https://tls.browserleaks.com/json'
resp = requests.get(url)
print(resp.json())
resp = requests.get(url, proxies=proxies)
print(resp.json())
# 输出
"""
{'hash': '8d9f7747675e24454cd9b7ed35c58707', 'fingerprint': '771,4866-4867-4865-49196-49200-49195-49199-52393-52392-159-158-52394-49327-49325-49326-49324-49188-49192-49187-49191-49162-49172-49161-49171-49315-49311-49314-49310-107-103-57-51-157-156-49313-49309-49312-49308-61-60-53-47-255,0-11-10-16-22-23-49-13-43-45-51-21,29-23-30-25-24,0-1-2', 'ciphers': 'TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-CHACHA20-POLY1305,DHE-RSA-AES256-GCM-SHA384,DHE-RSA-AES128-GCM-SHA256,DHE-RSA-CHACHA20-POLY1305,ECDHE-ECDSA-AES256-CCM8,ECDHE-ECDSA-AES256-CCM,ECDHE-ECDSA-AES128-CCM8,ECDHE-ECDSA-AES128-CCM,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-RSA-AES256-CCM8,DHE-RSA-AES256-CCM,DHE-RSA-AES128-CCM8,DHE-RSA-AES128-CCM,DHE-RSA-AES256-SHA256,DHE-RSA-AES128-SHA256,DHE-RSA-AES256-SHA,DHE-RSA-AES128-SHA,AES256-GCM-SHA384,AES128-GCM-SHA256,AES256-CCM8,AES256-CCM,AES128-CCM8,AES128-CCM,AES256-SHA256,AES128-SHA256,AES256-SHA,AES128-SHA,TLS_EMPTY_RENEGOTIATION_INFO_SCSV', 'curves': '001D:0017:001E:0019:0018', 'protocol': 'TLSv1.3', 'user_agent': 'python-requests/2.31.0'}
{'hash': '8d9f7747675e24454cd9b7ed35c58707', 'fingerprint': '771,4866-4867-4865-49196-49200-49195-49199-52393-52392-159-158-52394-49327-49325-49326-49324-49188-49192-49187-49191-49162-49172-49161-49171-49315-49311-49314-49310-107-103-57-51-157-156-49313-49309-49312-49308-61-60-53-47-255,0-11-10-16-22-23-49-13-43-45-51-21,29-23-30-25-24,0-1-2', 'ciphers': 'TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_GCM_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-CHACHA20-POLY1305,DHE-RSA-AES256-GCM-SHA384,DHE-RSA-AES128-GCM-SHA256,DHE-RSA-CHACHA20-POLY1305,ECDHE-ECDSA-AES256-CCM8,ECDHE-ECDSA-AES256-CCM,ECDHE-ECDSA-AES128-CCM8,ECDHE-ECDSA-AES128-CCM,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-RSA-AES256-CCM8,DHE-RSA-AES256-CCM,DHE-RSA-AES128-CCM8,DHE-RSA-AES128-CCM,DHE-RSA-AES256-SHA256,DHE-RSA-AES128-SHA256,DHE-RSA-AES256-SHA,DHE-RSA-AES128-SHA,AES256-GCM-SHA384,AES128-GCM-SHA256,AES256-CCM8,AES256-CCM,AES128-CCM8,AES128-CCM,AES256-SHA256,AES128-SHA256,AES256-SHA,AES128-SHA,TLS_EMPTY_RENEGOTIATION_INFO_SCSV', 'curves': '001D:0017:001E:0019:0018', 'protocol': 'TLSv1.3', 'user_agent': 'python-requests/2.31.0'}
"""
神奇不,完全一致。
可以看到,虽然使用了隧道代理,每次请求的 IP 都是不一样的,但是这个网站返回的内容始终是一样的。所以如果这不是一个测试网站,而是一个加了这个检测机制的网站,那么它轻松就能把我给屏蔽了。
JA3(Just Another 3-Letter Acronym)是一种用于 SSL/TLS 客户端 “Hello” 消息的散列算法,用于创建 SSL/TLS 客户端的指纹。它的原理基于 SSL/TLS 握手协议,具体原理如下:
握手消息捕获:JA3 的原理是基于捕获 SSL/TLS 握手过程中客户端发送的 “Client Hello” 消息。“Client Hello” 消息是 SSL/TLS 握手的第一个消息,其中包含了客户端的配置信息,如支持的 TLS 版本、密码套件、扩展等。
提取关键信息:JA3 根据 “Client Hello” 消息中包含的关键信息来生成散列。这些关键信息通常包括:
构建 JA3 字符串:JA3 根据上述提取的信息构建一个 JA3 字符串,该字符串将这些信息按照特定格式组合在一起。通常,JA3 字符串的格式为 “TLS版本,密码套件,扩展”,其中这些信息以逗号分隔。
计算散列:生成的 JA3 字符串被用作输入,通过哈希函数(通常使用 MD5 或 SHA-1)计算出 JA3 散列。这个散列值是 JA3 的唯一标识,用于表示 SSL/TLS 客户端的配置。
总之,JA3算法收集了SSL请求中的多个参数,包括但不限于SSL/TLS版本、密码套件数量、浏览器扩展列表、椭圆曲线等等。这些参数被综合组合成一个独特的指纹字符串。虽然你可能与一些人具有相同的密码套件数量、浏览器扩展数,甚至TLS版本号,但所有这些参数完全相同的人却相对较少。而在这小众人群中,他们同时访问同一网站的概率更低。因此,通过JA3算法,网站可以近似确定,在一段时间内,拥有相同指纹字符串的连续请求很可能来自同一个用户。
现在有很多网站,已经能够通过JA3或者其他指纹信息,来识别请求是不是Requests发起的。这种情况下,你无论怎么改Headers还是代理,都没有任何意义。
使用一个第三方库:
pip install curl_cffi
代码如下:
from curl_cffi import requests as request_cffi
resp = request_cffi.get(url, impersonate="chrome110")
print(resp.json())
# 输出
"""
{'hash': '6cc593a36386ff801a9304392b84426d', 'fingerprint': '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,10-13-51-65281-18-45-27-23-35-5-11-0-43-17513-16-21,29-23-24,0', 'ciphers': 'TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-CHACHA20-POLY1305,ECDHE-RSA-CHACHA20-POLY1305,ECDHE-RSA-AES128-SHA,ECDHE-RSA-AES256-SHA,AES128-GCM-SHA256,AES256-GCM-SHA384,AES128-SHA,AES256-SHA', 'curves': '001D:0017:0018', 'protocol': 'TLSv1.3', 'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'}
"""
另外一个网站信息更全:
# https://tls.browserleaks.com/json
{'ja3_hash': '8d9f7747675e24454cd9b7ed35c58707', 'ja3_text': '771,4866-4867-4865-49196-49200-49195-49199-52393-52392-159-158-52394-49327-49325-49326-49324-49188-49192-49187-49191-49162-49172-49161-49171-49315-49311-49314-49310-107-103-57-51-157-156-49313-49309-49312-49308-61-60-53-47-255,0-11-10-16-22-23-49-13-43-45-51-21,29-23-30-25-24,0-1-2', 'ja3n_hash': 'a790a1e311289ac1543f411f6ffceddf', 'ja3n_text': '771,4866-4867-4865-49196-49200-49195-49199-52393-52392-159-158-52394-49327-49325-49326-49324-49188-49192-49187-49191-49162-49172-49161-49171-49315-49311-49314-49310-107-103-57-51-157-156-49313-49309-49312-49308-61-60-53-47-255,0-10-11-13-16-21-22-23-43-45-49-51,29-23-30-25-24,0-1-2', 'akamai_hash': '', 'akamai_text': ''}
{'ja3_hash': 'b3f3441b7ab96e4c7b503bcdf85e366f', 'ja3_text': '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,13-16-17513-0-65281-51-43-10-18-27-23-45-5-35-11-21,29-23-24,0', 'ja3n_hash': 'aa56c057ad164ec4fdcb7a5a283be9fc', 'ja3n_text': '771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-5-10-11-13-16-18-21-23-27-35-43-45-51-17513-65281,29-23-24,0', 'akamai_hash': 'b7c68e9109d979eceed22f28cad1f034', 'akamai_text': '1:65536;2:0;3:1000;4:6291456;6:262144|15663105||m,a,s,p'}
import asyncio
import random
import ssl
import aiohttp
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 SSLFactory:
def __init__(self):
self.ciphers = ORIGIN_CIPHERS.split(":")
def __call__(self, *args, **kwargs) -> ssl.SSLContext:
random.shuffle(self.ciphers)
ciphers = ":".join(self.ciphers)
ciphers = ciphers + ":!aNULL:!eNULL:!MD5"
context = ssl.create_default_context()
context.set_ciphers(ciphers)
return context
async def main():
sslgen = SSLFactory()
async with aiohttp.ClientSession() as session:
for _ in range(3):
async with session.get("https://tls.browserleaks.com/json", headers={}, ssl=sslgen()) as response:
data = await response.json()
print(data)
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
# 输出
"""
{'ja3_hash': '8d40a03f3d26a49e8881f5aa25eafc69', 'ja3_text': '771,4866-4867-4865-49196-49200-52393-52392-49327-49325-49245-49249-49195-49199-49326-49324-49244-49248-49188-49192-49267-49271-49187-49191-49266-49270-49162-49172-49161-49171-163-159-49315-49311-162-158-49314-49310-107-106-103-64-57-56-51-50-157-49313-49309-156-49312-49308-61-60-53-47-52394-49239-49235-49238-49234-196-195-190-189-136-135-69-68-49233-49232-192-186-132-65-255,0-11-10-35-22-23-13-43-45-51-21,29-23-30-25-24,0-1-2', 'ja3n_hash': '15e37cb3a99d7d4a5e30d20a3caf465c', 'ja3n_text': '771,4866-4867-4865-49196-49200-52393-52392-49327-49325-49245-49249-49195-49199-49326-49324-49244-49248-49188-49192-49267-49271-49187-49191-49266-49270-49162-49172-49161-49171-163-159-49315-49311-162-158-49314-49310-107-106-103-64-57-56-51-50-157-49313-49309-156-49312-49308-61-60-53-47-52394-49239-49235-49238-49234-196-195-190-189-136-135-69-68-49233-49232-192-186-132-65-255,0-10-11-13-21-22-23-35-43-45-51,29-23-30-25-24,0-1-2', 'akamai_hash': '', 'akamai_text': ''}
{'ja3_hash': 'd162146d9c2773d7dd831f9079e31f1a', 'ja3_text': '771,4866-4867-4865-49196-49200-49195-49199-157-49313-49309-156-49312-49308-61-60-53-47-49233-49232-192-186-132-65-163-159-49315-49311-162-158-49314-49310-107-106-103-64-57-56-51-50-49327-49325-49188-49192-49162-49172-52393-52392-49245-49249-49326-49324-49244-49248-49267-49271-49187-49191-49266-49270-49161-49171-52394-49239-49235-49238-49234-196-195-190-189-136-135-69-68-255,0-11-10-35-22-23-13-43-45-51-21,29-23-30-25-24,0-1-2', 'ja3n_hash': '7f71e78e23f68358d46d75a926e9ab4f', 'ja3n_text': '771,4866-4867-4865-49196-49200-49195-49199-157-49313-49309-156-49312-49308-61-60-53-47-49233-49232-192-186-132-65-163-159-49315-49311-162-158-49314-49310-107-106-103-64-57-56-51-50-49327-49325-49188-49192-49162-49172-52393-52392-49245-49249-49326-49324-49244-49248-49267-49271-49187-49191-49266-49270-49161-49171-52394-49239-49235-49238-49234-196-195-190-189-136-135-69-68-255,0-10-11-13-21-22-23-35-43-45-51,29-23-30-25-24,0-1-2', 'akamai_hash': '', 'akamai_text': ''}
{'ja3_hash': '3d09497efab600bd2de80c795969f777', 'ja3_text': '771,4866-4867-4865-163-159-49315-49311-107-106-57-56-157-49313-49309-156-49312-49308-61-60-53-47-49196-49200-49327-49325-49188-49192-49162-49172-49233-49232-192-186-132-65-49195-49199-49326-49324-49187-49191-49161-49171-162-158-52394-49239-49235-49314-49310-49238-49234-196-195-103-64-190-189-136-135-51-50-69-68-52393-52392-49245-49249-49244-49248-49267-49271-49266-49270-255,0-11-10-35-22-23-13-43-45-51-21,29-23-30-25-24,0-1-2', 'ja3n_hash': 'f18f3a028f49d706daa16a9ea2e8b7d1', 'ja3n_text': '771,4866-4867-4865-163-159-49315-49311-107-106-57-56-157-49313-49309-156-49312-49308-61-60-53-47-49196-49200-49327-49325-49188-49192-49162-49172-49233-49232-192-186-132-65-49195-49199-49326-49324-49187-49191-49161-49171-162-158-52394-49239-49235-49314-49310-49238-49234-196-195-103-64-190-189-136-135-51-50-69-68-52393-52392-49245-49249-49244-49248-49267-49271-49266-49270-255,0-10-11-13-21-22-23-35-43-45-51,29-23-30-25-24,0-1-2', 'akamai_hash': '', 'akamai_text': ''}
"""