現在越來越多的網站已經能夠通過JA3
或者其他指紋信息,來識別你是不是爬蟲了。傳統的方式比如換UA,加代理是沒有任何意義了,所以這個時候我們就需要使用到curl_cffi
了。
1.TLS 指紋是啥?
在絕大多數的網站都已經使用了 HTTPS,要建立 HTTPS 鏈接,服務器和客戶端之間首先要進行
TLS 握手,在握手過程中交換雙方支持的 TLS 版本,加密算法等信息。不同的客戶端之間的差異
很大,而且一般這些信息還都是穩定的,所以服務端就可以根據 TLS 的握手信息來作為特征,識別
一個請求是普通的用戶瀏覽器訪問,還是來自 Python 腳本等的自動化訪問。
JA3 是生成 TLS 指紋的一個常用算法。它的工作原理也很簡單,大概就是把以上特征拼接并求 md5。
查看 tls 指紋的網站有:
- https://tls.browserleaks.com/json
- TrackMe | Home
- https://kawayiyi.com/tls
不同網站的生成的指紋可能有差異,但是多次訪問同一個網站生成的指紋是穩定的,而且能區分開
不同客戶端。下文以第一個網站為例。
2.直接安裝
pip install curl_cffi
功能簡介:
- 支持JA3/TLS和http2指紋模擬;
- 比requests/tls_client快分開,和aiohttp/pycurl的速度比肩;
- 預編譯,不需要自己的機器上再弄一遍;
- 支持asyncio,并且每個請求都可以換代理;
- 修改請求的API,支持http 2.0;
- 模仿requests。
先來看一段代碼:
import requests
from pprint import pprint
headers = {'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','cache-control': 'no-cache','dnt': '1','pragma': 'no-cache','sec-ch-ua': '"Chromium";v="118", "Microsoft Edge";v="118", "Not=A?Brand";v="99"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"macOS"','sec-fetch-dest': 'document','sec-fetch-mode': 'navigate','sec-fetch-site': 'same-origin','sec-fetch-user': '?1','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.46',
}response = requests.get('https://tls.browserleaks.com/json', headers=headers)
pprint(response.json())
運行效果:
這是直接使用Requests發起的請求。你可以試一試,加上代理以后,這里的ja3_hash
并不會發生變化。并且akamai_hash
和akamai_text
都是空。這個特征是非常明顯的,網站直接根據這些特征就可以屏蔽你的爬蟲。
于是為了完美模擬瀏覽器,國外大佬開發出了 curl-impersonate,將 curl 底層依賴的庫全部換成了瀏覽器使用的庫,并且版本也是一致的,這樣生成的指紋就和瀏覽器完全一樣了。
而 curl_cffi 正是 curl-impersonate 的 Python binding,我們直接使用 pip 安裝即可。
那這個問題咋解決尼?
只需要 把 import requests 改成 from curl_cffi import requests
。最后,在requests.get中加一個參數:impersonate="chrome110"
代碼如下:
# import requests
from curl_cffi import requests
from pprint import pprint
headers = {'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6','cache-control': 'no-cache','dnt': '1','pragma': 'no-cache','sec-ch-ua': '"Chromium";v="118", "Microsoft Edge";v="118", "Not=A?Brand";v="99"','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"macOS"','sec-fetch-dest': 'document','sec-fetch-mode': 'navigate','sec-fetch-site': 'same-origin','sec-fetch-user': '?1','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.46',
}response = requests.get('https://tls.browserleaks.com/json', headers=headers, impersonate="chrome110")
pprint(response.json())
這次再運行下代碼:
這次可以看到akamai_hash
和 akamai_text
已經都有了,網站已經無法識別你的爬蟲了。在網站看來,這只是一個Chrome 110
版本發起的請求。甚至Akamai需要的簽名也都有了。
支持使用 Sessions
session = requests.Session()
也支持使用代理
proxies = {"https": "xxxxx:7890"}
proxies=proxies
支持模擬的瀏覽器版本:
- chrome99、
- chrome100、
- chrome101、
- chrome104、
- chrome107、
- chrome110、
- chrome99_android、
- edge99、
- edge101、
- safari15_3、
- safari15_5
同樣它也支持asyncio,示例代碼如下所示:
from curl_cffi.requests import AsyncSessionasync with AsyncSession() as s:r = await s.get("https://example.com")
要使用異步寫法時,代碼如下:
import asyncio
from curl_cffi.requests import AsyncSessionurls = ["https://googel.com/","https://facebook.com/","https://twitter.com/",
]async def main():async with AsyncSession() as s:tasks = []for url in urls:task = s.get("https://example.com")tasks.append(task)results = await asyncio.gather(*tasks)asyncio.run(main())
我們以這個網站為例: https://apk.support/ 分析下:
瀏覽器抓包是能看到頁面返回的關鍵數據,但是再把這個請求放到postman 發個請求試一下:
Just a moment...
,完蛋涼了 標志性的5s盾。
咋辦 用curl_cffi
發個請求試一試?
代碼如下:
# import requests
from curl_cffi import requests
url = "https://apk.support/search?q=app"payload={}
headers = {'authority': 'apk.support','accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7','accept-language': 'zh-CN,zh;q=0.9,en;q=0.8','cache-control': 'no-cache','pragma': 'no-cache','referer': 'https://apk.support/search?q=app','sec-ch-ua-mobile': '?0','sec-ch-ua-platform': '"macOS"','sec-fetch-dest': 'document','sec-fetch-mode': 'navigate','sec-fetch-site': 'same-origin','sec-fetch-user': '?1','upgrade-insecure-requests': '1','user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
proxies = {'http': 'http://127.0.0.1:7890','https': 'http://127.0.0.1:7890'
}
response = requests.get(url=url, headers=headers, impersonate="chrome110", proxies=proxies)print(response.text)
看看效果:
可以看到返回的已經是正常數據,不再是5s盾了。
參考鏈接:
https://mp.weixin.qq.com/s/Ch7taYpD-dnNL2FLOuxgGA
https://blog.csdn.net/qiulin_wu/article/details/134180011
https://blog.csdn.net/resphina/article/details/132507212
https://www.jb51.net/python/302044jai.htm