never-primp 1.0.0__cp38-abi3-win_amd64.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.

Potentially problematic release.


This version of never-primp might be problematic. Click here for more details.

@@ -0,0 +1,269 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import sys
5
+ from functools import partial
6
+ from typing import TYPE_CHECKING, TypedDict
7
+
8
+ if sys.version_info <= (3, 11):
9
+ from typing_extensions import Unpack
10
+ else:
11
+ from typing import Unpack
12
+
13
+
14
+ from .never_primp import RClient
15
+
16
+ if TYPE_CHECKING:
17
+ from .never_primp import IMPERSONATE, IMPERSONATE_OS, ClientRequestParams, HttpMethod, RequestParams, Response
18
+ else:
19
+
20
+ class _Unpack:
21
+ @staticmethod
22
+ def __getitem__(*args, **kwargs):
23
+ pass
24
+
25
+ Unpack = _Unpack()
26
+ RequestParams = ClientRequestParams = TypedDict
27
+
28
+
29
+ class Client(RClient):
30
+ """Initializes an HTTP client that can impersonate web browsers."""
31
+
32
+ def __init__(
33
+ self,
34
+ auth: tuple[str, str | None] | None = None,
35
+ auth_bearer: str | None = None,
36
+ params: dict[str, str] | None = None,
37
+ headers: dict[str, str] | None = None,
38
+ cookie_store: bool | None = True,
39
+ referer: bool | None = True,
40
+ proxy: str | None = None,
41
+ timeout: float | None = 30,
42
+ impersonate: IMPERSONATE | None = None,
43
+ impersonate_os: IMPERSONATE_OS | None = None,
44
+ follow_redirects: bool | None = True,
45
+ max_redirects: int | None = 20,
46
+ verify: bool | None = True,
47
+ ca_cert_file: str | None = None,
48
+ https_only: bool | None = False,
49
+ http2_only: bool | None = False,
50
+ # Performance optimization parameters
51
+ pool_idle_timeout: float | None = None,
52
+ pool_max_idle_per_host: int | None = None,
53
+ tcp_nodelay: bool | None = None,
54
+ tcp_keepalive: float | None = None,
55
+ # Retry mechanism
56
+ retry_count: int | None = None,
57
+ retry_backoff: float | None = None,
58
+ ):
59
+ """
60
+ Args:
61
+ auth: a tuple containing the username and an optional password for basic authentication. Default is None.
62
+ auth_bearer: a string representing the bearer token for bearer token authentication. Default is None.
63
+ params: a map of query parameters to append to the URL. Default is None.
64
+ headers: an optional map of HTTP headers to send with requests. Ignored if `impersonate` is set.
65
+ cookie_store: enable a persistent cookie store. Received cookies will be preserved and included
66
+ in additional requests. Default is True.
67
+ referer: automatic setting of the `Referer` header. Default is True.
68
+ proxy: proxy URL for HTTP requests, example: "socks5://127.0.0.1:9150". Default is None.
69
+ timeout: timeout for HTTP requests in seconds. Default is 30.
70
+ impersonate: impersonate browser. Supported browsers:
71
+ "chrome_100", "chrome_101", "chrome_104", "chrome_105", "chrome_106",
72
+ "chrome_107", "chrome_108", "chrome_109", "chrome_114", "chrome_116",
73
+ "chrome_117", "chrome_118", "chrome_119", "chrome_120", "chrome_123",
74
+ "chrome_124", "chrome_126", "chrome_127", "chrome_128", "chrome_129",
75
+ "chrome_130", "chrome_131", "chrome_133"
76
+ "safari_15.3", "safari_15.5", "safari_15.6.1", "safari_16",
77
+ "safari_16.5", "safari_17.0", "safari_17.2.1", "safari_17.4.1",
78
+ "safari_17.5", "safari_18", "safari_18.2",
79
+ "safari_ios_16.5", "safari_ios_17.2", "safari_ios_17.4.1", "safari_ios_18.1.1",
80
+ "safari_ipad_18",
81
+ "okhttp_3.9", "okhttp_3.11", "okhttp_3.13", "okhttp_3.14", "okhttp_4.9",
82
+ "okhttp_4.10", "okhttp_5",
83
+ "edge_101", "edge_122", "edge_127", "edge_131",
84
+ "firefox_109", "firefox_117", "firefox_128", "firefox_133", "firefox_135".
85
+ Default is None.
86
+ impersonate_os: impersonate OS. Supported OS:
87
+ "android", "ios", "linux", "macos", "windows". Default is None.
88
+ follow_redirects: a boolean to enable or disable following redirects. Default is True.
89
+ max_redirects: the maximum number of redirects if `follow_redirects` is True. Default is 20.
90
+ verify: an optional boolean indicating whether to verify SSL certificates. Default is True.
91
+ ca_cert_file: path to CA certificate store. Default is None.
92
+ https_only: restrict the Client to be used with HTTPS only requests. Default is False.
93
+ http2_only: if true - use only HTTP/2, if false - use only HTTP/1. Default is False.
94
+ """
95
+ super().__init__()
96
+
97
+ def __enter__(self) -> Client:
98
+ return self
99
+
100
+ def __exit__(self, *args):
101
+ del self
102
+
103
+ def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response:
104
+ return super().request(method=method, url=url, **kwargs)
105
+
106
+ def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
107
+ return self.request(method="GET", url=url, **kwargs)
108
+
109
+ def head(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
110
+ return self.request(method="HEAD", url=url, **kwargs)
111
+
112
+ def options(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
113
+ return self.request(method="OPTIONS", url=url, **kwargs)
114
+
115
+ def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
116
+ return self.request(method="DELETE", url=url, **kwargs)
117
+
118
+ def post(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
119
+ return self.request(method="POST", url=url, **kwargs)
120
+
121
+ def put(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
122
+ return self.request(method="PUT", url=url, **kwargs)
123
+
124
+ def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
125
+ return self.request(method="PATCH", url=url, **kwargs)
126
+
127
+
128
+ class AsyncClient(Client):
129
+ def __init__(self,
130
+ auth: tuple[str, str | None] | None = None,
131
+ auth_bearer: str | None = None,
132
+ params: dict[str, str] | None = None,
133
+ headers: dict[str, str] | None = None,
134
+ cookie_store: bool | None = True,
135
+ referer: bool | None = True,
136
+ proxy: str | None = None,
137
+ timeout: float | None = 30,
138
+ impersonate: IMPERSONATE | None = None,
139
+ impersonate_os: IMPERSONATE_OS | None = None,
140
+ follow_redirects: bool | None = True,
141
+ max_redirects: int | None = 20,
142
+ verify: bool | None = True,
143
+ ca_cert_file: str | None = None,
144
+ https_only: bool | None = False,
145
+ http2_only: bool | None = False,
146
+ # Performance optimization parameters
147
+ pool_idle_timeout: float | None = None,
148
+ pool_max_idle_per_host: int | None = None,
149
+ tcp_nodelay: bool | None = None,
150
+ tcp_keepalive: float | None = None,
151
+ # Retry mechanism
152
+ retry_count: int | None = None,
153
+ retry_backoff: float | None = None):
154
+ super().__init__()
155
+
156
+ async def __aenter__(self) -> AsyncClient:
157
+ return self
158
+
159
+ async def __aexit__(self, *args):
160
+ del self
161
+
162
+ async def _run_sync_asyncio(self, fn, *args, **kwargs):
163
+ loop = asyncio.get_running_loop()
164
+ return await loop.run_in_executor(None, partial(fn, *args, **kwargs))
165
+
166
+ async def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
167
+ return await self._run_sync_asyncio(super().request, method=method, url=url, **kwargs)
168
+
169
+ async def get(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
170
+ return await self.request(method="GET", url=url, **kwargs)
171
+
172
+ async def head(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
173
+ return await self.request(method="HEAD", url=url, **kwargs)
174
+
175
+ async def options(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
176
+ return await self.request(method="OPTIONS", url=url, **kwargs)
177
+
178
+ async def delete(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
179
+ return await self.request(method="DELETE", url=url, **kwargs)
180
+
181
+ async def post(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
182
+ return await self.request(method="POST", url=url, **kwargs)
183
+
184
+ async def put(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
185
+ return await self.request(method="PUT", url=url, **kwargs)
186
+
187
+ async def patch(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
188
+ return await self.request(method="PATCH", url=url, **kwargs)
189
+
190
+
191
+ def request(
192
+ method: HttpMethod,
193
+ url: str,
194
+ impersonate: IMPERSONATE | None = None,
195
+ impersonate_os: IMPERSONATE_OS | None = None,
196
+ verify: bool | None = True,
197
+ ca_cert_file: str | None = None,
198
+ **kwargs: Unpack[RequestParams],
199
+ ):
200
+ """
201
+ Args:
202
+ method: the HTTP method to use (e.g., "GET", "POST").
203
+ url: the URL to which the request will be made.
204
+ impersonate: impersonate browser. Supported browsers:
205
+ "chrome_100", "chrome_101", "chrome_104", "chrome_105", "chrome_106",
206
+ "chrome_107", "chrome_108", "chrome_109", "chrome_114", "chrome_116",
207
+ "chrome_117", "chrome_118", "chrome_119", "chrome_120", "chrome_123",
208
+ "chrome_124", "chrome_126", "chrome_127", "chrome_128", "chrome_129",
209
+ "chrome_130", "chrome_131", "chrome_133",
210
+ "safari_15.3", "safari_15.5", "safari_15.6.1", "safari_16",
211
+ "safari_16.5", "safari_17.0", "safari_17.2.1", "safari_17.4.1",
212
+ "safari_17.5", "safari_18", "safari_18.2",
213
+ "safari_ios_16.5", "safari_ios_17.2", "safari_ios_17.4.1", "safari_ios_18.1.1",
214
+ "safari_ipad_18",
215
+ "okhttp_3.9", "okhttp_3.11", "okhttp_3.13", "okhttp_3.14", "okhttp_4.9",
216
+ "okhttp_4.10", "okhttp_5",
217
+ "edge_101", "edge_122", "edge_127", "edge_131",
218
+ "firefox_109", "firefox_117", "firefox_128", "firefox_133", "firefox_135".
219
+ Default is None.
220
+ impersonate_os: impersonate OS. Supported OS:
221
+ "android", "ios", "linux", "macos", "windows". Default is None.
222
+ verify: an optional boolean indicating whether to verify SSL certificates. Default is True.
223
+ ca_cert_file: path to CA certificate store. Default is None.
224
+ auth: a tuple containing the username and an optional password for basic authentication. Default is None.
225
+ auth_bearer: a string representing the bearer token for bearer token authentication. Default is None.
226
+ params: a map of query parameters to append to the URL. Default is None.
227
+ headers: an optional map of HTTP headers to send with requests. If `impersonate` is set, this will be ignored.
228
+ cookies: an optional map of cookies to send with requests as the `Cookie` header.
229
+ timeout: the timeout for the request in seconds. Default is 30.
230
+ content: the content to send in the request body as bytes. Default is None.
231
+ data: the form data to send in the request body. Default is None.
232
+ json: a JSON serializable object to send in the request body. Default is None.
233
+ files: a map of file fields to file paths to be sent as multipart/form-data. Default is None.
234
+ """
235
+ with Client(
236
+ impersonate=impersonate,
237
+ impersonate_os=impersonate_os,
238
+ verify=verify,
239
+ ca_cert_file=ca_cert_file,
240
+ ) as client:
241
+ return client.request(method, url, **kwargs)
242
+
243
+
244
+ def get(url: str, **kwargs: Unpack[ClientRequestParams]):
245
+ return request(method="GET", url=url, **kwargs)
246
+
247
+
248
+ def head(url: str, **kwargs: Unpack[ClientRequestParams]):
249
+ return request(method="HEAD", url=url, **kwargs)
250
+
251
+
252
+ def options(url: str, **kwargs: Unpack[ClientRequestParams]):
253
+ return request(method="OPTIONS", url=url, **kwargs)
254
+
255
+
256
+ def delete(url: str, **kwargs: Unpack[ClientRequestParams]):
257
+ return request(method="DELETE", url=url, **kwargs)
258
+
259
+
260
+ def post(url: str, **kwargs: Unpack[ClientRequestParams]):
261
+ return request(method="POST", url=url, **kwargs)
262
+
263
+
264
+ def put(url: str, **kwargs: Unpack[ClientRequestParams]):
265
+ return request(method="PUT", url=url, **kwargs)
266
+
267
+
268
+ def patch(url: str, **kwargs: Unpack[ClientRequestParams]):
269
+ return request(method="PATCH", url=url, **kwargs)
Binary file
@@ -0,0 +1,133 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from typing import Any, Iterator, Literal, TypedDict
5
+
6
+ if sys.version_info <= (3, 11):
7
+ from typing_extensions import Unpack
8
+ else:
9
+ from typing import Unpack
10
+
11
+ HttpMethod = Literal["GET", "HEAD", "OPTIONS", "DELETE", "POST", "PUT", "PATCH"]
12
+ IMPERSONATE = Literal[
13
+ "chrome_100", "chrome_101", "chrome_104", "chrome_105", "chrome_106",
14
+ "chrome_107", "chrome_108", "chrome_109", "chrome_114", "chrome_116",
15
+ "chrome_117", "chrome_118", "chrome_119", "chrome_120", "chrome_123",
16
+ "chrome_124", "chrome_126", "chrome_127", "chrome_128", "chrome_129",
17
+ "chrome_130", "chrome_131", "chrome_133", "chrome_134", "chrome_135",
18
+ "chrome_136", "chrome_137", "chrome_138", "chrome_139",
19
+ "chrome_140","chrome_141",
20
+ "safari_15.3", "safari_15.5", "safari_15.6.1", "safari_16",
21
+ "safari_16.5", "safari_17.0", "safari_17.2.1", "safari_17.4.1",
22
+ "safari_17.5", "safari_18", "safari_18.2","safari_18.3","safari_18.3.1","safari_18.5",
23
+ "safari_ios_16.5", "safari_ios_17.2", "safari_ios_17.4.1", "safari_ios_18.1.1",
24
+ "safari_ipad_18","safari_26","safari_ipad_26","safari_ios_26",
25
+ "okhttp_3.9", "okhttp_3.11", "okhttp_3.13", "okhttp_3.14", "okhttp_4.9",
26
+ "okhttp_4.10", "okhttp_4.12","okhttp_5",
27
+ "edge_101", "edge_122", "edge_127", "edge_131","edge_134",
28
+ "opera_116", "opera_117", "opera_118", "opera_119",
29
+ "firefox_109", "firefox_117", "firefox_128", "firefox_133", "firefox_135",
30
+ "firefox_136", "firefox_139", "firefox_142", "firefox_143", "firefox_android_135",
31
+ "firefox_private_135", "firefox_private_136",
32
+ "random",
33
+ ] # fmt: skip
34
+ IMPERSONATE_OS = Literal["android", "ios", "linux", "macos", "windows", "random"]
35
+
36
+ class RequestParams(TypedDict, total=False):
37
+ auth: tuple[str, str | None] | None
38
+ auth_bearer: str | None
39
+ params: dict[str, str] | None
40
+ headers: dict[str, str] | None
41
+ cookies: dict[str, str] | None
42
+ timeout: float | None
43
+ content: bytes | None
44
+ data: dict[str, Any] | None
45
+ json: Any | None
46
+ files: dict[str, str] | None
47
+
48
+ class ClientRequestParams(RequestParams):
49
+ impersonate: IMPERSONATE | None
50
+ impersonate_os: IMPERSONATE_OS | None
51
+ verify: bool | None
52
+ ca_cert_file: str | None
53
+
54
+ class Response:
55
+ @property
56
+ def content(self) -> bytes: ...
57
+ @property
58
+ def cookies(self) -> dict[str, str]: ...
59
+ @property
60
+ def headers(self) -> dict[str, str]: ...
61
+ @property
62
+ def status_code(self) -> int: ...
63
+ @property
64
+ def url(self) -> str: ...
65
+ @property
66
+ def encoding(self) -> str: ...
67
+ @property
68
+ def text(self) -> str: ...
69
+ def json(self) -> Any: ...
70
+ def stream(self) -> Iterator[bytes]: ...
71
+ @property
72
+ def text_markdown(self) -> str: ...
73
+ @property
74
+ def text_plain(self) -> str: ...
75
+ @property
76
+ def text_rich(self) -> str: ...
77
+
78
+ class RClient:
79
+ def __init__(
80
+ self,
81
+ auth: tuple[str, str | None] | None = None,
82
+ auth_bearer: str | None = None,
83
+ params: dict[str, str] | None = None,
84
+ headers: dict[str, str] | None = None,
85
+ timeout: float | None = None,
86
+ cookie_store: bool | None = True,
87
+ referer: bool | None = True,
88
+ proxy: str | None = None,
89
+ impersonate: IMPERSONATE | None = None,
90
+ impersonate_os: IMPERSONATE_OS | None = None,
91
+ follow_redirects: bool | None = True,
92
+ max_redirects: int | None = 20,
93
+ verify: bool | None = True,
94
+ ca_cert_file: str | None = None,
95
+ https_only: bool | None = False,
96
+ http2_only: bool | None = False,
97
+ ): ...
98
+ @property
99
+ def headers(self) -> dict[str, str]: ...
100
+ @headers.setter
101
+ def headers(self, headers: dict[str, str]) -> None: ...
102
+ def headers_update(self, headers: dict[str, str]) -> None: ...
103
+ def get_cookies(self, url: str) -> dict[str, str]: ...
104
+ def set_cookies(self, url: str, cookies: dict[str, str]) -> None: ...
105
+ @property
106
+ def proxy(self) -> str | None: ...
107
+ @proxy.setter
108
+ def proxy(self, proxy: str) -> None: ...
109
+ @property
110
+ def impersonate(self) -> str | None: ...
111
+ @impersonate.setter
112
+ def impersonate(self, impersonate: IMPERSONATE) -> None: ...
113
+ @property
114
+ def impersonate_os(self) -> str | None: ...
115
+ @impersonate_os.setter
116
+ def impersonate_os(self, impersonate: IMPERSONATE_OS) -> None: ...
117
+ def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
118
+ def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
119
+ def head(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
120
+ def options(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
121
+ def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
122
+ def post(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
123
+ def put(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
124
+ def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
125
+
126
+ def request(method: HttpMethod, url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
127
+ def get(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
128
+ def head(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
129
+ def options(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
130
+ def delete(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
131
+ def post(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
132
+ def put(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
133
+ def patch(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
never_primp/py.typed ADDED
File without changes
@@ -0,0 +1,338 @@
1
+ Metadata-Version: 2.4
2
+ Name: never_primp
3
+ Version: 1.0.0
4
+ Summary: 基于原primp 重新优化调整的请求库
5
+ Author: Neverland
6
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
7
+
8
+ ![Python >= 3.8](https://img.shields.io/badge/python->=3.8-red.svg) [![](https://badgen.net/github/release/deedy5/pyreqwest-impersonate)](https://github.com/deedy5/pyreqwest-impersonate/releases) [![](https://badge.fury.io/py/primp.svg)](https://pypi.org/project/primp) [![Downloads](https://static.pepy.tech/badge/primp/week)](https://pepy.tech/project/primp) [![CI](https://github.com/deedy5/pyreqwest-impersonate/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/deedy5/pyreqwest-impersonate/actions/workflows/CI.yml)
9
+ # 🪞NEVER_PRIMP
10
+ **🪞NEVER_PRIMP** = **P**ython **R**equests **IMP**ersonate
11
+
12
+ The fastest python HTTP client that can impersonate web browsers.</br>
13
+ Provides precompiled wheels:</br>
14
+ * 🐧 linux: `amd64`, `aarch64`, `armv7` (⚠️aarch64 and armv7 builds are `manylinux_2_34` compatible - `ubuntu>=22.04`, `debian>=12`);</br>
15
+ * 🐧 musllinux: `amd64`, `aarch64`;</br>
16
+ * 🪟 windows: `amd64`;</br>
17
+ * 🍏 macos: `amd64`, `aarch64`.</br>
18
+
19
+ ## Table of Contents
20
+
21
+ - [Installation](#installation)
22
+ - [Key Features](#key-features)
23
+ - [Benchmark](#benchmark)
24
+ - [Usage](#usage)
25
+ - [I. Client](#i-client)
26
+ - [Client methods](#client-methods)
27
+ - [Response object](#response-object)
28
+ - [Devices](#devices)
29
+ - [Examples](#examples)
30
+ - [II. AsyncClient](#ii-asyncclient)
31
+ - [Disclaimer](#disclaimer)
32
+
33
+ ## Installation
34
+
35
+ ```python
36
+ pip install -U never_primp
37
+ ```
38
+
39
+ ## Key Features
40
+
41
+ 🚀 **Performance Optimized**
42
+ - Connection pooling with configurable idle timeout and max connections per host
43
+ - TCP optimization (TCP_NODELAY, TCP keepalive)
44
+ - ~59% faster for sequential requests with connection reuse
45
+
46
+ 🔒 **Advanced Certificate Management**
47
+ - Uses system's native certificate store (auto-updated with OS)
48
+ - No more certificate expiration issues
49
+ - Custom CA bundle support
50
+
51
+ 🍪 **Smart Cookie Management**
52
+ - Automatic cookie persistence using wreq's native Jar API
53
+ - Manual cookie control with `get_cookies()` / `set_cookies()`
54
+ - Cookies survive client configuration changes
55
+
56
+ ⚙️ **Dynamic Configuration**
57
+ - Modify headers, proxy, and impersonation at runtime
58
+ - No need to recreate the client
59
+ - Thread-safe configuration updates
60
+
61
+ 🔄 **Retry Mechanism**
62
+ - Configurable retry count and backoff timing
63
+ - Handle transient failures gracefully
64
+
65
+ 🎭 **Browser Impersonation**
66
+ - Impersonate Chrome, Safari, Edge, Firefox, OkHttp
67
+ - Mimic TLS/JA3/JA4/HTTP2 fingerprints
68
+ - Custom OS impersonation (Windows, macOS, Linux, Android, iOS)
69
+
70
+ ## Usage
71
+ ### I. Client
72
+
73
+ HTTP client that can impersonate web browsers.
74
+ ```python
75
+ class Client:
76
+ """Initializes an HTTP client that can impersonate web browsers.
77
+
78
+ Args:
79
+ auth (tuple[str, str| None] | None): Username and password for basic authentication. Default is None.
80
+ auth_bearer (str | None): Bearer token for authentication. Default is None.
81
+ params (dict[str, str] | None): Default query parameters to include in all requests. Default is None.
82
+ headers (dict[str, str] | None): Default headers to send with requests. If `impersonate` is set, this will be ignored.
83
+ timeout (float | None): HTTP request timeout in seconds. Default is 30.
84
+ cookie_store (bool | None): Enable a persistent cookie store. Received cookies will be preserved and included
85
+ in additional requests. Default is True.
86
+ referer (bool | None): Enable or disable automatic setting of the `Referer` header. Default is True.
87
+ proxy (str | None): Proxy URL for HTTP requests. Example: "socks5://127.0.0.1:9150". Default is None.
88
+ impersonate (str | None): Entity to impersonate. Example: "chrome_124". Default is None.
89
+ Chrome: "chrome_100","chrome_101","chrome_104","chrome_105","chrome_106","chrome_107","chrome_108",
90
+ "chrome_109","chrome_114","chrome_116","chrome_117","chrome_118","chrome_119","chrome_120",
91
+ "chrome_123","chrome_124","chrome_126","chrome_127","chrome_128","chrome_129","chrome_130",
92
+ "chrome_131","chrome_133"
93
+ Safari: "safari_ios_16.5","safari_ios_17.2","safari_ios_17.4.1","safari_ios_18.1.1",
94
+ "safari_15.3","safari_15.5","safari_15.6.1","safari_16","safari_16.5","safari_17.0",
95
+ "safari_17.2.1","safari_17.4.1","safari_17.5","safari_18","safari_18.2","safari_ipad_18"
96
+ OkHttp: "okhttp_3.9","okhttp_3.11","okhttp_3.13","okhttp_3.14","okhttp_4.9","okhttp_4.10","okhttp_5"
97
+ Edge: "edge_101","edge_122","edge_127","edge_131"
98
+ Firefox: "firefox_109","firefox_117","firefox_128","firefox_133","firefox_135"
99
+ Select random: "random"
100
+ impersonate_os (str | None): impersonate OS. Example: "windows". Default is "linux".
101
+ Android: "android", iOS: "ios", Linux: "linux", Mac OS: "macos", Windows: "windows"
102
+ Select random: "random"
103
+ follow_redirects (bool | None): Whether to follow redirects. Default is True.
104
+ max_redirects (int | None): Maximum redirects to follow. Default 20. Applies if `follow_redirects` is True.
105
+ verify (bool | None): Verify SSL certificates. Default is True.
106
+ ca_cert_file (str | None): Path to CA certificate store. Default is None.
107
+ https_only (bool | None): Restrict the Client to be used with HTTPS only requests. Default is False.
108
+ http2_only (bool | None): If true - use only HTTP/2; if false - use only HTTP/1. Default is False.
109
+ pool_idle_timeout (float | None): Connection pool idle timeout in seconds. Default is None.
110
+ pool_max_idle_per_host (int | None): Maximum number of idle connections per host. Default is None.
111
+ tcp_nodelay (bool | None): Enable TCP_NODELAY (disable Nagle's algorithm). Default is None.
112
+ tcp_keepalive (float | None): TCP keepalive interval in seconds. Default is None.
113
+ retry_count (int | None): Maximum number of retry attempts. Default is None.
114
+ retry_backoff (float | None): Backoff time between retries in seconds. Default is None.
115
+
116
+ """
117
+ ```
118
+
119
+ #### Client methods
120
+
121
+ The `Client` class provides a set of methods for making HTTP requests: `get`, `head`, `options`, `delete`, `post`, `put`, `patch`, each of which internally utilizes the `request()` method for execution. The parameters for these methods closely resemble those in `httpx`.
122
+ ```python
123
+ def get(
124
+ url: str,
125
+ params: dict[str, str] | None = None,
126
+ headers: dict[str, str] | None = None,
127
+ cookies: dict[str, str] | None = None,
128
+ auth: tuple[str, str| None] | None = None,
129
+ auth_bearer: str | None = None,
130
+ timeout: float | None = 30,
131
+ ):
132
+ """Performs a GET request to the specified URL.
133
+
134
+ Args:
135
+ url (str): The URL to which the request will be made.
136
+ params (dict[str, str] | None): A map of query parameters to append to the URL. Default is None.
137
+ headers (dict[str, str] | None): A map of HTTP headers to send with the request. Default is None.
138
+ cookies (dict[str, str] | None): - An optional map of cookies to send with requests as the `Cookie` header.
139
+ auth (tuple[str, str| None] | None): A tuple containing the username and an optional password
140
+ for basic authentication. Default is None.
141
+ auth_bearer (str | None): A string representing the bearer token for bearer token authentication. Default is None.
142
+ timeout (float | None): The timeout for the request in seconds. Default is 30.
143
+
144
+ """
145
+ ```
146
+ ```python
147
+ def post(
148
+ url: str,
149
+ params: dict[str, str] | None = None,
150
+ headers: dict[str, str] | None = None,
151
+ cookies: dict[str, str] | None = None,
152
+ content: bytes | None = None,
153
+ data: dict[str, Any] | None = None,
154
+ json: Any | None = None,
155
+ files: dict[str, str] | None = None,
156
+ auth: tuple[str, str| None] | None = None,
157
+ auth_bearer: str | None = None,
158
+ timeout: float | None = 30,
159
+ ):
160
+ """Performs a POST request to the specified URL.
161
+
162
+ Args:
163
+ url (str): The URL to which the request will be made.
164
+ params (dict[str, str] | None): A map of query parameters to append to the URL. Default is None.
165
+ headers (dict[str, str] | None): A map of HTTP headers to send with the request. Default is None.
166
+ cookies (dict[str, str] | None): - An optional map of cookies to send with requests as the `Cookie` header.
167
+ content (bytes | None): The content to send in the request body as bytes. Default is None.
168
+ data (dict[str, Any] | None): The form data to send in the request body. Default is None.
169
+ json (Any | None): A JSON serializable object to send in the request body. Default is None.
170
+ files (dict[str, str] | None): A map of file fields to file paths to be sent as multipart/form-data. Default is None.
171
+ auth (tuple[str, str| None] | None): A tuple containing the username and an optional password
172
+ for basic authentication. Default is None.
173
+ auth_bearer (str | None): A string representing the bearer token for bearer token authentication. Default is None.
174
+ timeout (float | None): The timeout for the request in seconds. Default is 30.
175
+
176
+ """
177
+ ```
178
+ #### Response object
179
+ ```python
180
+ resp.content
181
+ resp.stream() # stream the response body in chunks of bytes
182
+ resp.cookies
183
+ resp.encoding
184
+ resp.headers
185
+ resp.json()
186
+ resp.status_code
187
+ resp.text
188
+ resp.text_markdown # html is converted to markdown text
189
+ resp.text_plain # html is converted to plain text
190
+ resp.text_rich # html is converted to rich text
191
+ resp.url
192
+ ```
193
+
194
+ #### Devices
195
+
196
+ ##### Impersonate
197
+
198
+ - Chrome: `chrome_100`,`chrome_101`,`chrome_104`,`chrome_105`,`chrome_106`,`chrome_107`,`chrome_108`,`chrome_109`,`chrome_114`,`chrome_116`,`chrome_117`,`chrome_118`,`chrome_119`,`chrome_120`,`chrome_123`,`chrome_124`,`chrome_126`,`chrome_127`,`chrome_128`,`chrome_129`,`chrome_130`,`chrome_131`, `chrome_133`, `chrome_134`, `chrome_135`, `chrome_136`, `chrome_137`, `chrome_138`, `chrome_139`, `chrome_140`, `chrome_141`
199
+
200
+ - Edge: `edge_101`,`edge_122`,`edge_127`, `edge_131`, `edge_134`
201
+
202
+ - Safari: `safari_ios_17.2`,`safari_ios_17.4.1`,`safari_ios_16.5`,`safari_ios_18.1.1`, `safari_15.3`,`safari_15.5`,`safari_15.6.1`,`safari_16`,`safari_16.5`,`safari_17.0`,`safari_17.2.1`,`safari_17.4.1`,`safari_17.5`,`safari_18`,`safari_18.2`, `safari_ipad_18`, `safari_ipad_26`, `safari_ios_26`, `safari_26`
203
+
204
+ - OkHttp: `okhttp_3.9`,`okhttp_3.11`,`okhttp_3.13`,`okhttp_3.14`,`okhttp_4.9`,`okhttp_4.10`,`okhttp_5`
205
+
206
+ - Firefox: `firefox_109`, `firefox_117`, `firefox_128`, `firefox_133`, `firefox_135`, `firefox_136`, `firefox_139`, `firefox_142`, `firefox_143`
207
+
208
+ ##### Impersonate OS
209
+
210
+ - `android`, `ios`, `linux`, `macos`, `windows`
211
+
212
+ #### Examples
213
+
214
+ ```python
215
+ import never_primp as primp
216
+
217
+ # Impersonate
218
+ client = primp.Client(impersonate="chrome_131", impersonate_os="windows")
219
+
220
+ # Performance optimization with connection pooling
221
+ client = primp.Client(
222
+ impersonate="chrome_131",
223
+ pool_idle_timeout=90.0, # Keep connections alive for 90 seconds
224
+ pool_max_idle_per_host=10, # Max 10 idle connections per host
225
+ tcp_nodelay=True, # Disable Nagle's algorithm for lower latency
226
+ tcp_keepalive=60.0, # TCP keepalive every 60 seconds
227
+ )
228
+
229
+ # Retry mechanism
230
+ client = primp.Client(
231
+ retry_count=3, # Retry up to 3 times
232
+ retry_backoff=1.0, # 1 second backoff between retries
233
+ )
234
+
235
+ # Dynamic configuration updates
236
+ client.headers = {"User-Agent": "Custom User Agent"} # Update all headers
237
+ client.headers_update({"Referer": "https://example.com"}) # Merge new headers
238
+ client.proxy = "http://127.0.0.1:8080" # Change proxy
239
+ client.impersonate = "chrome_133" # Change browser impersonation
240
+ client.impersonate_os = "macos" # Change OS impersonation
241
+
242
+ # GET request
243
+ resp = client.get("https://tls.peet.ws/api/all")
244
+
245
+ # GET request with passing params and setting timeout
246
+ params = {"param1": "value1", "param2": "value2"}
247
+ resp = client.post(url="https://httpbin.org/anything", params=params, timeout=10)
248
+
249
+ # Stream response
250
+ resp = client.get("https://nytimes")
251
+ for chunk in resp.stream():
252
+ print(chunk)
253
+
254
+ # Cookie management - Automatic (recommended)
255
+ client = primp.Client(cookie_store=True) # Default: enabled
256
+ resp = client.get("https://httpbin.org/cookies/set?session=abc123")
257
+ # Cookies are automatically stored and sent in subsequent requests
258
+ resp2 = client.get("https://httpbin.org/cookies") # Session cookie automatically included
259
+
260
+ # Cookie management - Manual
261
+ cookies = {"c1_n": "c1_value", "c2_n": "c2_value"}
262
+ client.set_cookies(url="https://nytimes.com", cookies=cookies) # Set cookies for a specific domain
263
+ resp = client.get("https://nytimes.com/", cookies=cookies) # Or pass cookies in request
264
+
265
+ # Get cookies
266
+ all_cookies = client.get_cookies(url="https://nytimes.com") # Get all cookies from jar
267
+ response_cookies = resp.cookies # Get cookies from response
268
+
269
+ # POST Binary Request Data
270
+ content = b"some_data"
271
+ resp = client.post(url="https://httpbin.org/anything", content=content)
272
+
273
+ # POST Form Encoded Data
274
+ data = {"key1": "value1", "key2": "value2"}
275
+ resp = client.post(url="https://httpbin.org/anything", data=data)
276
+
277
+ # POST JSON Encoded Data
278
+ json = {"key1": "value1", "key2": "value2"}
279
+ resp = client.post(url="https://httpbin.org/anything", json=json)
280
+
281
+ # POST Multipart-Encoded Files
282
+ files = {'file1': '/home/root/file1.txt', 'file2': 'home/root/file2.txt'}
283
+ resp = client.post("https://httpbin.org/post", files=files)
284
+
285
+ # Authentication using user/password
286
+ resp = client.post(url="https://httpbin.org/anything", auth=("user", "password"))
287
+
288
+ # Authentication using auth bearer
289
+ resp = client.post(url="https://httpbin.org/anything", auth_bearer="bearerXXXXXXXXXXXXXXXXXXXX")
290
+
291
+ # Using proxy or env var PRIMP_PROXY
292
+ resp = primp.Client(proxy="http://127.0.0.1:8080") # set proxy in Client
293
+ export PRIMP_PROXY="socks5://127.0.0.1:1080" # set proxy as environment variable
294
+
295
+ # SSL/TLS certificate verification
296
+ # Note: Primp uses your system's native certificate store by default (auto-updated with OS)
297
+ resp = primp.Client(verify=True) # Default: uses system certificates
298
+
299
+ # Using custom CA certificate store:
300
+ resp = primp.Client(ca_cert_file="/cert/cacert.pem")
301
+ resp = primp.Client(ca_cert_file=certifi.where())
302
+ export PRIMP_CA_BUNDLE="/home/user/Downloads/cert.pem" # set as environment variable
303
+
304
+ # You can also use convenience functions that use a default Client instance under the hood:
305
+ # primp.get() | primp.head() | primp.options() | primp.delete() | primp.post() | primp.patch() | primp.put()
306
+ # These functions can accept the `impersonate` parameter:
307
+ resp = primp.get("https://httpbin.org/anything", impersonate="chrome_131", impersonate_os="android")
308
+ ```
309
+
310
+ ### II. AsyncClient
311
+
312
+ `primp.AsyncClient()` is an asynchronous wrapper around the `primp.Client` class, offering the same functions, behavior, and input arguments.
313
+
314
+ ```python3
315
+ import asyncio
316
+ import logging
317
+
318
+ import never_primp as primp
319
+
320
+ async def aget_text(url):
321
+ async with primp.AsyncClient(impersonate="chrome_131") as client:
322
+ resp = await client.get(url)
323
+ return resp.text
324
+
325
+ async def main():
326
+ urls = ["https://nytimes.com/", "https://cnn.com/", "https://abcnews.go.com/"]
327
+ tasks = [aget_text(u) for u in urls]
328
+ results = await asyncio.gather(*tasks)
329
+
330
+ if __name__ == "__main__":
331
+ logging.basicConfig(level=logging.INFO)
332
+ asyncio.run(main())
333
+ ```
334
+
335
+ ## Disclaimer
336
+
337
+ This tool is for educational purposes only. Use it at your own risk.
338
+
@@ -0,0 +1,7 @@
1
+ never_primp-1.0.0.dist-info/METADATA,sha256=Cuiw8t_orxEPdw7hBh3bO5WN6uCZoeuKsgOwpJhHaUw,15657
2
+ never_primp-1.0.0.dist-info/WHEEL,sha256=CG8OzNtm0LMpJ2zhrjswlO8N-965OeMLklsQAG-nMvQ,94
3
+ never_primp/__init__.py,sha256=Y6Bs079kVXF4HWG2ZpmatSAirlcPCFZ4P0FyGG9ZvLo,12810
4
+ never_primp/never_primp.pyd,sha256=2bWHiIOmBgitjbGsgXNAq1YbBtPv8MSJFxzPvvKYETg,6435328
5
+ never_primp/never_primp.pyi,sha256=8QRuXaqNfk29qkFZrbtwKKK92Y_wL-O702nys8DCcM0,5731
6
+ never_primp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ never_primp-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.9.6)
3
+ Root-Is-Purelib: false
4
+ Tag: cp38-abi3-win_amd64