Порівнюємо Python-бібліотеки для обходу бот-захисту
Мене звати Дмитро, я займаюсь збором даних з інтернету та автоматизацією процесів, пов’язаних з цим. Перед тим, як розпочати, хочу наголосити: важливо цінувати чужу працю, уникати зайвих і непотрібних запитів, не спамити вебсайти та завжди діяти відповідно до законодавства.
Штучний інтелект стає дедалі важливішим у нашому житті, впливаючи на різні аспекти суспільства. Його роль також суттєво змінила світ вебскрапінгу. Через агресивні стратегії збору даних, які використовують команди, що працюють над ШІ (оскільки всі намагаються зібрати більше даних для покращення своїх моделей), вебсайти посилили захист від ботів. У результаті традиційні інструменти для збору даних з вебсайтів або вебскрапінгу, як-от scrapy
і requests
, стають менш ефективними.
Для обходу цього захисту були розроблені спеціалізовані бібліотеки, такі як tls_client
та curl_cffi
. Обидві бібліотеки дозволяють імітувати поведінку реального користувача, обходячи тим самим фільтри, що блокують ботів. Однак між ними є низка відмінностей...
tls_client
На сторінці PyPI є лише мінімальний опис бібліотеки tls_client
:
## Python-TLS-Client Python-TLS-Client is an advanced HTTP library based on requests and tls-client. ## Examples The syntax is inspired by requests, so its very similar and there are only very few things that are different.
Не багато, чесно кажучи, тому я вирішив запитати у бота Gemini
: «What is the tls_client Python library?». Ось фрагмент відповіді:
The `tls_client` library in Python is a specialized tool designed for making HTTP requests with a strong focus on bypassing bot protection mechanisms. It achieves this by: - **TLS Fingerprinting**: The library can mimic the TLS fingerprint of specific browsers, making it appear as if a real browser is making the request. This helps to evade bot detection systems that rely on analyzing TLS characteristics. - **Browser-like Behavior**: `tls_client` can simulate various aspects of browser behavior, such as: - User-Agent headers - HTTP request methods - Request timing and intervals - Other relevant browser-specific features - **Stealth**: By mimicking real browser behavior, `tls_client` can help your requests remain undetected by bot protection systems, allowing you to bypass these mechanisms effectively.
Загалом все зрозуміло. Не хотілося б зупинятися на деталях бібліотеки. Краще перевіримо, як вона працює з вебсайтами (запитами). Нижче наведено скрипт, який я використав для цього експерименту. Подивімося, які саме headers передає ця бібліотека за замовчуванням.
import tls_client session = tls_client.Session( client_identifier="chrome112", random_tls_extension_order=True ) res = session.get( "https://postman-echo.com/headers", ) print(res.text)
Pезультат:
[{ "headers": { "host": "postman-echo.com", "x-request-start": "t1737710233.424", "connection": "close", "x-forwarded-proto": "https", "x-forwarded-port": "443", "x-amzn-trace-id": "Root=1-67935a99-606f44314d13eab1020b3628" } }, { "headers": { "host": "postman-echo.com", "x-request-start": "t1737710233.424", "connection": "close", "x-forwarded-proto": "https", "x-forwarded-port": "443", "x-amzn-trace-id": "Root=1-67935a99-606f44314d13eab1020b3628", "host": "postman-echo.com", "x-request-start": "t1737710233.424", "connection": "close", "x-forwarded-proto": "https", "x-forwarded-port": "443", "x-amzn-trace-id": "Root=1-67935a99-606f44314d13eab1020b3628", "connection": "close", "x-forwarded-proto": "https", "x-forwarded-port": "443", "x-amzn-trace-id": "Root=1-67935a99-606f44314d13eab1020b3628", "x-forwarded-port": "443", "x-amzn-trace-id": "Root=1-67935a99-606f44314d13eab1020b3628", "x-amzn-trace-id": "Root=1-67935a99-606f44314d13eab1020b3628", "accept": "*/*", "accept-encoding": "gzip, deflate, br", "user-agent": "tls-client/1.0.1" } }]
Мене зацікавив headers user-agent
, який за замовчуванням встановлено на «tls-client/1.0.1». Хоча я використовую параметр client_identifier
під час створення сесії, це значення залишилося незмінним. Щоб дізнатися, на що впливає цей параметр, скористаємося іншим API: tls.browserleaks.com/json
Приклад із ‘chrome112’:
# chrome112 import tls_client session = tls_client.Session( client_identifier="chrome112", random_tls_extension_order=True ) res = session.get( "https://tls.browserleaks.com/json", ) print(res.text)
Результат:
{ "user_agent": "tls-client/1.0.1", "ja3_hash": "957c782e99776836574ba40d58efef94", "ja3_text": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-17513-43-16-13-51-10-11-35-65037-23-45-18-65281-5-27,29-23-24,0", "ja3n_hash": "473f0e7c0b6a0f7b049072f4e683068b", "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-23-27-35-43-45-51-17513-65037-65281,29-23-24,0", "ja4": "t13d1516h2_8daaf6152771_02713d6af862", "ja4_r": "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,4469,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601", "ja4_o": "t13d1516h2_acb858a92679_28a27801816f", "ja4_ro": "t13d1516h2_1301,1302,1303,c02b,c02f,c02c,c030,cca9,cca8,c013,c014,009c,009d,002f,0035_0000,4469,002b,0010,000d,0033,000a,000b,0023,fe0d,0017,002d,0012,ff01,0005,001b_0403,0804,0401,0503,0805,0501,0806,0601", "akamai_hash": "52d84b11737d980aef856699f885ca86", "akamai_text": "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p" }
Приклад із firefox_117
:
# firefox_117 import tls_client session = tls_client.Session( client_identifier="firefox_117", random_tls_extension_order=True ) res = session.get( "https://tls.browserleaks.com/json", ) print(res.text)
Результат:
{ "user_agent": "tls-client/1.0.1", "ja3_hash": "d519c3b590dc914c42d977672aa9c08b", "ja3_text": "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,35-16-23-11-13-10-5-0-43-51-34-45-28-65281-21,29-23-24-25-256-257,0", "ja3n_hash": "b1efda11c805621e0f9cdc311958cb8c", "ja3n_text": "771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-5-10-11-13-16-21-23-28-34-35-43-45-51-65281,29-23-24-25-256-257,0", "ja4": "t13d1715h2_5b57614c22b0_3d5424432f57", "ja4_r": "t13d1715h2_002f,0035,009c,009d,1301,1302,1303,c009,c00a,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0015,0017,001c,0022,0023,002b,002d,0033,ff01_0403,0503,0603,0804,0805,0806,0401,0501,0601,0203,0201", "ja4_o": "t13d1715h2_5b234860e130_3f89fa4fa9d8", "ja4_ro": "t13d1715h2_1301,1303,1302,c02b,c02f,cca9,cca8,c02c,c030,c00a,c009,c013,c014,009c,009d,002f,0035_0023,0010,0017,000b,000d,000a,0005,0000,002b,0033,0022,002d,001c,ff01,0015_0403,0503,0603,0804,0805,0806,0401,0501,0601,0203,0201", "akamai_hash": "3d9132023bf26a71d40fe766e5c24c9d", "akamai_text": "1:65536;4:131072;5:16384|12517377|3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241|m,p,a,s" }
Тотбо:
client_identifier
не впливає наuser-agent
.- А от
ja3_hash
,ja3_text
таakamai_hash
суттєво відрізняються міжchrome112
іfirefox_117
.
Я не заглиблюватимусь у деталі про те, що таке ja3_hash
, ja3_text
та akamai_hash
, оскільки це не є темою цієї статті. Але ці параметри відіграють важливу роль в обході захисту від ботів.
curl_cffi
У README.md на GitHub
ми знаходимо такий опис:
# curl_cffi ... [Documentation](https://curl-cffi.readthedocs.io) Python binding for [curl-impersonate fork](https://github.com/lexiforest/curl-impersonate) via [cffi](https://cffi.readthedocs.io/en/latest/). Unlike other pure python http clients like `httpx` or `requests`, `curl_cffi` can impersonate browsers' TLS/JA3 and HTTP/2 fingerprints. If you are blocked by some website for no obvious reason, you can give `curl_cffi` a try. Only Python 3.8 and above are supported. Python 3.7 has reached its end of life. ...
Хмм, цікаво. Запитаємо у Gemini
більше інформації.
`curl_cffi` is a Python library that provides a powerful and efficient interface to the `libcurl` library, a widely-used open-source library for transferring data using various protocols (HTTP, FTP, etc.). ## Key Features - High Performance: Leverages the speed and robustness of libcurl for fast and reliable HTTP requests. - Low-Level Control: Offers fine-grained control over the request and response process, including: - Custom headers - SSL/TLS configuration - Proxy settings - HTTP m ethods (GET, POST, PUT, etc.) - Handling cookies and authentication - Thread Safety: Safe to use in multi-threaded applications. - Cross-Platform Compatibility: Works across various operating systems. - CFFI-based: Built on top of C Foreign Function Interface (CFFI), enabling efficient interaction with C libraries. ...
З опису видно, що curl_cffi
підтримує не тільки HTTP/HTTPS (тоді як tls_client
зосереджується на імітації поведінки браузера для обходу захисту від ботів), але й інші протоколи, наприклад FTP. Однак у контексті вебскрапінгу ця різниця може бути несуттєвою.
Перевіримо, що повертає бібліотека curl_cffi
при виконанні запиту.
from curl_cffi import requests r = requests.get("https://postman-echo.com/headers", impersonate="chrome") print(r.text)
Результат:
{ "headers": { "host": "postman-echo.com", "x-request-start": "t1737713426.623", "connection": "close", "x-forwarded-proto": "https", "x-forwarded-port": "443", "x-amzn-trace-id": "Root=1-67936712-115e75d03764be6e18a2df05", "sec-ch-ua": "\"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\", \"Not-A.Brand\";v=\"99\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"macOS\"", "upgrade-insecure-requests": "1", "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "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", "sec-fetch-site": "none", "sec-fetch-mode": "navigate", "sec-fetch-user": "?1", "sec-fetch-dest": "document", "accept-encoding": "gzip, deflate, br, zstd", "accept-language": "en-US,en;q=0.9", "priority": "u=0, i" } }
Звідси видно, що багато headers встановлюються без нашої участі, і не потрібно турбуватися про відповідність impersonate/client_identifier
із headers (наприклад, user-agent
). Це зручно, хоча й не завжди. Як і все в житті...
Перевіримо різницю у fingerprint
для різних impersonate
.
Для chrome
:
from curl_cffi import requests r = requests.get("https://tls.browserleaks.com/json", impersonate="chrome") print(r.text) class="sourceCode python"><a href="#cb13-1" aria-hidden="true"></a><code>from curl_cffi import requests </code><a href="#cb13-2" aria-hidden="true"></a><code> </code><a href="#cb13-3" aria-hidden="true"></a><code>r = requests.get("https://tls.browserleaks.com/json", impersonate="chrome") </code><a href="#cb13-4" aria-hidden="true"></a><code> </code><a href="#cb13-5" aria-hidden="true"></a><code>print(r.text)</code>
Результат:
{ "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "ja3_hash": "f1bfb5be52bd682e3aa5b4f1aa6aff4b", "ja3_text": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,23-13-45-5-35-18-17513-27-43-65037-11-65281-51-16-0-10,25497-29-23-24,0", "ja3n_hash": "4c9ce26028c11d7544da00d3f7e4f45c", "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-23-27-35-43-45-51-17513-65037-65281,25497-29-23-24,0", "ja4": "t13d1516h2_8daaf6152771_02713d6af862", "ja4_r": "t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,4469,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601", "ja4_o": "t13d1516h2_acb858a92679_a605c1873949", "ja4_ro": "t13d1516h2_1301,1302,1303,c02b,c02f,c02c,c030,cca9,cca8,c013,c014,009c,009d,002f,0035_0017,000d,002d,0005,0023,0012,4469,001b,002b,fe0d,000b,ff01,0033,0010,0000,000a_0403,0804,0401,0503,0805,0501,0806,0601", "akamai_hash": "52d84b11737d980aef856699f885ca86", "akamai_text": "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p" }
Для safari17_0
:
from curl_cffi import requests r = requests.get("https://tls.browserleaks.com/json", impersonate="safari17_0") print(r.text)
Результат:
{ "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15", "ja3_hash": "773906b0efdefa24a7f2b8eb6985bf37", "ja3_text": "771,4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10,0-23-65281-10-11-16-5-13-18-51-45-43-27-21,29-23-24-25,0", "ja3n_hash": "44f7ed5185d22c92b96da72dbe68d307", "ja3n_text": "771,4865-4866-4867-49196-49195-52393-49200-49199-52392-49162-49161-49172-49171-157-156-53-47-49160-49170-10,0-5-10-11-13-16-18-21-23-27-43-45-51-65281,29-23-24-25,0", "ja4": "t13d2014h2_a09f3c656075_14788d8d241b", "ja4_r": "t13d2014h2_000a,002f,0035,009c,009d,1301,1302,1303,c008,c009,c00a,c012,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0015,0017,001b,002b,002d,0033,ff01_0403,0804,0401,0503,0203,0805,0805,0501,0806,0601,0201", "ja4_o": "t13d2014h2_de3eb69493ac_65135c5c1a6b", "ja4_ro": "t13d2014h2_1301,1302,1303,c02c,c02b,cca9,c030,c02f,cca8,c00a,c009,c014,c013,009d,009c,0035,002f,c008,c012,000a_0000,0017,ff01,000a,000b,0010,0005,000d,0012,0033,002d,002b,001b,0015_0403,0804,0401,0503,0203,0805,0805,0501,0806,0601,0201", "akamai_hash": "959a7e813b79b909a1a0b00a38e8bba3", "akamai_text": "2:0;4:4194304;3:100|10485760|0|m,s,p,a" }
Результати для сhrome
та safari17_0
показують різні фінгерпринти (ja3_hash
, ja3_text
, akamai_hash
тощо). Ця різниця демонструє, як проста зміна impersonate
може суттєво вплинути на фінгерпринт HTTP-запиту, що допомагає обходити захист від ботів.
Висновок
Я не стверджую, що curl_cffi
краща чи гірша за tls_client
; моєю метою було показати, як працюють обидві бібліотеки, та розглянути ключові концепції. Остаточний вибір залежить від ваших потреб. Якщо вас цікавить порівняння швидкості цих бібліотек, ознайомтеся з цим топіком.
5 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів