notify-tls-client 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,10 +44,15 @@ class RecoveryConfig:
44
44
  instantiate_new_client_on_forbidden_response: bool = False
45
45
  instantiate_new_client_on_exception: bool = False
46
46
  change_client_identifier_on_forbidden_response: bool = False
47
- status_codes_to_forbidden_response_handle: list[int] = field(default_factory=lambda: [403])
47
+ status_codes_to_forbidden_response_handle: tuple[int, ...] = field(default_factory=lambda: (403,))
48
48
 
49
49
  def __post_init__(self):
50
50
  """Valida os parâmetros de configuração."""
51
+ # Converter lista para tuple se necessário (compatibilidade)
52
+ if isinstance(self.status_codes_to_forbidden_response_handle, list):
53
+ object.__setattr__(self, 'status_codes_to_forbidden_response_handle',
54
+ tuple(self.status_codes_to_forbidden_response_handle))
55
+
51
56
  if not self.status_codes_to_forbidden_response_handle:
52
57
  raise ValueError("status_codes_to_forbidden_response_handle não pode ser vazio")
53
58
 
@@ -3,6 +3,8 @@ from datetime import datetime
3
3
  from typing import Optional
4
4
  from urllib.parse import urlparse
5
5
 
6
+ from orjson import orjson
7
+
6
8
  from notify_tls_client.tls_client.response import Response
7
9
 
8
10
  logger = logging.getLogger(__name__)
@@ -79,41 +81,65 @@ def _log_request_info(self,
79
81
  response: Optional[Response],
80
82
  request_url: Optional[str],
81
83
  request_headers: dict):
84
+ if not logger.isEnabledFor(logging.DEBUG):
85
+ return
86
+
82
87
  if response:
83
- logger.debug(f"""Request finished
84
- client_identifier={self.get_current_client_identifier()}
85
- request_url={request_url}
86
- request_headers={request_headers}
87
- response_url={response.url}
88
- status_code={response.status_code}
89
- response_time={response.elapsed}ms
90
- response_headers={dict(response.headers)}
91
- proxy={self.client.proxies['http'] if self.client.proxies else None}
92
- """,
88
+ proxy = self.client.proxies['http'] if self.client.proxies else None
89
+ client_id = self.get_current_client_identifier()
90
+ response_headers_dict = dict(response.headers)
91
+
92
+ # Serializar dados complexos com orjson (mais rápido)
93
+ try:
94
+ request_headers_json = orjson.dumps(request_headers).decode('utf-8')
95
+ response_headers_json = orjson.dumps(response_headers_dict).decode('utf-8')
96
+ except Exception:
97
+ request_headers_json = str(request_headers)
98
+ response_headers_json = str(response_headers_dict)
99
+
100
+ logger.debug("Request finished\n"
101
+ " client_identifier=%s\n"
102
+ " request_url=%s\n"
103
+ " request_headers=%s\n"
104
+ " response_url=%s\n"
105
+ " status_code=%s\n"
106
+ " response_time=%sms\n"
107
+ " response_headers=%s\n"
108
+ " proxy=%s",
109
+ client_id, request_url, request_headers_json, response.url,
110
+ response.status_code, response.elapsed, response_headers_json, proxy,
93
111
  extra={
94
112
  "date": datetime.now().strftime("%d-%m-%Y %H:%M:%S.%f")[:-3],
95
113
  "request_url": request_url,
96
114
  "request_headers": request_headers,
97
115
  "response_url": response.url,
98
116
  "status_code": response.status_code,
99
- "response_headers": dict(response.headers),
117
+ "response_headers": response_headers_dict,
100
118
  "response_elapsed_ms": response.elapsed,
101
- "proxy": self.client.proxies['http'] if self.client.proxies else None,
102
- "client_identifier": self.get_current_client_identifier()
103
-
119
+ "proxy": proxy,
120
+ "client_identifier": client_id
104
121
  })
105
122
 
106
123
  if not response:
107
- logger.debug(f"""Request failed before getting a response
108
- client_identifier={self.get_current_client_identifier()}
109
- request_url={request_url}
110
- request_headers={request_headers}
111
- response_url=None
112
- status_code=None
113
- response_headers=None
114
- response_time=0
115
- proxy={self.client.proxies['http'] if self.client.proxies else None}
116
- """,
124
+ proxy = self.client.proxies['http'] if self.client.proxies else None
125
+ client_id = self.get_current_client_identifier()
126
+
127
+ # Serializar request_headers com orjson
128
+ try:
129
+ request_headers_json = orjson.dumps(request_headers).decode('utf-8')
130
+ except Exception:
131
+ request_headers_json = str(request_headers)
132
+
133
+ logger.debug("Request failed before getting a response\n"
134
+ " client_identifier=%s\n"
135
+ " request_url=%s\n"
136
+ " request_headers=%s\n"
137
+ " response_url=None\n"
138
+ " status_code=None\n"
139
+ " response_headers=None\n"
140
+ " response_time=0\n"
141
+ " proxy=%s",
142
+ client_id, request_url, request_headers_json, proxy,
117
143
  extra={
118
144
  "date": datetime.now().strftime("%d-%m-%Y %H:%M:%S.%f")[:-3],
119
145
  "request_url": request_url,
@@ -122,9 +148,8 @@ def _log_request_info(self,
122
148
  "status_code": None,
123
149
  "response_headers": None,
124
150
  "response_elapsed_ms": 0,
125
- "proxy": self.client.proxies['http'] if self.client.proxies else None,
126
- "client_identifier": self.get_current_client_identifier()
127
-
151
+ "proxy": proxy,
152
+ "client_identifier": client_id
128
153
  })
129
154
 
130
155
 
@@ -7,12 +7,15 @@ from notify_tls_client.tls_client.settings import ClientIdentifiers
7
7
 
8
8
  @dataclass
9
9
  class ClientIdentifiersManager:
10
- _items: list[ClientIdentifiers] = field(default_factory=list)
10
+ _items: tuple[ClientIdentifiers, ...] = field(default_factory=tuple)
11
11
  _current_index: int = 0
12
12
  _current_item: Optional[ClientIdentifiers] = None
13
13
 
14
14
  def __post_init__(self):
15
15
  self._lock = threading.Lock()
16
+ # Converter lista para tuple se necessário
17
+ if isinstance(self._items, list):
18
+ object.__setattr__(self, '_items', tuple(self._items))
16
19
 
17
20
  def get_next(self) -> ClientIdentifiers:
18
21
  with self._lock:
@@ -26,19 +29,19 @@ class ClientIdentifiersManager:
26
29
  return _item
27
30
 
28
31
  def get_current_item(self) -> Optional[ClientIdentifiers]:
29
- with self._lock:
30
- return self._current_item
32
+ # Lock-free read - safe para strings imutáveis
33
+ return self._current_item
31
34
 
32
35
  def set_items(self, items: list[ClientIdentifiers]):
33
36
  with self._lock:
34
- self._items = items
37
+ self._items = tuple(items) if isinstance(items, list) else items
35
38
  self._current_index = 0
36
39
  self._current_item = None
37
40
 
38
- def get_item(self) -> list[ClientIdentifiers]:
39
- with self._lock:
40
- return self._items.copy()
41
+ def get_item(self) -> tuple[ClientIdentifiers, ...]:
42
+ # Lock-free read - tuple é imutável
43
+ return self._items
41
44
 
42
45
  def get_total_items(self) -> int:
43
- with self._lock:
44
- return len(self._items)
46
+ # Lock-free read - len é thread-safe para objetos imutáveis
47
+ return len(self._items)
@@ -19,6 +19,16 @@ logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
21
  class NotifyTLSClient:
22
+ __slots__ = (
23
+ 'config', 'client', 'free', 'requests_amount', 'requests_amount_with_current_proxy',
24
+ 'requests_amount_with_current_client_identifier', 'last_request_status', 'current_proxy',
25
+ '_lock', 'client_identifiers_manager', 'proxies_manager', 'requests_limit_with_same_proxy',
26
+ 'requests_limit_with_same_client_identifier', 'random_tls_extension_order',
27
+ 'instantiate_new_client_on_forbidden_response', 'instantiate_new_client_on_exception',
28
+ 'change_client_identifier_on_forbidden_response', 'status_codes_to_forbidden_response_handle',
29
+ 'disable_http3', 'debug_mode', 'headers'
30
+ )
31
+
22
32
  def __init__(self,
23
33
  config: Optional[ClientConfiguration] = None,
24
34
  # Parâmetros legados - mantidos para compatibilidade mas deprecados
@@ -143,34 +153,35 @@ class NotifyTLSClient:
143
153
  random_tls_extension_order: bool = False,
144
154
  proxy: Optional[Proxy] = None):
145
155
 
146
- with self._lock:
147
- old_client_identifier = None
148
- old_client = None
156
+ # Preparar novo cliente fora do lock
157
+ new_client = Session(client_identifier=client_identifier,
158
+ random_tls_extension_order=random_tls_extension_order,
159
+ disable_http3=self.disable_http3)
149
160
 
150
- if self.client:
151
- old_client_identifier = self.client.client_identifier
152
- old_client = self.client
161
+ # Aplicar configurações ao novo cliente
162
+ if proxy:
163
+ new_client.proxies = proxy.to_proxy_dict()
153
164
 
154
- self.client = Session(client_identifier=client_identifier,
155
- random_tls_extension_order=random_tls_extension_order,
156
- disable_http3=self.disable_http3)
165
+ if self.headers:
166
+ new_client.headers = CaseInsensitiveDict(self.headers)
157
167
 
158
- if old_client:
159
- old_client.close()
168
+ # Lock apenas para swap atômico
169
+ with self._lock:
170
+ old_client = self.client
171
+ old_client_identifier = old_client.client_identifier if old_client else None
172
+
173
+ self.client = new_client
160
174
 
161
175
  if old_client_identifier != client_identifier:
162
176
  self.requests_amount_with_current_client_identifier = 0
163
177
 
164
- if proxy:
165
- self.client.proxies = proxy.to_proxy_dict()
166
-
167
- if self.current_proxy != proxy:
168
- self.current_proxy = proxy
169
- self.requests_amount_with_current_proxy = 0
178
+ if proxy and self.current_proxy != proxy:
179
+ self.current_proxy = proxy
180
+ self.requests_amount_with_current_proxy = 0
170
181
 
171
- # Aplicar headers padrão
172
- if self.headers:
173
- self.client.headers = CaseInsensitiveDict(self.headers)
182
+ # Fechar cliente antigo fora do lock
183
+ if old_client:
184
+ old_client.close()
174
185
 
175
186
 
176
187
  def set_requests_limit_same_proxy(self, requests_limit_same_proxy: int):
@@ -8,22 +8,33 @@ from dataclasses_json import dataclass_json
8
8
  @dataclass_json
9
9
  @dataclass
10
10
  class Proxy:
11
+ __slots__ = ('host', 'port', 'username', 'password', '_proxy_dict_cache')
12
+
11
13
  host: str
12
14
  port: int
13
15
  username: Optional[str] = None
14
16
  password: Optional[str] = None
15
17
 
18
+ def __post_init__(self):
19
+ object.__setattr__(self, '_proxy_dict_cache', None)
20
+
16
21
  def to_proxy_dict(self):
22
+ if self._proxy_dict_cache is not None:
23
+ return self._proxy_dict_cache
24
+
17
25
  if self.username is None:
18
- return {
19
- 'http': f'http://{self.host}:{self.port}',
20
- 'https': f'https://{self.host}:{self.port}'
26
+ proxy_dict = {
27
+ 'http': 'http://' + self.host + ':' + str(self.port),
28
+ 'https': 'https://' + self.host + ':' + str(self.port)
29
+ }
30
+ else:
31
+ proxy_dict = {
32
+ 'http': 'http://' + self.username + ':' + self.password + '@' + self.host + ':' + str(self.port),
33
+ 'https': 'https://' + self.username + ':' + self.password + '@' + self.host + ':' + str(self.port)
21
34
  }
22
35
 
23
- return {
24
- 'http': f'http://{self.username}:{self.password}@{self.host}:{self.port}',
25
- 'https': f'https://{self.username}:{self.password}@{self.host}:{self.port}'
26
- }
36
+ object.__setattr__(self, '_proxy_dict_cache', proxy_dict)
37
+ return proxy_dict
27
38
 
28
39
  def __eq__(self, other):
29
40
  if not isinstance(other, Proxy):
@@ -58,8 +69,8 @@ class ProxiesManager:
58
69
  return proxy
59
70
 
60
71
  def get_current_proxy(self) -> Optional[Proxy]:
61
- with self._lock:
62
- return self._current_proxy
72
+ # Lock-free read - Proxy é imutável (frozen dataclass)
73
+ return self._current_proxy
63
74
 
64
75
  def set_proxies(self, proxies: list[Proxy]):
65
76
  with self._lock:
@@ -69,11 +80,14 @@ class ProxiesManager:
69
80
 
70
81
  def add_proxy(self, proxy: Proxy):
71
82
  with self._lock:
72
- self._proxies.append(proxy)
83
+ # Criar nova lista ao invés de modificar in-place
84
+ new_proxies = self._proxies.copy()
85
+ new_proxies.append(proxy)
86
+ self._proxies = new_proxies
73
87
 
74
88
  def get_proxies(self) -> list[Proxy]:
75
- with self._lock:
76
- return self._proxies.copy()
89
+ # Lock-free read - retornar referência direta (lista é copiada pelo caller se necessário)
90
+ return self._proxies
77
91
 
78
92
 
79
93
 
@@ -9,6 +9,7 @@ import json
9
9
 
10
10
  class Response:
11
11
  """object, which contains the response to an HTTP request."""
12
+ __slots__ = ('elapsed', 'url', 'status_code', 'text', 'headers', 'cookies', '_content', '_content_consumed')
12
13
 
13
14
  def __init__(self):
14
15
 
@@ -0,0 +1,338 @@
1
+ Metadata-Version: 2.4
2
+ Name: notify-tls-client
3
+ Version: 2.0.1
4
+ Summary: Cliente HTTP avançado com TLS fingerprinting, rotação automática de proxies e recuperação inteligente para web scraping profissional
5
+ Author-email: Jeferson Albara <jeferson.albara@example.com>
6
+ Maintainer-email: Jeferson Albara <jeferson.albara@example.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/jefersonAlbara/notify-tls-client
9
+ Project-URL: Documentation, https://github.com/jefersonAlbara/notify-tls-client#readme
10
+ Project-URL: Repository, https://github.com/jefersonAlbara/notify-tls-client
11
+ Project-URL: Issues, https://github.com/jefersonAlbara/notify-tls-client/issues
12
+ Project-URL: Changelog, https://github.com/jefersonAlbara/notify-tls-client/blob/main/CHANGELOG.md
13
+ Keywords: tls-client,http-client,web-scraping,proxy-rotation,tls-fingerprinting,browser-emulation,http2,http3,requests,automation
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Internet :: Proxy Servers
23
+ Classifier: Operating System :: OS Independent
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Description-Content-Type: text/markdown
27
+ Requires-Dist: dataclasses-json>=0.6.0
28
+ Requires-Dist: typing-extensions>=4.8.0
29
+ Requires-Dist: orjson>=3.9.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.4.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
33
+ Requires-Dist: black>=23.0.0; extra == "dev"
34
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+
37
+ # Notify TLS Client
38
+
39
+ [![PyPI version](https://badge.fury.io/py/notify-tls-client.svg)](https://badge.fury.io/py/notify-tls-client)
40
+ [![Python Version](https://img.shields.io/pypi/pyversions/notify-tls-client.svg)](https://pypi.org/project/notify-tls-client/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
42
+
43
+ Cliente HTTP avançado em Python com suporte a TLS/SSL customizado, fingerprinting de navegadores e rotação automática de proxies. Construído sobre a biblioteca `tls-client` com funcionalidades adicionais para web scraping e automação resiliente.
44
+
45
+ ## 🚀 Características Principais
46
+
47
+ - **Fingerprinting TLS Avançado**: Emula múltiplos navegadores (Chrome, Firefox, Safari, Edge, Mobile)
48
+ - **Rotação Automática**: Proxies e client identifiers com políticas configuráveis
49
+ - **Recuperação Automática**: Reconexão inteligente em erros e respostas proibidas
50
+ - **Thread-Safe**: Uso seguro em ambientes multi-threaded
51
+ - **Configuração Modular**: Sistema de configuração baseado em objetos reutilizáveis
52
+ - **Presets Prontos**: Configurações pré-definidas para casos de uso comuns
53
+ - **HTTP/3 Support**: Suporte opcional a QUIC/HTTP3
54
+
55
+ ## 📦 Instalação
56
+
57
+ ```bash
58
+ pip install notify-tls-client
59
+ ```
60
+
61
+ ### Requisitos
62
+
63
+ - Python >= 3.12
64
+ - Sistema operacional: Windows, macOS, Linux (x86_64, ARM64)
65
+
66
+ ## 🎯 Quick Start
67
+
68
+ ### Uso Básico
69
+
70
+ ```python
71
+ from notify_tls_client import NotifyTLSClient
72
+
73
+ # Cliente com configuração padrão
74
+ client = NotifyTLSClient()
75
+
76
+ # Fazer requisição
77
+ response = client.get("https://api.example.com/data")
78
+ print(response.status_code)
79
+ print(response.json())
80
+ ```
81
+
82
+ ### Usando Presets (Recomendado)
83
+
84
+ ```python
85
+ from notify_tls_client import NotifyTLSClient
86
+ from notify_tls_client.config import ClientConfiguration
87
+ from notify_tls_client.core.proxiesmanager import ProxiesManagerLoader
88
+
89
+ # Carregar proxies
90
+ proxies = ProxiesManagerLoader().from_txt("proxies.txt")
91
+
92
+ # Preset para scraping agressivo
93
+ config = ClientConfiguration.aggressive(proxies)
94
+ client = NotifyTLSClient(config)
95
+
96
+ # Fazer múltiplas requisições
97
+ for i in range(100):
98
+ response = client.get("https://example.com/api/endpoint")
99
+ print(f"Request {i}: {response.status_code}")
100
+ ```
101
+
102
+ ### Configuração Customizada
103
+
104
+ ```python
105
+ from notify_tls_client import NotifyTLSClient
106
+ from notify_tls_client.config import (
107
+ ClientConfiguration,
108
+ RotationConfig,
109
+ RecoveryConfig,
110
+ ClientConfig
111
+ )
112
+
113
+ config = ClientConfiguration(
114
+ proxies_manager=proxies,
115
+ rotation=RotationConfig(
116
+ requests_limit_same_proxy=50,
117
+ requests_limit_same_client_identifier=200,
118
+ random_tls_extension_order=True
119
+ ),
120
+ recovery=RecoveryConfig(
121
+ instantiate_new_client_on_forbidden_response=True,
122
+ instantiate_new_client_on_exception=True,
123
+ change_client_identifier_on_forbidden_response=True,
124
+ status_codes_to_forbidden_response_handle=[403, 429, 503]
125
+ ),
126
+ client=ClientConfig(
127
+ client_identifiers=["chrome_133", "firefox_120", "safari_17_0"],
128
+ disable_http3=False,
129
+ debug_mode=False
130
+ )
131
+ )
132
+
133
+ client = NotifyTLSClient(config)
134
+ ```
135
+
136
+ ## 📚 Presets Disponíveis
137
+
138
+ ### Simple
139
+ Uso básico com rotação de proxies padrão.
140
+ ```python
141
+ config = ClientConfiguration.simple(proxies)
142
+ ```
143
+
144
+ ### Aggressive
145
+ Para scraping intensivo com recuperação automática completa.
146
+ ```python
147
+ config = ClientConfiguration.aggressive(proxies)
148
+ ```
149
+ - Troca proxy a cada 10 requisições
150
+ - Troca client identifier a cada 50 requisições
151
+ - Recuperação automática em erros e 403/429/503
152
+ - Múltiplos client identifiers
153
+
154
+ ### Stealth
155
+ Foco em evitar detecção através de diversidade.
156
+ ```python
157
+ config = ClientConfiguration.stealth(proxies)
158
+ ```
159
+ - 4 client identifiers diferentes
160
+ - Ordem de extensões TLS randomizada
161
+ - Rotação moderada (100 req/proxy)
162
+
163
+ ### Mobile
164
+ Simula dispositivos móveis.
165
+ ```python
166
+ # Android
167
+ config = ClientConfiguration.mobile(proxies, platform="android")
168
+
169
+ # iOS
170
+ config = ClientConfiguration.mobile(proxies, platform="ios")
171
+ ```
172
+
173
+ ## 🔧 Funcionalidades Avançadas
174
+
175
+ ### Rotação de Proxies
176
+
177
+ ```python
178
+ from notify_tls_client.core.proxiesmanager import ProxiesManagerLoader
179
+
180
+ # Carregar de arquivo
181
+ proxies = ProxiesManagerLoader().from_txt("proxies.txt")
182
+
183
+ # Formato do arquivo (um por linha):
184
+ # host:port
185
+ # host:port:username:password
186
+ # http://username:password@host:port
187
+ ```
188
+
189
+ ### Client Identifiers Suportados
190
+
191
+ **Desktop:**
192
+ - Chrome: `chrome_133`, `chrome_131`, `chrome_120`, etc.
193
+ - Firefox: `firefox_120`, `firefox_117`, `firefox_110`, etc.
194
+ - Safari: `safari_17_0`, `safari_16_0`, etc.
195
+ - Edge, Opera
196
+
197
+ **Mobile:**
198
+ - Android: `okhttp4_android_13`, `okhttp4_android_12`, etc.
199
+ - iOS: `safari_ios_16_0`, `safari_ios_15_6`, etc.
200
+
201
+ ### Recuperação Automática
202
+
203
+ ```python
204
+ config = ClientConfiguration(
205
+ recovery=RecoveryConfig(
206
+ # Criar nova sessão em respostas proibidas
207
+ instantiate_new_client_on_forbidden_response=True,
208
+
209
+ # Criar nova sessão em exceções
210
+ instantiate_new_client_on_exception=True,
211
+
212
+ # Trocar identifier em respostas proibidas
213
+ change_client_identifier_on_forbidden_response=True,
214
+
215
+ # Status codes que acionam recuperação
216
+ status_codes_to_forbidden_response_handle=[403, 429, 503]
217
+ )
218
+ )
219
+ ```
220
+
221
+ ### Headers Customizados
222
+
223
+ ```python
224
+ config = ClientConfiguration(
225
+ client=ClientConfig(
226
+ default_headers={
227
+ "User-Agent": "Mozilla/5.0...",
228
+ "Accept-Language": "pt-BR,pt;q=0.9",
229
+ "Custom-Header": "value"
230
+ }
231
+ )
232
+ )
233
+
234
+ # Ou por requisição
235
+ response = client.get(
236
+ "https://example.com",
237
+ headers={"Authorization": "Bearer token"}
238
+ )
239
+ ```
240
+
241
+ ### Cookies
242
+
243
+ ```python
244
+ # Obter todos os cookies
245
+ cookies = client.get_cookies()
246
+
247
+ # Obter cookie específico
248
+ value = client.get_cookie_by_name("session_id")
249
+
250
+ # Definir cookie
251
+ client.set_cookie("name", "value")
252
+ ```
253
+
254
+ ## 🔒 Thread Safety
255
+
256
+ A biblioteca é thread-safe e pode ser usada em ambientes multi-threaded:
257
+
258
+ ```python
259
+ import concurrent.futures
260
+
261
+ client = NotifyTLSClient(ClientConfiguration.aggressive(proxies))
262
+
263
+ def make_request(url):
264
+ return client.get(url)
265
+
266
+ with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
267
+ urls = ["https://example.com"] * 100
268
+ results = list(executor.map(make_request, urls))
269
+ ```
270
+
271
+ ## 📊 Logging
272
+
273
+ ```python
274
+ import logging
275
+
276
+ # Habilitar logs de debug
277
+ logging.basicConfig(level=logging.DEBUG)
278
+
279
+ # Ou configurar apenas para notify_tls_client
280
+ logger = logging.getLogger("notify_tls_client")
281
+ logger.setLevel(logging.DEBUG)
282
+ ```
283
+
284
+ ## 🛠️ Métodos HTTP Suportados
285
+
286
+ ```python
287
+ # GET
288
+ response = client.get(url, params={"key": "value"})
289
+
290
+ # POST
291
+ response = client.post(url, json={"data": "value"})
292
+ response = client.post(url, data="form data")
293
+
294
+ # PUT
295
+ response = client.put(url, json={"data": "value"})
296
+
297
+ # PATCH
298
+ response = client.patch(url, json={"data": "value"})
299
+
300
+ # DELETE
301
+ response = client.delete(url)
302
+ ```
303
+
304
+ ## 📖 Documentação Completa
305
+
306
+ Para documentação detalhada sobre arquitetura, componentes internos e exemplos avançados, consulte:
307
+ - [CLAUDE.md](CLAUDE.md) - Guia completo de desenvolvimento
308
+ - [examples/](examples/) - Exemplos de código
309
+
310
+ ## 🤝 Contribuindo
311
+
312
+ Contribuições são bem-vindas! Por favor, leia [CONTRIBUTING.md](CONTRIBUTING.md) para detalhes sobre nosso código de conduta e processo de submissão de pull requests.
313
+
314
+ ## 📝 Changelog
315
+
316
+ Veja [CHANGELOG.md](CHANGELOG.md) para histórico de versões e mudanças.
317
+
318
+ ## 📄 Licença
319
+
320
+ Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para detalhes.
321
+
322
+ ## ⚠️ Aviso Legal
323
+
324
+ Esta biblioteca é fornecida apenas para fins educacionais e de pesquisa. O uso desta ferramenta para violar termos de serviço de websites, realizar scraping não autorizado ou qualquer atividade ilegal é de sua responsabilidade. Os desenvolvedores não se responsabilizam pelo uso indevido desta biblioteca.
325
+
326
+ ## 🙏 Agradecimentos
327
+
328
+ - [tls-client](https://github.com/bogdanfinn/tls-client) - Biblioteca Go subjacente para TLS fingerprinting
329
+ - Comunidade Python por ferramentas e bibliotecas incríveis
330
+
331
+ ## 📞 Suporte
332
+
333
+ - **Issues**: [GitHub Issues](https://github.com/jefersonAlbara/notify-tls-client/issues)
334
+ - **Discussões**: [GitHub Discussions](https://github.com/jefersonAlbara/notify-tls-client/discussions)
335
+
336
+ ---
337
+
338
+ **Desenvolvido com ❤️ para a comunidade Python**
@@ -2,22 +2,22 @@ notify_tls_client/__init__.py,sha256=APA8wxvkGYZiVMMbTWGo7prB3iKE-OzUajd0bfc8PwE
2
2
  notify_tls_client/config/__init__.py,sha256=2xsw7M2E8-ncpIDySYcgH500xAnYyO3qy3PRXkP2inQ,495
3
3
  notify_tls_client/config/client_config.py,sha256=Fwg6LS1zP8XFRPbTxNj2svCqeWh2GJcqrq7hnpQtYRQ,2837
4
4
  notify_tls_client/config/client_configuration.py,sha256=Zl71lxZSKU7oVJwnHPSYV9uufk2IgFtHW3u9deqo5HU,9103
5
- notify_tls_client/config/recovery_config.py,sha256=L5a_jMUfdUMdFBXeLPz9RAU0oXz-Ma4ZuzG7B-h7hDs,2876
5
+ notify_tls_client/config/recovery_config.py,sha256=zKBjSdCHJ4iF0PXUawPh3EktOxGdn-8bzpjl7LOIoa4,3202
6
6
  notify_tls_client/config/rotation_config.py,sha256=Av_K6BzgPknFYZXxe9WmDra8VGI9JEpDJSm0dDs8QqM,2089
7
7
  notify_tls_client/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- notify_tls_client/core/client_identifiers_manager.py,sha256=ko4Y5IVMxNTRgk4e6jiJF_kcMjzej9rRt7BJtX-OMyg,1346
9
- notify_tls_client/core/notifytlsclient.py,sha256=Mj6myXtzc_M52oOj_-cj4NcSx2WOuDIfg396CtOLmDo,12598
8
+ notify_tls_client/core/client_identifiers_manager.py,sha256=D8xJ89aaxZ_8Pssx9VqEH05G8cp3bMez14-KrTMtJiU,1646
9
+ notify_tls_client/core/notifytlsclient.py,sha256=CcDS-yFj5g3Vw-mtbu_AFXZZD7wDjyPF_P5ha4vysqo,13307
10
10
  notify_tls_client/core/client/__init__.py,sha256=VKjpq02hC-r9ER6mbfkwG6W2ybv_jND9d9bidte0CjU,51
11
- notify_tls_client/core/client/decorators.py,sha256=GjGUl5eE5KZGB7Bc6XRFgmJ1Xjz6gNBXxOH8jldCAl8,8554
11
+ notify_tls_client/core/client/decorators.py,sha256=TMKHLkWuVUy802TzY3iLwR1WV9QymcCxCelzBocPJH0,9705
12
12
  notify_tls_client/core/proxiesmanager/__init__.py,sha256=emF4vnZvSfb9zlHkt9dDdTcGwkfs1DADi7XVw_DxsWs,105
13
- notify_tls_client/core/proxiesmanager/proxiesmanager.py,sha256=V-X5dWskGHUjrX-hpl9E4L6pv53LBg1M0giEbWmG6JE,2286
13
+ notify_tls_client/core/proxiesmanager/proxiesmanager.py,sha256=kfW7nEIyeXtDj1YzaAZo2ctPGMk3MnNIXV15Cj7iiY0,3010
14
14
  notify_tls_client/core/proxiesmanager/proxiesmanagerloader.py,sha256=7xr3SVdRnr95KWOdk15iehOCXG2huA-rY1j9VIe30YQ,1179
15
15
  notify_tls_client/tls_client/__init__.py,sha256=sThiIAzA650rfBlqZ_xalTjgTysUsjKua5ODnqyvhUE,669
16
16
  notify_tls_client/tls_client/__version__.py,sha256=32ZZ-ufGC9Yo6oPk5a1xig8YKJ2ZkRXnXoVqiqO0ptg,395
17
17
  notify_tls_client/tls_client/cffi.py,sha256=pedwBcQOwJvI66yp5GpyNU6zoqrQhTv3ocM1-1PtUm0,1291
18
18
  notify_tls_client/tls_client/cookies.py,sha256=1fIOnFDMWMeXuAjQybSrUJXnyjhP-_jJ68AxBUZYgYU,15609
19
19
  notify_tls_client/tls_client/exceptions.py,sha256=xIoFb5H3Suk7XssQ-yw3I1DBkPLqnDXsiAe2MMp1qNQ,80
20
- notify_tls_client/tls_client/response.py,sha256=7j4iJa59Hcdw5ZWPvaxgLTvC_AWrch9hwMbrK4HNJjo,2618
20
+ notify_tls_client/tls_client/response.py,sha256=v4mQhZroNL9203mA5KDJvxSP4GntVq0sjYRtJNfIoE0,2732
21
21
  notify_tls_client/tls_client/sessions.py,sha256=1xeUDR9H42wS900mCzkpu4At7Lx1O07QGoDuThbLG0M,19233
22
22
  notify_tls_client/tls_client/settings.py,sha256=x_2Vrph-QebbCjkxmnX8UPd5oYYU4ClqPOpfoqelno4,1485
23
23
  notify_tls_client/tls_client/structures.py,sha256=md-tJmo8X5bad0KrMUTVN8jxUIvui7NiBxaf10bLULU,2517
@@ -26,7 +26,7 @@ notify_tls_client/tls_client/dependencies/tls-client-darwin-amd64-1.12.0.dylib,s
26
26
  notify_tls_client/tls_client/dependencies/tls-client-linux-arm64-1.12.0.so,sha256=fQvdtCiRRS228WrFUE_ucq4OPC4Z7QU4_KI4B3Gf97Y,14404088
27
27
  notify_tls_client/tls_client/dependencies/tls-client-linux-ubuntu-amd64-1.12.0.so,sha256=UTvtZa93fWLWaSBINC_Cu8mNoLwsVdcQbQZsdQnZrJM,15210112
28
28
  notify_tls_client/tls_client/dependencies/tls-client-windows-64-1.12.0.dll,sha256=uk80UEGW8WepYMglh1Yo6VSrBSNDwon-OyFqE_1bWmM,24849278
29
- notify_tls_client-2.0.0.dist-info/METADATA,sha256=O_m_lTWjraTbcqLAHr1GxwtNwOzHWnb6T1InhT5qiUA,1941
30
- notify_tls_client-2.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
- notify_tls_client-2.0.0.dist-info/top_level.txt,sha256=fq9YA0cFdpCuUO7cdMFN7oxm1zDfZm_m1KPXehUqA5o,18
32
- notify_tls_client-2.0.0.dist-info/RECORD,,
29
+ notify_tls_client-2.0.1.dist-info/METADATA,sha256=Fk4CucsgT0kffRjqOMLZQ1eFFDAU0Z3yg1YMCMdcdI4,10565
30
+ notify_tls_client-2.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
+ notify_tls_client-2.0.1.dist-info/top_level.txt,sha256=fq9YA0cFdpCuUO7cdMFN7oxm1zDfZm_m1KPXehUqA5o,18
32
+ notify_tls_client-2.0.1.dist-info/RECORD,,
@@ -1,38 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: notify-tls-client
3
- Version: 2.0.0
4
- Summary: Cliente HTTP avançado com TLS fingerprinting, rotação de proxies e recuperação automática para web scraping resiliente
5
- Author-email: Jeferson Albara <jeferson.albara@example.com>
6
- Maintainer-email: Jeferson Albara <jeferson.albara@example.com>
7
- License: MIT
8
- Project-URL: Homepage, https://github.com/jefersonAlbara/notify-tls-client
9
- Project-URL: Documentation, https://github.com/jefersonAlbara/notify-tls-client#readme
10
- Project-URL: Repository, https://github.com/jefersonAlbara/notify-tls-client
11
- Project-URL: Issues, https://github.com/jefersonAlbara/notify-tls-client/issues
12
- Project-URL: Changelog, https://github.com/jefersonAlbara/notify-tls-client/blob/main/CHANGELOG.md
13
- Keywords: tls-client,http-client,web-scraping,proxy-rotation,tls-fingerprinting,browser-emulation,http2,http3,requests,automation
14
- Classifier: Development Status :: 4 - Beta
15
- Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: MIT License
17
- Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Topic :: Internet :: WWW/HTTP
21
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
- Classifier: Topic :: Internet :: Proxy Servers
23
- Classifier: Operating System :: OS Independent
24
- Classifier: Typing :: Typed
25
- Requires-Python: >=3.12
26
- Description-Content-Type: text/markdown
27
- Requires-Dist: dataclasses-json>=0.6.0
28
- Requires-Dist: typing-extensions>=4.8.0
29
- Requires-Dist: orjson>=3.9.0
30
- Provides-Extra: dev
31
- Requires-Dist: pytest>=7.4.0; extra == "dev"
32
- Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
33
- Requires-Dist: black>=23.0.0; extra == "dev"
34
- Requires-Dist: mypy>=1.5.0; extra == "dev"
35
- Requires-Dist: ruff>=0.1.0; extra == "dev"
36
-
37
- # MY TLS Client
38
- Pacote python privado com modificações na classe 'tls client'