Skip to content

Commit b7d006f

Browse files
Merge pull request #48 from thewebscraping/feat/optimize-logging-cookies
feat: optimize logging cookies
2 parents d8d98c5 + b9e1b3f commit b7d006f

File tree

9 files changed

+127
-29
lines changed

9 files changed

+127
-29
lines changed

CHANGELOG.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
Release History
2-
===============
2+
================
3+
4+
1.1.7 (2025-11-23)
5+
------------------
6+
**Improvements:**
7+
8+
- Optimized logging.
9+
- Fixed cookie response handling.
10+
11+
1.1.6 (2025-10-14)
12+
------------------
13+
**Enhancements:**
14+
This pull request introduces two major enhancements that significantly improve the library's anti‑detection capabilities and overall robustness:
15+
16+
**A Smart Rotator System**
17+
- Automatically rotates proxies, headers, and TLS identifiers to mimic authentic traffic.
18+
- Introduced three new rotator classes: `ProxyRotator`, `HeaderRotator`, and `TLSIdentifierRotator`.
19+
- Client and AsyncClient now enable header and TLS identifier rotation by default, using built‑in realistic templates.
20+
- Unified parameters accept a single value, a list, or a pre‑configured Rotator instance.
21+
- Proxy feedback loop (`mark_result`/`amark_result`) optimizes weighted rotation strategy.
22+
23+
**Robust Library Management**
24+
- Dependency‑free, self‑managing mechanism for the core `tls-client` C library lifecycle.
25+
- Removed `requests` and `tqdm`; now uses built‑in `urllib` and [json](cci:1://file:///Users/twofarm/Desktop/works/tls_requests/tls_requests/models/response.py:204:4-205:43).
26+
- TLSLibrary is version‑aware, automatically downloading the correct version from GitHub when needed.
27+
- Automatic cleanup of old library files after successful updates.
328

429
1.0.7 (2024-12-14)
530
-------------------

docs/index.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
**A powerful and lightweight Python library for making secure and reliable HTTP/TLS fingerprint requests.**
33

44
* * *
5+
## Table of Contents
6+
7+
- [Installation](#installation)
8+
- [Quick Start](#quick-start)
9+
- [Key Benefits](#key-benefits)
10+
- [Cookie Management](#cookie-management)
11+
- [Documentation](#documentation)
512

613
**Installation**
714
----------------
@@ -20,6 +27,17 @@ pip install wrapper-tls-requests
2027
pip install git+https://github.com/thewebscraping/tls-requests.git
2128
```
2229

30+
> **Note**: After installation you can update the TLS library manually using:
31+
> ```bash
32+
> python -m tls_requests.models.libraries
33+
> ```
34+
>
35+
> **Logging**: The library now uses the standard `logging` module. Configure it in your application, e.g.:
36+
> ```python
37+
> import logging
38+
> logging.basicConfig(level=logging.INFO)
39+
> ```
40+
2341
### Quick Start
2442
2543
Start using TLS Requests with just a few lines of code:

docs/quickstart.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ Begin by importing the library:
1111

1212
```pycon
1313
>>> import tls_requests
14+
>>> import logging
15+
>>> logging.basicConfig(level=logging.INFO)
1416
```
1517

1618
Making HTTP Requests
@@ -24,6 +26,7 @@ Fetch a webpage using a GET request:
2426
>>> r = tls_requests.get('https://httpbin.org/get')
2527
>>> r
2628
<Response [200 OK]>
29+
>>> # Cookies now have proper domain backfilled from request URL
2730
```
2831

2932
### POST Request

mkdocs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,5 @@ markdown_extensions:
4343
css_class: highlight
4444
- mkautodoc
4545

46-
extra_css:
47-
- css/custom.css
46+
# extra_css: # custom CSS removed because file is missing
47+
# - css/custom.css

tls_requests/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
__url__ = "https://github.com/thewebscraping/tls-requests"
66
__author__ = "Tu Pham"
77
__author_email__ = "thetwofarm@gmail.com"
8-
__version__ = "1.1.6"
8+
__version__ = "1.1.7"
99
__license__ = "MIT"

tls_requests/models/cookies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ def set(self, name, value, **kwargs) -> Optional[Cookie]:
561561
self.cookiejar.set_cookie(cookie)
562562
return cookie
563563

564-
def get(self, name, default=None, domain="", path="/") -> str:
564+
def get(self, name, default=None, domain=None, path=None) -> str:
565565
return self.cookiejar.get(name, default, domain, path)
566566

567567
def delete(self, name: str, domain: str = None, path: str = None) -> None:

tls_requests/models/libraries.py

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
from platform import machine
1313
from typing import List, Optional, Tuple
1414

15+
from tls_requests.utils import get_logger
16+
17+
logger = get_logger("TLSLibrary")
18+
1519
__all__ = ["TLSLibrary"]
1620

1721
LATEST_VERSION_TAG_NAME = "v1.11.2"
@@ -60,7 +64,6 @@
6064

6165
PATTERN_RE = re.compile(r"%s-%s.*%s" % (PLATFORM, MACHINE, FILE_EXT), re.I)
6266
PATTERN_UBUNTU_RE = re.compile(r"%s-%s.*%s" % ("ubuntu", MACHINE, FILE_EXT), re.I)
63-
6467
TLS_LIBRARY_PATH = os.getenv("TLS_LIBRARY_PATH")
6568

6669

@@ -131,6 +134,7 @@ class TLSLibrary:
131134
"""
132135

133136
_PATH: str = None
137+
_LIBRARY: Optional[ctypes.CDLL] = None
134138
_STATIC_API_DATA = {
135139
"name": "v1.11.2",
136140
"tag_name": "v1.11.2",
@@ -250,9 +254,9 @@ def cleanup_files(cls, keep_file: str = None):
250254
if is_remove:
251255
try:
252256
os.remove(file_path)
253-
print(f"Removed old library file: {file_path}")
257+
logger.info(f"Removed old library file: {file_path}")
254258
except OSError as e:
255-
print(f"Error removing old library file {file_path}: {e}")
259+
logger.error(f"Error removing old library file {file_path}: {e}")
256260

257261
@classmethod
258262
def fetch_api(cls, version: str = None, retries: int = 3):
@@ -280,7 +284,7 @@ def _find_release(data, version_: str = None):
280284
_find_release(json.loads(content))
281285
break
282286
except Exception as ex:
283-
print("Unable to fetch GitHub API: %s" % ex)
287+
logger.error("Unable to fetch GitHub API: %s" % ex)
284288

285289
if not asset_urls and not ubuntu_urls:
286290
_find_release([cls._STATIC_API_DATA])
@@ -302,10 +306,23 @@ def find(cls) -> str:
302306
def find_all(cls) -> List[str]:
303307
return [src for src in glob.glob(os.path.join(BIN_DIR, r"*")) if src.lower().endswith(("so", "dll", "dylib"))]
304308

309+
@classmethod
310+
def update(cls):
311+
"""Forces a download of the latest library version."""
312+
logger.info(f"Updating TLS library to version {LATEST_VERSION_TAG_NAME}...")
313+
downloaded_fp = cls.download(version=LATEST_VERSION_TAG_NAME)
314+
if downloaded_fp:
315+
cls.cleanup_files(keep_file=downloaded_fp)
316+
logger.info("Update complete.")
317+
return downloaded_fp
318+
logger.error("Update failed.")
319+
320+
upgrade = update
321+
305322
@classmethod
306323
def download(cls, version: str = None) -> str:
307324
try:
308-
print(
325+
logger.info(
309326
"System Info - Platform: %s, Machine: %s, File Ext : %s."
310327
% (
311328
PLATFORM,
@@ -319,7 +336,7 @@ def download(cls, version: str = None) -> str:
319336
download_url = url
320337
break
321338

322-
print("Library Download URL: %s" % download_url)
339+
logger.info("Library Download URL: %s" % download_url)
323340
if download_url:
324341
destination_name = download_url.split("/")[-1]
325342
destination = os.path.join(BIN_DIR, destination_name)
@@ -352,13 +369,12 @@ def download(cls, version: str = None) -> str:
352369
sys.stdout.write(f"\rDownloading {destination_name}: [{bar}] {percent:.1f}%")
353370
sys.stdout.flush()
354371

355-
print() # Newline after download completes
356372
return destination
357373

358374
except (urllib.error.URLError, urllib.error.HTTPError) as ex:
359-
print("Unable to download file: %s" % ex)
375+
logger.error("Unable to download file: %s" % ex)
360376
except Exception as e:
361-
print("An unexpected error occurred during download: %s" % e)
377+
logger.error("An unexpected error occurred during download: %s" % e)
362378

363379
@classmethod
364380
def set_path(cls, fp: str):
@@ -370,22 +386,32 @@ def load(cls):
370386
Loads the TLS library. It checks for the correct version, downloads it if
371387
the local version is outdated or missing, and then loads it into memory.
372388
"""
389+
target_version = cls._parse_version(LATEST_VERSION_TAG_NAME)
390+
391+
if cls._LIBRARY and cls._PATH:
392+
cached_version = cls._parse_version_from_filename(cls._PATH)
393+
if cached_version == target_version:
394+
return cls._LIBRARY
373395

374396
def _load_library(fp_):
375397
try:
376398
lib = ctypes.cdll.LoadLibrary(fp_)
377399
cls.set_path(fp_)
378-
print(f"Successfully loaded TLS library: {fp_}")
400+
cls._LIBRARY = lib
401+
logger.info(f"Successfully loaded TLS library: {fp_}")
379402
return lib
380403
except Exception as ex:
381-
print(f"Unable to load TLS library '{fp_}', details: {ex}")
404+
logger.error(f"Unable to load TLS library '{fp_}', details: {ex}")
382405
try:
383406
os.remove(fp_)
384407
except (FileNotFoundError, PermissionError):
385408
pass
386409

387-
target_version = cls._parse_version(LATEST_VERSION_TAG_NAME)
388-
print(f"Required library version: {LATEST_VERSION_TAG_NAME}")
410+
if TLS_LIBRARY_PATH:
411+
logger.info(f"Loading TLS library from environment variable: {TLS_LIBRARY_PATH}")
412+
return _load_library(TLS_LIBRARY_PATH)
413+
414+
logger.debug(f"Required library version: {LATEST_VERSION_TAG_NAME}")
389415
local_files = cls.find_all()
390416
newest_local_version = (0, 0, 0)
391417
newest_local_file = None
@@ -396,24 +422,33 @@ def _load_library(fp_):
396422
if file_version > newest_local_version:
397423
newest_local_version = file_version
398424
newest_local_file = file_path
399-
print(
425+
logger.debug(
400426
f"Found newest local library: {newest_local_file} (version {'.'.join(map(str, newest_local_version))})"
401427
)
402428
else:
403-
print("No local library found.")
429+
logger.debug("No local library found.")
404430

405431
if newest_local_version < target_version:
406432
if newest_local_file:
407-
print(f"Local library is outdated. Upgrading to {LATEST_VERSION_TAG_NAME}...")
433+
logger.warning(
434+
f"Local library is outdated (Found: {'.'.join(map(str, newest_local_version))}, "
435+
f"Required: {LATEST_VERSION_TAG_NAME}). "
436+
f"Auto-downloading... To manually upgrade, run: `python -m tls_requests.models.libraries`"
437+
)
408438
else:
409-
print(f"Downloading required library version {LATEST_VERSION_TAG_NAME}...")
439+
logger.info(f"Downloading required library version {LATEST_VERSION_TAG_NAME}...")
410440

411441
downloaded_fp = cls.download(version=LATEST_VERSION_TAG_NAME)
412442
if downloaded_fp:
413443
cls.cleanup_files(keep_file=downloaded_fp)
414444
library = _load_library(downloaded_fp)
415445
if library:
416446
return library
447+
448+
logger.error(
449+
f"Failed to download the required TLS library {LATEST_VERSION_TAG_NAME}. "
450+
"Please check your connection or download it manually from GitHub."
451+
)
417452
raise OSError("Failed to download the required TLS library.")
418453

419454
if newest_local_file:
@@ -423,3 +458,7 @@ def _load_library(fp_):
423458
return library
424459

425460
raise OSError("Could not find or load a compatible TLS library.")
461+
462+
463+
if __name__ == "__main__":
464+
TLSLibrary.update()

tls_requests/models/response.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,14 @@ def http_version(self) -> str:
9898

9999
@property
100100
def cookies(self) -> Cookies:
101-
if self._cookies is None:
102-
self._cookies = Cookies()
103-
self._cookies.extract_cookies(self, self.request)
101+
if self._request:
102+
# Fix missing domain in cookies by backfilling from request URL
103+
# Ref: https://github.com/thewebscraping/tls-requests/issues/47
104+
for cookie in self._cookies.cookiejar:
105+
if not cookie.domain:
106+
cookie.domain = self._request.url.host
107+
cookie.domain_specified = False
108+
cookie.domain_initial_dot = False
104109
return self._cookies
105110

106111
@property

tls_requests/utils.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import logging
66
from typing import Any, AnyStr, Union
77

8-
FORMAT = "%(levelname)s:%(asctime)s:%(name)s:%(funcName)s:%(lineno)d >>> %(message)s"
9-
DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
8+
FORMAT = "[%(asctime)s] %(levelname)-8s %(name)s:%(funcName)s:%(lineno)d - %(message)s"
9+
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
1010

1111

1212
def import_module(name: Union[str, list[str]]):
@@ -30,10 +30,18 @@ def import_module(name: Union[str, list[str]]):
3030
jsonlib = json
3131

3232

33-
def get_logger(name: str = "TLSRequests", level: int | str = logging.INFO) -> logging.Logger:
34-
logging.basicConfig(format=FORMAT, datefmt=DATE_FORMAT, level=level)
33+
def get_logger(
34+
name: str = "TLSRequests", level: int | str = logging.INFO
35+
) -> logging.Logger:
3536
logger = logging.getLogger(name)
3637
logger.setLevel(level)
38+
39+
if not logger.handlers:
40+
handler = logging.StreamHandler()
41+
formatter = logging.Formatter(FORMAT, datefmt=DATE_FORMAT)
42+
handler.setFormatter(formatter)
43+
logger.addHandler(handler)
44+
3745
return logger
3846

3947

0 commit comments

Comments
 (0)