impit 0.2.1__cp313-cp313-musllinux_1_2_x86_64.whl → 0.9.0__cp313-cp313-musllinux_1_2_x86_64.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.
- impit/__init__.py +63 -0
- impit/cookies.py +177 -0
- impit/impit.cpython-313-x86_64-linux-musl.so +0 -0
- impit/impit.pyi +695 -13
- {impit-0.2.1.dist-info → impit-0.9.0.dist-info}/METADATA +11 -6
- impit-0.9.0.dist-info/RECORD +9 -0
- {impit-0.2.1.dist-info → impit-0.9.0.dist-info}/WHEEL +1 -1
- impit.libs/{libgcc_s-1e52349c.so.1 → libgcc_s-98a1ef30.so.1} +0 -0
- impit-0.2.1.dist-info/RECORD +0 -8
impit/__init__.py
CHANGED
|
@@ -1,9 +1,39 @@
|
|
|
1
|
+
from importlib import metadata
|
|
1
2
|
from typing import Literal
|
|
2
3
|
|
|
4
|
+
from .cookies import Cookies
|
|
3
5
|
from .impit import (
|
|
4
6
|
AsyncClient,
|
|
5
7
|
Client,
|
|
8
|
+
CloseError,
|
|
9
|
+
ConnectError,
|
|
10
|
+
ConnectTimeout,
|
|
11
|
+
CookieConflict,
|
|
12
|
+
DecodingError,
|
|
13
|
+
HTTPError,
|
|
14
|
+
HTTPStatusError,
|
|
15
|
+
InvalidURL,
|
|
16
|
+
LocalProtocolError,
|
|
17
|
+
NetworkError,
|
|
18
|
+
PoolTimeout,
|
|
19
|
+
ProtocolError,
|
|
20
|
+
ProxyError,
|
|
21
|
+
ReadError,
|
|
22
|
+
ReadTimeout,
|
|
23
|
+
RemoteProtocolError,
|
|
24
|
+
RequestError,
|
|
25
|
+
RequestNotRead,
|
|
6
26
|
Response,
|
|
27
|
+
ResponseNotRead,
|
|
28
|
+
StreamClosed,
|
|
29
|
+
StreamConsumed,
|
|
30
|
+
StreamError,
|
|
31
|
+
TimeoutException,
|
|
32
|
+
TooManyRedirects,
|
|
33
|
+
TransportError,
|
|
34
|
+
UnsupportedProtocol,
|
|
35
|
+
WriteError,
|
|
36
|
+
WriteTimeout,
|
|
7
37
|
delete,
|
|
8
38
|
get,
|
|
9
39
|
head,
|
|
@@ -11,14 +41,46 @@ from .impit import (
|
|
|
11
41
|
patch,
|
|
12
42
|
post,
|
|
13
43
|
put,
|
|
44
|
+
stream,
|
|
14
45
|
trace,
|
|
15
46
|
)
|
|
16
47
|
|
|
48
|
+
__version__ = metadata.version('impit')
|
|
49
|
+
|
|
17
50
|
__all__ = [
|
|
18
51
|
'AsyncClient',
|
|
19
52
|
'Browser',
|
|
20
53
|
'Client',
|
|
54
|
+
'CloseError',
|
|
55
|
+
'ConnectError',
|
|
56
|
+
'ConnectTimeout',
|
|
57
|
+
'CookieConflict',
|
|
58
|
+
'Cookies',
|
|
59
|
+
'DecodingError',
|
|
60
|
+
'HTTPError',
|
|
61
|
+
'HTTPStatusError',
|
|
62
|
+
'InvalidURL',
|
|
63
|
+
'LocalProtocolError',
|
|
64
|
+
'NetworkError',
|
|
65
|
+
'PoolTimeout',
|
|
66
|
+
'ProtocolError',
|
|
67
|
+
'ProxyError',
|
|
68
|
+
'ReadError',
|
|
69
|
+
'ReadTimeout',
|
|
70
|
+
'RemoteProtocolError',
|
|
71
|
+
'RequestError',
|
|
72
|
+
'RequestNotRead',
|
|
21
73
|
'Response',
|
|
74
|
+
'ResponseNotRead',
|
|
75
|
+
'StreamClosed',
|
|
76
|
+
'StreamConsumed',
|
|
77
|
+
'StreamError',
|
|
78
|
+
'TimeoutException',
|
|
79
|
+
'TooManyRedirects',
|
|
80
|
+
'TransportError',
|
|
81
|
+
'UnsupportedProtocol',
|
|
82
|
+
'WriteError',
|
|
83
|
+
'WriteTimeout',
|
|
22
84
|
'delete',
|
|
23
85
|
'get',
|
|
24
86
|
'head',
|
|
@@ -26,6 +88,7 @@ __all__ = [
|
|
|
26
88
|
'patch',
|
|
27
89
|
'post',
|
|
28
90
|
'put',
|
|
91
|
+
'stream',
|
|
29
92
|
'trace',
|
|
30
93
|
]
|
|
31
94
|
|
impit/cookies.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
10
|
+
|
|
11
|
+
* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
12
|
+
|
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
14
|
+
"""
|
|
15
|
+
# ruff: noqa: SIM102, E501
|
|
16
|
+
|
|
17
|
+
## The Cookies class below is a modification of the `httpx.Cookies` class, licensed under the BSD 3-Clause "New" License.
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import typing
|
|
22
|
+
from http.cookiejar import Cookie, CookieJar
|
|
23
|
+
|
|
24
|
+
from .impit import CookieConflict
|
|
25
|
+
|
|
26
|
+
CookieTypes = typing.Union['Cookies', CookieJar, dict[str, str], list[tuple[str, str]]]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Cookies(typing.MutableMapping[str, str]):
|
|
30
|
+
"""HTTP Cookies, as a mutable mapping."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, cookies: CookieTypes | None = None) -> None:
|
|
33
|
+
if cookies is None or isinstance(cookies, dict):
|
|
34
|
+
self.jar = CookieJar()
|
|
35
|
+
if isinstance(cookies, dict):
|
|
36
|
+
for key, value in cookies.items():
|
|
37
|
+
self.set(key, value)
|
|
38
|
+
elif isinstance(cookies, list):
|
|
39
|
+
self.jar = CookieJar()
|
|
40
|
+
for key, value in cookies:
|
|
41
|
+
self.set(key, value)
|
|
42
|
+
elif isinstance(cookies, Cookies):
|
|
43
|
+
self.jar = CookieJar()
|
|
44
|
+
for cookie in cookies.jar:
|
|
45
|
+
self.jar.set_cookie(cookie)
|
|
46
|
+
else:
|
|
47
|
+
self.jar = cookies
|
|
48
|
+
|
|
49
|
+
def set(self, name: str, value: str, domain: str = '', path: str = '/') -> None:
|
|
50
|
+
"""Set a cookie value by name. May optionally include domain and path."""
|
|
51
|
+
kwargs = {
|
|
52
|
+
'version': 0,
|
|
53
|
+
'name': name,
|
|
54
|
+
'value': value,
|
|
55
|
+
'port': None,
|
|
56
|
+
'port_specified': False,
|
|
57
|
+
'domain': domain,
|
|
58
|
+
'domain_specified': bool(domain),
|
|
59
|
+
'domain_initial_dot': domain.startswith('.'),
|
|
60
|
+
'path': path,
|
|
61
|
+
'path_specified': bool(path),
|
|
62
|
+
'secure': False,
|
|
63
|
+
'expires': None,
|
|
64
|
+
'discard': True,
|
|
65
|
+
'comment': None,
|
|
66
|
+
'comment_url': None,
|
|
67
|
+
'rest': {'HttpOnly': None},
|
|
68
|
+
'rfc2109': False,
|
|
69
|
+
}
|
|
70
|
+
cookie = Cookie(**kwargs) # type: ignore[arg-type]
|
|
71
|
+
self.jar.set_cookie(cookie)
|
|
72
|
+
|
|
73
|
+
def get( # type: ignore[override]
|
|
74
|
+
self,
|
|
75
|
+
name: str,
|
|
76
|
+
default: str | None = None,
|
|
77
|
+
domain: str | None = None,
|
|
78
|
+
path: str | None = None,
|
|
79
|
+
) -> str | None:
|
|
80
|
+
"""Get a cookie by name.
|
|
81
|
+
|
|
82
|
+
May optionally include domain and path in order to specify exactly which cookie to retrieve.
|
|
83
|
+
"""
|
|
84
|
+
value = None
|
|
85
|
+
for cookie in self.jar:
|
|
86
|
+
if cookie.name == name:
|
|
87
|
+
if domain is None or cookie.domain == domain:
|
|
88
|
+
if path is None or cookie.path == path:
|
|
89
|
+
if value is not None:
|
|
90
|
+
message = f'Multiple cookies exist with name={name}'
|
|
91
|
+
raise CookieConflict(message)
|
|
92
|
+
value = cookie.value
|
|
93
|
+
|
|
94
|
+
if value is None:
|
|
95
|
+
return default
|
|
96
|
+
return value
|
|
97
|
+
|
|
98
|
+
def delete(
|
|
99
|
+
self,
|
|
100
|
+
name: str,
|
|
101
|
+
domain: str | None = None,
|
|
102
|
+
path: str | None = None,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Delete a cookie by name.
|
|
105
|
+
|
|
106
|
+
May optionally include domain and path in order to specify exactly which cookie to delete.
|
|
107
|
+
"""
|
|
108
|
+
if domain is not None and path is not None:
|
|
109
|
+
return self.jar.clear(domain, path, name)
|
|
110
|
+
|
|
111
|
+
remove = [
|
|
112
|
+
cookie
|
|
113
|
+
for cookie in self.jar
|
|
114
|
+
if cookie.name == name
|
|
115
|
+
and (domain is None or cookie.domain == domain)
|
|
116
|
+
and (path is None or cookie.path == path)
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
for cookie in remove:
|
|
120
|
+
self.jar.clear(cookie.domain, cookie.path, cookie.name)
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
def clear(self, domain: str | None = None, path: str | None = None) -> None:
|
|
124
|
+
"""Delete all cookies.
|
|
125
|
+
|
|
126
|
+
Optionally include a domain and path in order to only delete a subset of all the cookies.
|
|
127
|
+
"""
|
|
128
|
+
args = []
|
|
129
|
+
if domain is not None:
|
|
130
|
+
args.append(domain)
|
|
131
|
+
if path is not None:
|
|
132
|
+
assert domain is not None # noqa: S101
|
|
133
|
+
args.append(path)
|
|
134
|
+
self.jar.clear(*args)
|
|
135
|
+
|
|
136
|
+
def update(self, cookies: CookieTypes | None = None) -> None: # type: ignore[override]
|
|
137
|
+
"""Update the cookie jar with new cookies. Accepts various types."""
|
|
138
|
+
cookies = Cookies(cookies)
|
|
139
|
+
for cookie in cookies.jar:
|
|
140
|
+
self.jar.set_cookie(cookie)
|
|
141
|
+
|
|
142
|
+
def __setitem__(self, name: str, value: str) -> None:
|
|
143
|
+
"""Set a cookie by name."""
|
|
144
|
+
return self.set(name, value)
|
|
145
|
+
|
|
146
|
+
def __getitem__(self, name: str) -> str:
|
|
147
|
+
"""Get a cookie by name."""
|
|
148
|
+
value = self.get(name)
|
|
149
|
+
if value is None:
|
|
150
|
+
raise KeyError(name)
|
|
151
|
+
return value
|
|
152
|
+
|
|
153
|
+
def __delitem__(self, name: str) -> None:
|
|
154
|
+
"""Delete a cookie by name."""
|
|
155
|
+
return self.delete(name)
|
|
156
|
+
|
|
157
|
+
def __len__(self) -> int:
|
|
158
|
+
"""Return the number of cookies in the jar."""
|
|
159
|
+
return len(self.jar)
|
|
160
|
+
|
|
161
|
+
def __iter__(self) -> typing.Iterator[str]:
|
|
162
|
+
"""Return an iterator over the names of the cookies in the jar."""
|
|
163
|
+
return (cookie.name for cookie in self.jar)
|
|
164
|
+
|
|
165
|
+
def __bool__(self) -> bool:
|
|
166
|
+
"""Return True if there are any cookies in the jar."""
|
|
167
|
+
for _ in self.jar:
|
|
168
|
+
return True
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
def __repr__(self) -> str:
|
|
172
|
+
"""Return a string representation of the Cookies object."""
|
|
173
|
+
cookies_repr = ', '.join(
|
|
174
|
+
[f'<Cookie {cookie.name}={cookie.value} for {cookie.domain} />' for cookie in self.jar]
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return f'<Cookies[{cookies_repr}]>'
|
|
Binary file
|
impit/impit.pyi
CHANGED
|
@@ -1,44 +1,449 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from http.cookiejar import CookieJar
|
|
3
|
+
from .cookies import Cookies
|
|
2
4
|
|
|
3
|
-
from typing import Literal
|
|
5
|
+
from typing import Literal, Any
|
|
6
|
+
from collections.abc import Iterator, AsyncIterator
|
|
7
|
+
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
Browser = Literal['chrome', 'firefox']
|
|
7
11
|
|
|
8
12
|
|
|
13
|
+
class HTTPError(Exception):
|
|
14
|
+
"""Represents an HTTP-related error."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RequestError(HTTPError):
|
|
18
|
+
"""Represents an error during the request process."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TransportError(RequestError):
|
|
22
|
+
"""Represents a transport-layer error."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TimeoutException(TransportError):
|
|
26
|
+
"""Represents a timeout error."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ConnectTimeout(TimeoutException):
|
|
30
|
+
"""Represents a connection timeout error."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ReadTimeout(TimeoutException):
|
|
34
|
+
"""Represents a read timeout error."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class WriteTimeout(TimeoutException):
|
|
38
|
+
"""Represents a write timeout error."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PoolTimeout(TimeoutException):
|
|
42
|
+
"""Represents a connection pool timeout error."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NetworkError(TransportError):
|
|
46
|
+
"""Represents a network-related error."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ConnectError(NetworkError):
|
|
50
|
+
"""Represents a connection error."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ReadError(NetworkError):
|
|
54
|
+
"""Represents a read error."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class WriteError(NetworkError):
|
|
58
|
+
"""Represents a write error."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CloseError(NetworkError):
|
|
62
|
+
"""Represents an error when closing a connection."""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ProtocolError(TransportError):
|
|
66
|
+
"""Represents a protocol-related error."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class LocalProtocolError(ProtocolError):
|
|
70
|
+
"""Represents a local protocol error."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class RemoteProtocolError(ProtocolError):
|
|
74
|
+
"""Represents a remote protocol error."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ProxyError(TransportError):
|
|
78
|
+
"""Represents a proxy-related error."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class UnsupportedProtocol(TransportError):
|
|
82
|
+
"""Represents an unsupported protocol error."""
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class DecodingError(RequestError):
|
|
86
|
+
"""Represents an error during response decoding."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class TooManyRedirects(RequestError):
|
|
90
|
+
"""Represents an error due to excessive redirects."""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class HTTPStatusError(HTTPError):
|
|
94
|
+
"""Represents an error related to HTTP status codes."""
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class InvalidURL(Exception):
|
|
98
|
+
"""Represents an error due to an invalid URL."""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class CookieConflict(Exception):
|
|
102
|
+
"""Represents a cookie conflict error."""
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class StreamError(Exception):
|
|
106
|
+
"""Represents a stream-related error."""
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class StreamConsumed(StreamError):
|
|
110
|
+
"""Represents an error when a stream is already consumed."""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ResponseNotRead(StreamError):
|
|
114
|
+
"""Represents an error when a response is not read."""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class RequestNotRead(StreamError):
|
|
118
|
+
"""Represents an error when a request is not read."""
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class StreamClosed(StreamError):
|
|
122
|
+
"""Represents an error when a stream is closed."""
|
|
123
|
+
|
|
9
124
|
class Response:
|
|
10
|
-
"""Response object returned by impit
|
|
125
|
+
"""Response object returned by impit clients (:class:`Client` or :class:`AsyncClient` instances).
|
|
126
|
+
|
|
127
|
+
When constructed manually (e.g. for testing purposes), the following parameters can be provided:
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
status_code: HTTP status code of the response (e.g., 200, 404)
|
|
131
|
+
content: Response body as bytes (or None for empty body)
|
|
132
|
+
headers: Response headers as a dictionary (or None for empty headers)
|
|
133
|
+
default_encoding: Default encoding for the response text. Used only if `content-type` header is not present or does not specify a charset.
|
|
134
|
+
url: Final URL of the response.
|
|
135
|
+
"""
|
|
11
136
|
|
|
12
137
|
status_code: int
|
|
13
|
-
"""HTTP status code (e.g., 200, 404)
|
|
138
|
+
"""HTTP status code of the response (e.g., 200, 404).
|
|
139
|
+
|
|
140
|
+
.. tip::
|
|
141
|
+
|
|
142
|
+
If the status code indicates an error (4xx or 5xx), you can raise an `HTTPStatusError` exception using the :meth:`raise_for_status` method.
|
|
143
|
+
|
|
144
|
+
.. code-block:: python
|
|
145
|
+
|
|
146
|
+
response = await client.get("https://crawlee.dev")
|
|
147
|
+
print(response.status_code) # 200
|
|
148
|
+
"""
|
|
14
149
|
|
|
15
150
|
reason_phrase: str
|
|
16
|
-
"""HTTP reason phrase (e.g., 'OK', 'Not Found')
|
|
151
|
+
"""HTTP reason phrase for the response (e.g., 'OK', 'Not Found'). This maps the numerical :attr:`status_code` to a human-readable string.
|
|
152
|
+
|
|
153
|
+
.. code-block:: python
|
|
154
|
+
|
|
155
|
+
response = await client.get("https://crawlee.dev")
|
|
156
|
+
print(response.reason_phrase) # 'OK'
|
|
157
|
+
"""
|
|
17
158
|
|
|
18
159
|
http_version: str
|
|
19
|
-
"""HTTP version (e.g., 'HTTP/1.1', 'HTTP/2')
|
|
160
|
+
"""HTTP version (e.g., 'HTTP/1.1', 'HTTP/2') negotiated for the response during the TLS handshake.
|
|
161
|
+
|
|
162
|
+
.. code-block:: python
|
|
163
|
+
|
|
164
|
+
response = await client.get("https://crawlee.dev")
|
|
165
|
+
print(response.http_version) # 'HTTP/2'
|
|
166
|
+
"""
|
|
20
167
|
|
|
21
168
|
headers: dict[str, str]
|
|
22
|
-
"""Response headers as a dictionary
|
|
169
|
+
"""Response headers as a Python dictionary.
|
|
170
|
+
|
|
171
|
+
.. code-block:: python
|
|
172
|
+
|
|
173
|
+
response = await client.get("https://crawlee.dev")
|
|
174
|
+
print(response.headers) # {'content-type': 'text/html; charset=utf-8', ... }
|
|
175
|
+
"""
|
|
23
176
|
|
|
24
177
|
text: str
|
|
25
|
-
"""Response body as text. Decoded from
|
|
178
|
+
"""Response body as text. Decoded from :attr:`content` using :attr:`encoding`.
|
|
179
|
+
|
|
180
|
+
.. code-block:: python
|
|
181
|
+
|
|
182
|
+
response = await client.get("https://crawlee.dev")
|
|
183
|
+
print(response.text) # '<!DOCTYPE html>...'
|
|
184
|
+
"""
|
|
26
185
|
|
|
27
186
|
encoding: str
|
|
28
|
-
"""Response content encoding
|
|
187
|
+
"""Response content encoding. Determined from `content-type` header or by bytestream prescan. Falls back to 'utf-8' if not found.
|
|
188
|
+
|
|
189
|
+
.. code-block:: python
|
|
190
|
+
|
|
191
|
+
response = await client.get("https://crawlee.dev")
|
|
192
|
+
print(response.encoding) # 'utf-8'
|
|
193
|
+
|
|
194
|
+
This can be used to decode the `Response` body manually. By default, :attr:`text` uses this encoding to decode :attr:`content`.
|
|
195
|
+
"""
|
|
29
196
|
|
|
30
197
|
is_redirect: bool
|
|
31
|
-
"""
|
|
198
|
+
"""`True` if the response is a redirect (has a 3xx status code), `False` otherwise.
|
|
199
|
+
|
|
200
|
+
.. code-block:: python
|
|
201
|
+
|
|
202
|
+
response = await client.get("https://crawlee.dev")
|
|
203
|
+
print(response.is_redirect) # False
|
|
204
|
+
"""
|
|
32
205
|
|
|
33
206
|
url: str
|
|
34
|
-
"""
|
|
207
|
+
"""The final URL of the response. This may differ from the requested URL if redirects were followed (see the `follow_redirects` parameter in :class:`Client` and :class:`AsyncClient`).
|
|
208
|
+
|
|
209
|
+
.. code-block:: python
|
|
210
|
+
|
|
211
|
+
response = await client.get("https://crawlee.dev")
|
|
212
|
+
print(response.url) # 'https://crawlee.dev'
|
|
213
|
+
"""
|
|
35
214
|
|
|
36
215
|
content: bytes
|
|
37
|
-
"""
|
|
216
|
+
"""Contains the response body as bytes. If the response was created with `stream=True`, this will be empty until the content is read using :meth:`read` or :meth:`iter_bytes`.
|
|
217
|
+
|
|
218
|
+
.. code-block:: python
|
|
219
|
+
|
|
220
|
+
response = await client.get("https://crawlee.dev")
|
|
221
|
+
print(response.content) # b'<!DOCTYPE html>...'
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
is_closed: bool
|
|
225
|
+
"""
|
|
226
|
+
True if the response has been closed using the :meth:`close` method, `False` otherwise.
|
|
227
|
+
|
|
228
|
+
Closing a response releases any underlying resources (e.g., network connections).
|
|
229
|
+
|
|
230
|
+
.. code-block:: python
|
|
231
|
+
|
|
232
|
+
response = await client.get("https://crawlee.dev")
|
|
233
|
+
print(response.is_closed) # False
|
|
234
|
+
response.close()
|
|
235
|
+
print(response.is_closed) # True
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
is_stream_consumed: bool
|
|
239
|
+
"""Whether the response stream has been consumed or closed.
|
|
240
|
+
|
|
241
|
+
If this is `True`, calling :meth:`read` or :meth:`iter_bytes` will raise a :class:`StreamConsumed` or :class:`StreamClosed` error.
|
|
242
|
+
|
|
243
|
+
The read response body is still available in the :attr:`content` attribute.
|
|
244
|
+
|
|
245
|
+
.. code-block:: python
|
|
246
|
+
|
|
247
|
+
response = await client.get("https://crawlee.dev", stream=True)
|
|
248
|
+
print(response.is_stream_consumed) # False
|
|
249
|
+
for chunk in response.iter_bytes():
|
|
250
|
+
pass
|
|
251
|
+
print(response.is_stream_consumed) # True
|
|
252
|
+
# calling response.read() or response.iter_bytes() again will raise StreamConsumed error
|
|
253
|
+
# read the content of the response using response.content
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
def __init__(
|
|
257
|
+
self,
|
|
258
|
+
status_code: int,
|
|
259
|
+
*,
|
|
260
|
+
content: bytes | None = None,
|
|
261
|
+
headers: dict[str, str] | None = None,
|
|
262
|
+
default_encoding: str | None = None,
|
|
263
|
+
url: str | None = None,
|
|
264
|
+
) -> None:
|
|
265
|
+
"""Initialize a Response object.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
status_code: HTTP status code
|
|
269
|
+
content: Response body as bytes
|
|
270
|
+
headers: Response headers as a dictionary
|
|
271
|
+
default_encoding: Default encoding for the response text. Used only if `content-type` header is not present or does not specify a charset.
|
|
272
|
+
url: Final URL of the response
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
def read(self) -> bytes:
|
|
276
|
+
"""Read the response content as bytes. Synchronous version of :meth:`aread`.
|
|
277
|
+
|
|
278
|
+
Useful for consuming the entire response body in one go (not chunked).
|
|
279
|
+
|
|
280
|
+
.. code-block:: python
|
|
281
|
+
|
|
282
|
+
with Client() as client:
|
|
283
|
+
with client.stream("GET", "https://example.com/largefile") as response:
|
|
284
|
+
content = response.read()
|
|
285
|
+
process(content) # Process the entire content at once
|
|
286
|
+
"""
|
|
287
|
+
def iter_bytes(self) -> Iterator[bytes]:
|
|
288
|
+
"""Iterate over the response content in chunks. Synchronous version of :meth:`aiter_bytes`.
|
|
289
|
+
|
|
290
|
+
Useful for streaming large responses without loading the entire content into memory.
|
|
291
|
+
.. code-block:: python
|
|
292
|
+
|
|
293
|
+
with Client() as client:
|
|
294
|
+
with client.stream("GET", "https://example.com/largefile") as response:
|
|
295
|
+
for chunk in response.iter_bytes():
|
|
296
|
+
process(chunk) # Process each chunk as it is received
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
async def aread(self) -> bytes:
|
|
300
|
+
"""Asynchronously read the response content as bytes. Asynchronous version of :meth:`read`.
|
|
301
|
+
|
|
302
|
+
Useful for consuming the entire response body in one go (not chunked).
|
|
303
|
+
|
|
304
|
+
.. code-block:: python
|
|
305
|
+
|
|
306
|
+
async with AsyncClient() as client:
|
|
307
|
+
async with client.stream("GET", "https://example.com/largefile") as response:
|
|
308
|
+
content = await response.aread()
|
|
309
|
+
process(content) # Process the entire content at once
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
def aiter_bytes(self) -> AsyncIterator[bytes]:
|
|
313
|
+
"""Asynchronously iterate over the response content in chunks. Asynchronous version of :meth:`iter_bytes`.
|
|
314
|
+
|
|
315
|
+
Useful for streaming large responses without loading the entire content into memory.
|
|
316
|
+
|
|
317
|
+
.. code-block:: python
|
|
318
|
+
|
|
319
|
+
async with AsyncClient() as client:
|
|
320
|
+
async with client.stream("GET", "https://example.com/largefile") as response:
|
|
321
|
+
async for chunk in response.aiter_bytes():
|
|
322
|
+
process(chunk) # Process each chunk as it is received
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
def json(self) -> Any:
|
|
326
|
+
"""Parse the response content as JSON.
|
|
327
|
+
|
|
328
|
+
.. note::
|
|
329
|
+
This method will raise a `DecodingError` if the response content is not valid JSON.
|
|
330
|
+
|
|
331
|
+
.. code-block:: python
|
|
332
|
+
|
|
333
|
+
response = await client.get("https://api.example.com/data")
|
|
334
|
+
data = response.json()
|
|
335
|
+
print(data) # Parsed JSON data as a Python object (dict, list, etc.)
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
def close(self) -> None:
|
|
339
|
+
"""Close the response and release resources.
|
|
340
|
+
|
|
341
|
+
.. warning::
|
|
342
|
+
You should not need to call this method directly.
|
|
343
|
+
|
|
344
|
+
Use the `with` statement to ensure proper resource management when working with synchronous clients.
|
|
345
|
+
|
|
346
|
+
.. code-block:: python
|
|
38
347
|
|
|
348
|
+
with impit.stream('GET', get_httpbin_url('/')) as response:
|
|
349
|
+
assert response.status_code == 200
|
|
350
|
+
|
|
351
|
+
assert response.is_closed is True
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
async def aclose(self) -> None:
|
|
355
|
+
"""Asynchronously close the response and release resources.
|
|
356
|
+
|
|
357
|
+
.. note::
|
|
358
|
+
This method is for internal use only.
|
|
359
|
+
|
|
360
|
+
Use the `async with` statement to ensure proper resource management when working with asynchronous clients.
|
|
361
|
+
|
|
362
|
+
.. code-block:: python
|
|
363
|
+
|
|
364
|
+
async with impit.stream('GET', get_httpbin_url('/')) as response:
|
|
365
|
+
assert response.status_code == 200
|
|
366
|
+
|
|
367
|
+
assert response.is_closed is True
|
|
368
|
+
"""
|
|
39
369
|
|
|
40
370
|
class Client:
|
|
41
|
-
"""Synchronous HTTP client with browser impersonation capabilities.
|
|
371
|
+
"""Synchronous HTTP client with browser impersonation capabilities.
|
|
372
|
+
|
|
373
|
+
.. note::
|
|
374
|
+
You can reuse the :class:`Client` instance to make multiple requests.
|
|
375
|
+
|
|
376
|
+
All requests made by the same client will share the same configuration, resources (e.g., cookie jar and connection pool), and other settings.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
browser: Browser to impersonate (`"chrome"` or `"firefox"`).
|
|
380
|
+
|
|
381
|
+
If this is `None` (default), no impersonation is performed.
|
|
382
|
+
http3:
|
|
383
|
+
|
|
384
|
+
If set to `True`, Impit will try to connect to the target servers using HTTP/3 protocol (if supported by the server).
|
|
385
|
+
|
|
386
|
+
.. note::
|
|
387
|
+
The HTTP/3 support is experimental and may not work with all servers. The impersonation capabilities are limited when using HTTP/3.
|
|
388
|
+
|
|
389
|
+
proxy:
|
|
390
|
+
|
|
391
|
+
The proxy URL to use for all the requests made by this client.
|
|
392
|
+
|
|
393
|
+
This can be an HTTP, HTTPS, or SOCKS proxy.
|
|
394
|
+
timeout:
|
|
395
|
+
Default request timeout in seconds.
|
|
396
|
+
|
|
397
|
+
This value can be overridden for individual requests.
|
|
398
|
+
verify:
|
|
399
|
+
If set to `False`, impit will not verify SSL certificates.
|
|
400
|
+
|
|
401
|
+
This can be used to ignore TLS errors (e.g., self-signed certificates).
|
|
402
|
+
|
|
403
|
+
True by default.
|
|
404
|
+
default_encoding:
|
|
405
|
+
Default encoding for response.text field (e.g., "utf-8", "cp1252").
|
|
406
|
+
|
|
407
|
+
Overrides `content-type` headers and bytestream prescan.
|
|
408
|
+
follow_redirects:
|
|
409
|
+
|
|
410
|
+
If set to `True` the client will automatically follow HTTP redirects (3xx responses).
|
|
411
|
+
|
|
412
|
+
`False` by default.
|
|
413
|
+
max_redirects:
|
|
414
|
+
|
|
415
|
+
Maximum number of redirects to follow if the `follow_redirects` option is enabled.
|
|
416
|
+
|
|
417
|
+
Default is 20.
|
|
418
|
+
cookie_jar:
|
|
419
|
+
|
|
420
|
+
Cookie jar to store cookies in.
|
|
421
|
+
|
|
422
|
+
This is a `http.cookiejar.CookieJar` instance.
|
|
423
|
+
|
|
424
|
+
By default, :class:`Client` doesn't store cookies between requests.
|
|
425
|
+
cookies: httpx-compatible cookies object.
|
|
426
|
+
|
|
427
|
+
These are the cookies to include in all requests (to the matching servers) made by this client.
|
|
428
|
+
headers: Default HTTP headers to include in requests.
|
|
429
|
+
|
|
430
|
+
These headers will be included in all requests made by this client.
|
|
431
|
+
|
|
432
|
+
Default is an empty dictionary.
|
|
433
|
+
local_address:
|
|
434
|
+
|
|
435
|
+
Local address to bind the client to.
|
|
436
|
+
|
|
437
|
+
Useful for testing purposes or when you want to bind the client to a specific network interface.
|
|
438
|
+
Can be an IP address in the format `xxx.xxx.xxx.xxx` (for IPv4) or `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff` (for IPv6).
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
def __enter__(self) -> Client:
|
|
442
|
+
"""Enter the runtime context related to this object."""
|
|
443
|
+
|
|
444
|
+
def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: object | None) -> None:
|
|
445
|
+
"""Exit the runtime context related to this object."""
|
|
446
|
+
|
|
42
447
|
|
|
43
448
|
def __init__(
|
|
44
449
|
self,
|
|
@@ -50,6 +455,10 @@ class Client:
|
|
|
50
455
|
default_encoding: str | None = None,
|
|
51
456
|
follow_redirects: bool | None = None,
|
|
52
457
|
max_redirects: int | None = None,
|
|
458
|
+
cookie_jar: CookieJar | None = None,
|
|
459
|
+
cookies: Cookies | None = None,
|
|
460
|
+
headers: dict[str, str] | None = None,
|
|
461
|
+
local_address: str | None = None,
|
|
53
462
|
) -> None:
|
|
54
463
|
"""Initialize a synchronous HTTP client.
|
|
55
464
|
|
|
@@ -63,6 +472,11 @@ class Client:
|
|
|
63
472
|
header and bytestream prescan.
|
|
64
473
|
follow_redirects: Whether to follow redirects (default: False)
|
|
65
474
|
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
475
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
476
|
+
cookies: httpx-compatible cookies object.
|
|
477
|
+
headers: Default HTTP headers to include in requests.
|
|
478
|
+
local_address: Local address to bind the client to. Useful for testing purposes or when you want to bind the client to a specific network interface.
|
|
479
|
+
Can be an IP address in the format "xxx.xxx.xxx.xxx" (for IPv4) or "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" (for IPv6).
|
|
66
480
|
"""
|
|
67
481
|
|
|
68
482
|
def get(
|
|
@@ -235,9 +649,44 @@ class Client:
|
|
|
235
649
|
headers: dict[str, str] | None = None,
|
|
236
650
|
timeout: float | None = None,
|
|
237
651
|
force_http3: bool | None = None,
|
|
652
|
+
stream: bool = False,
|
|
238
653
|
) -> Response:
|
|
239
654
|
"""Make an HTTP request with the specified method.
|
|
240
655
|
|
|
656
|
+
Args:
|
|
657
|
+
method: HTTP method (e.g., "get", "post")
|
|
658
|
+
url: URL to request
|
|
659
|
+
content: Raw content to send
|
|
660
|
+
data: Form data to send in request body
|
|
661
|
+
headers: HTTP headers
|
|
662
|
+
timeout: Request timeout in seconds (overrides default timeout)
|
|
663
|
+
force_http3: Force HTTP/3 protocol
|
|
664
|
+
stream: Whether to return a streaming response (default: False)
|
|
665
|
+
"""
|
|
666
|
+
|
|
667
|
+
def stream(
|
|
668
|
+
self,
|
|
669
|
+
method: str,
|
|
670
|
+
url: str,
|
|
671
|
+
content: bytes | bytearray | list[int] | None = None,
|
|
672
|
+
data: dict[str, str] | None = None,
|
|
673
|
+
headers: dict[str, str] | None = None,
|
|
674
|
+
timeout: float | None = None,
|
|
675
|
+
force_http3: bool | None = None,
|
|
676
|
+
) -> AbstractContextManager[Response]:
|
|
677
|
+
"""Make a streaming request with the specified method.
|
|
678
|
+
|
|
679
|
+
This method returns a context manager that yields a streaming :class:`Response` object.
|
|
680
|
+
|
|
681
|
+
See the following example for usage:
|
|
682
|
+
|
|
683
|
+
.. code-block:: python
|
|
684
|
+
|
|
685
|
+
with Client() as client:
|
|
686
|
+
with client.stream("GET", "https://example.com/largefile") as response:
|
|
687
|
+
for chunk in response.iter_bytes():
|
|
688
|
+
process(chunk) # Process each chunk as it is received
|
|
689
|
+
|
|
241
690
|
Args:
|
|
242
691
|
method: HTTP method (e.g., "get", "post")
|
|
243
692
|
url: URL to request
|
|
@@ -250,7 +699,81 @@ class Client:
|
|
|
250
699
|
|
|
251
700
|
|
|
252
701
|
class AsyncClient:
|
|
253
|
-
"""Asynchronous HTTP client with browser impersonation capabilities.
|
|
702
|
+
"""Asynchronous HTTP client with browser impersonation capabilities.
|
|
703
|
+
|
|
704
|
+
.. note::
|
|
705
|
+
You can reuse the :class:`Client` instance to make multiple requests.
|
|
706
|
+
|
|
707
|
+
All requests made by the same client will share the same configuration, resources (e.g., cookie jar and connection pool), and other settings.
|
|
708
|
+
|
|
709
|
+
Args:
|
|
710
|
+
browser: Browser to impersonate (`"chrome"` or `"firefox"`).
|
|
711
|
+
|
|
712
|
+
If this is `None` (default), no impersonation is performed.
|
|
713
|
+
http3:
|
|
714
|
+
|
|
715
|
+
If set to `True`, Impit will try to connect to the target servers using HTTP/3 protocol (if supported by the server).
|
|
716
|
+
|
|
717
|
+
.. note::
|
|
718
|
+
The HTTP/3 support is experimental and may not work with all servers. The impersonation capabilities are limited when using HTTP/3.
|
|
719
|
+
|
|
720
|
+
proxy:
|
|
721
|
+
|
|
722
|
+
The proxy URL to use for all the requests made by this client.
|
|
723
|
+
|
|
724
|
+
This can be an HTTP, HTTPS, or SOCKS proxy.
|
|
725
|
+
timeout:
|
|
726
|
+
Default request timeout in seconds.
|
|
727
|
+
|
|
728
|
+
This value can be overridden for individual requests.
|
|
729
|
+
verify:
|
|
730
|
+
If set to `False`, impit will not verify SSL certificates.
|
|
731
|
+
|
|
732
|
+
This can be used to ignore TLS errors (e.g., self-signed certificates).
|
|
733
|
+
|
|
734
|
+
True by default.
|
|
735
|
+
default_encoding:
|
|
736
|
+
Default encoding for response.text field (e.g., "utf-8", "cp1252").
|
|
737
|
+
|
|
738
|
+
Overrides `content-type` headers and bytestream prescan.
|
|
739
|
+
follow_redirects:
|
|
740
|
+
|
|
741
|
+
If set to `True` the client will automatically follow HTTP redirects (3xx responses).
|
|
742
|
+
|
|
743
|
+
`False` by default.
|
|
744
|
+
max_redirects:
|
|
745
|
+
|
|
746
|
+
Maximum number of redirects to follow if the `follow_redirects` option is enabled.
|
|
747
|
+
|
|
748
|
+
Default is 20.
|
|
749
|
+
cookie_jar:
|
|
750
|
+
|
|
751
|
+
Cookie jar to store cookies in.
|
|
752
|
+
|
|
753
|
+
This is a `http.cookiejar.CookieJar` instance.
|
|
754
|
+
|
|
755
|
+
By default, :class:`Client` doesn't store cookies between requests.
|
|
756
|
+
cookies: httpx-compatible cookies object.
|
|
757
|
+
|
|
758
|
+
These are the cookies to include in all requests (to the matching servers) made by this client.
|
|
759
|
+
headers: Default HTTP headers to include in requests.
|
|
760
|
+
|
|
761
|
+
These headers will be included in all requests made by this client.
|
|
762
|
+
|
|
763
|
+
Default is an empty dictionary.
|
|
764
|
+
local_address:
|
|
765
|
+
|
|
766
|
+
Local address to bind the client to.
|
|
767
|
+
|
|
768
|
+
Useful for testing purposes or when you want to bind the client to a specific network interface.
|
|
769
|
+
Can be an IP address in the format `xxx.xxx.xxx.xxx` (for IPv4) or `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff` (for IPv6).
|
|
770
|
+
"""
|
|
771
|
+
|
|
772
|
+
async def __aenter__(self) -> AsyncClient:
|
|
773
|
+
"""Enter the runtime context related to this object."""
|
|
774
|
+
|
|
775
|
+
async def __aexit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: object | None) -> None:
|
|
776
|
+
"""Exit the runtime context related to this object."""
|
|
254
777
|
|
|
255
778
|
def __init__(
|
|
256
779
|
self,
|
|
@@ -262,6 +785,10 @@ class AsyncClient:
|
|
|
262
785
|
default_encoding: str | None = None,
|
|
263
786
|
follow_redirects: bool | None = None,
|
|
264
787
|
max_redirects: int | None = None,
|
|
788
|
+
cookie_jar: CookieJar | None = None,
|
|
789
|
+
cookies: Cookies | None = None,
|
|
790
|
+
headers: dict[str, str] | None = None,
|
|
791
|
+
local_address: str | None = None,
|
|
265
792
|
) -> None:
|
|
266
793
|
"""Initialize an asynchronous HTTP client.
|
|
267
794
|
|
|
@@ -275,6 +802,11 @@ class AsyncClient:
|
|
|
275
802
|
header and bytestream prescan.
|
|
276
803
|
follow_redirects: Whether to follow redirects (default: False)
|
|
277
804
|
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
805
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
806
|
+
cookies: httpx-compatible cookies object.
|
|
807
|
+
headers: Default HTTP headers to include in requests.
|
|
808
|
+
local_address: Local address to bind the client to. Useful for testing purposes or when you want to bind the client to a specific network interface.
|
|
809
|
+
Can be an IP address in the format "xxx.xxx.xxx.xxx" (for IPv4) or "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" (for IPv6).
|
|
278
810
|
"""
|
|
279
811
|
|
|
280
812
|
async def get(
|
|
@@ -315,6 +847,7 @@ class AsyncClient:
|
|
|
315
847
|
headers: HTTP headers
|
|
316
848
|
timeout: Request timeout in seconds (overrides default timeout)
|
|
317
849
|
force_http3: Force HTTP/3 protocol
|
|
850
|
+
|
|
318
851
|
"""
|
|
319
852
|
|
|
320
853
|
async def put(
|
|
@@ -446,9 +979,44 @@ class AsyncClient:
|
|
|
446
979
|
headers: dict[str, str] | None = None,
|
|
447
980
|
timeout: float | None = None,
|
|
448
981
|
force_http3: bool | None = None,
|
|
982
|
+
stream: bool = False,
|
|
449
983
|
) -> Response:
|
|
450
984
|
"""Make an asynchronous HTTP request with the specified method.
|
|
451
985
|
|
|
986
|
+
Args:
|
|
987
|
+
method: HTTP method (e.g., "get", "post")
|
|
988
|
+
url: URL to request
|
|
989
|
+
content: Raw content to send
|
|
990
|
+
data: Form data to send in request body
|
|
991
|
+
headers: HTTP headers
|
|
992
|
+
timeout: Request timeout in seconds (overrides default timeout)
|
|
993
|
+
force_http3: Force HTTP/3 protocol
|
|
994
|
+
stream: Whether to return a streaming response (default: False)
|
|
995
|
+
"""
|
|
996
|
+
|
|
997
|
+
def stream(
|
|
998
|
+
self,
|
|
999
|
+
method: str,
|
|
1000
|
+
url: str,
|
|
1001
|
+
content: bytes | bytearray | list[int] | None = None,
|
|
1002
|
+
data: dict[str, str] | None = None,
|
|
1003
|
+
headers: dict[str, str] | None = None,
|
|
1004
|
+
timeout: float | None = None,
|
|
1005
|
+
force_http3: bool | None = None,
|
|
1006
|
+
) -> AbstractAsyncContextManager[Response]:
|
|
1007
|
+
"""Make an asynchronous streaming request with the specified method.
|
|
1008
|
+
|
|
1009
|
+
This method returns a AsyncContextManager that yields a streaming :class:`Response` object.
|
|
1010
|
+
|
|
1011
|
+
See the following example for usage:
|
|
1012
|
+
|
|
1013
|
+
.. code-block:: python
|
|
1014
|
+
|
|
1015
|
+
with Client() as client:
|
|
1016
|
+
with client.stream("GET", "https://example.com/largefile") as response:
|
|
1017
|
+
for chunk in response.iter_bytes():
|
|
1018
|
+
process(chunk) # Process each chunk as it is received
|
|
1019
|
+
|
|
452
1020
|
Args:
|
|
453
1021
|
method: HTTP method (e.g., "get", "post")
|
|
454
1022
|
url: URL to request
|
|
@@ -460,6 +1028,40 @@ class AsyncClient:
|
|
|
460
1028
|
"""
|
|
461
1029
|
|
|
462
1030
|
|
|
1031
|
+
def stream(
|
|
1032
|
+
method: str,
|
|
1033
|
+
url: str,
|
|
1034
|
+
content: bytes | bytearray | list[int] | None = None,
|
|
1035
|
+
data: dict[str, str] | None = None,
|
|
1036
|
+
headers: dict[str, str] | None = None,
|
|
1037
|
+
timeout: float | None = None,
|
|
1038
|
+
force_http3: bool | None = None,
|
|
1039
|
+
follow_redirects: bool | None = None,
|
|
1040
|
+
max_redirects: int | None = None,
|
|
1041
|
+
cookie_jar: CookieJar | None = None,
|
|
1042
|
+
cookies: Cookies | None = None,
|
|
1043
|
+
proxy: str | None = None,
|
|
1044
|
+
) -> AbstractContextManager[Response]:
|
|
1045
|
+
"""Make a streaming request without creating a client instance.
|
|
1046
|
+
|
|
1047
|
+
Args:
|
|
1048
|
+
method: HTTP method (e.g., "get", "post")
|
|
1049
|
+
url: URL to request
|
|
1050
|
+
content: Raw content to send
|
|
1051
|
+
data: Form data to send in request body
|
|
1052
|
+
headers: HTTP headers
|
|
1053
|
+
timeout: Request timeout in seconds
|
|
1054
|
+
force_http3: Force HTTP/3 protocol
|
|
1055
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1056
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1057
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1058
|
+
cookies: httpx-compatible cookies object.
|
|
1059
|
+
proxy: Proxy URL to use to make the request.
|
|
1060
|
+
|
|
1061
|
+
Returns:
|
|
1062
|
+
Response object
|
|
1063
|
+
"""
|
|
1064
|
+
|
|
463
1065
|
def get(
|
|
464
1066
|
url: str,
|
|
465
1067
|
content: bytes | bytearray | list[int] | None = None,
|
|
@@ -467,6 +1069,11 @@ def get(
|
|
|
467
1069
|
headers: dict[str, str] | None = None,
|
|
468
1070
|
timeout: float | None = None,
|
|
469
1071
|
force_http3: bool | None = None,
|
|
1072
|
+
follow_redirects: bool | None = None,
|
|
1073
|
+
max_redirects: int | None = None,
|
|
1074
|
+
cookie_jar: CookieJar | None = None,
|
|
1075
|
+
cookies: Cookies | None = None,
|
|
1076
|
+
proxy: str | None = None,
|
|
470
1077
|
) -> Response:
|
|
471
1078
|
"""Make a GET request without creating a client instance.
|
|
472
1079
|
|
|
@@ -477,6 +1084,11 @@ def get(
|
|
|
477
1084
|
headers: HTTP headers
|
|
478
1085
|
timeout: Request timeout in seconds
|
|
479
1086
|
force_http3: Force HTTP/3 protocol
|
|
1087
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1088
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1089
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1090
|
+
cookies: httpx-compatible cookies object.
|
|
1091
|
+
proxy: Proxy URL to use to make the request.
|
|
480
1092
|
|
|
481
1093
|
Returns:
|
|
482
1094
|
Response object
|
|
@@ -490,6 +1102,11 @@ def post(
|
|
|
490
1102
|
headers: dict[str, str] | None = None,
|
|
491
1103
|
timeout: float | None = None,
|
|
492
1104
|
force_http3: bool | None = None,
|
|
1105
|
+
follow_redirects: bool | None = None,
|
|
1106
|
+
max_redirects: int | None = None,
|
|
1107
|
+
cookie_jar: CookieJar | None = None,
|
|
1108
|
+
cookies: Cookies | None = None,
|
|
1109
|
+
proxy: str | None = None,
|
|
493
1110
|
) -> Response:
|
|
494
1111
|
"""Make a POST request without creating a client instance.
|
|
495
1112
|
|
|
@@ -500,6 +1117,11 @@ def post(
|
|
|
500
1117
|
headers: HTTP headers
|
|
501
1118
|
timeout: Request timeout in seconds
|
|
502
1119
|
force_http3: Force HTTP/3 protocol
|
|
1120
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1121
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1122
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1123
|
+
cookies: httpx-compatible cookies object.
|
|
1124
|
+
proxy: Proxy URL to use to make the request.
|
|
503
1125
|
|
|
504
1126
|
Returns:
|
|
505
1127
|
Response object
|
|
@@ -513,6 +1135,11 @@ def put(
|
|
|
513
1135
|
headers: dict[str, str] | None = None,
|
|
514
1136
|
timeout: float | None = None,
|
|
515
1137
|
force_http3: bool | None = None,
|
|
1138
|
+
follow_redirects: bool | None = None,
|
|
1139
|
+
max_redirects: int | None = None,
|
|
1140
|
+
cookie_jar: CookieJar | None = None,
|
|
1141
|
+
cookies: Cookies | None = None,
|
|
1142
|
+
proxy: str | None = None,
|
|
516
1143
|
) -> Response:
|
|
517
1144
|
"""Make a PUT request without creating a client instance.
|
|
518
1145
|
|
|
@@ -523,6 +1150,11 @@ def put(
|
|
|
523
1150
|
headers: HTTP headers
|
|
524
1151
|
timeout: Request timeout in seconds
|
|
525
1152
|
force_http3: Force HTTP/3 protocol
|
|
1153
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1154
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1155
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1156
|
+
cookies: httpx-compatible cookies object.
|
|
1157
|
+
proxy: Proxy URL to use to make the request.
|
|
526
1158
|
|
|
527
1159
|
Returns:
|
|
528
1160
|
Response object
|
|
@@ -536,6 +1168,11 @@ def patch(
|
|
|
536
1168
|
headers: dict[str, str] | None = None,
|
|
537
1169
|
timeout: float | None = None,
|
|
538
1170
|
force_http3: bool | None = None,
|
|
1171
|
+
follow_redirects: bool | None = None,
|
|
1172
|
+
max_redirects: int | None = None,
|
|
1173
|
+
cookie_jar: CookieJar | None = None,
|
|
1174
|
+
cookies: Cookies | None = None,
|
|
1175
|
+
proxy: str | None = None,
|
|
539
1176
|
) -> Response:
|
|
540
1177
|
"""Make a PATCH request without creating a client instance.
|
|
541
1178
|
|
|
@@ -546,6 +1183,11 @@ def patch(
|
|
|
546
1183
|
headers: HTTP headers
|
|
547
1184
|
timeout: Request timeout in seconds
|
|
548
1185
|
force_http3: Force HTTP/3 protocol
|
|
1186
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1187
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1188
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1189
|
+
cookies: httpx-compatible cookies object.
|
|
1190
|
+
proxy: Proxy URL to use to make the request.
|
|
549
1191
|
|
|
550
1192
|
Returns:
|
|
551
1193
|
Response object
|
|
@@ -559,6 +1201,11 @@ def delete(
|
|
|
559
1201
|
headers: dict[str, str] | None = None,
|
|
560
1202
|
timeout: float | None = None,
|
|
561
1203
|
force_http3: bool | None = None,
|
|
1204
|
+
follow_redirects: bool | None = None,
|
|
1205
|
+
max_redirects: int | None = None,
|
|
1206
|
+
cookie_jar: CookieJar | None = None,
|
|
1207
|
+
cookies: Cookies | None = None,
|
|
1208
|
+
proxy: str | None = None,
|
|
562
1209
|
) -> Response:
|
|
563
1210
|
"""Make a DELETE request without creating a client instance.
|
|
564
1211
|
|
|
@@ -569,6 +1216,11 @@ def delete(
|
|
|
569
1216
|
headers: HTTP headers
|
|
570
1217
|
timeout: Request timeout in seconds
|
|
571
1218
|
force_http3: Force HTTP/3 protocol
|
|
1219
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1220
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1221
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1222
|
+
cookies: httpx-compatible cookies object.
|
|
1223
|
+
proxy: Proxy URL to use to make the request.
|
|
572
1224
|
|
|
573
1225
|
Returns:
|
|
574
1226
|
Response object
|
|
@@ -582,6 +1234,11 @@ def head(
|
|
|
582
1234
|
headers: dict[str, str] | None = None,
|
|
583
1235
|
timeout: float | None = None,
|
|
584
1236
|
force_http3: bool | None = None,
|
|
1237
|
+
follow_redirects: bool | None = None,
|
|
1238
|
+
max_redirects: int | None = None,
|
|
1239
|
+
cookie_jar: CookieJar | None = None,
|
|
1240
|
+
cookies: Cookies | None = None,
|
|
1241
|
+
proxy: str | None = None,
|
|
585
1242
|
) -> Response:
|
|
586
1243
|
"""Make a HEAD request without creating a client instance.
|
|
587
1244
|
|
|
@@ -592,6 +1249,11 @@ def head(
|
|
|
592
1249
|
headers: HTTP headers
|
|
593
1250
|
timeout: Request timeout in seconds
|
|
594
1251
|
force_http3: Force HTTP/3 protocol
|
|
1252
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1253
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1254
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1255
|
+
cookies: httpx-compatible cookies object.
|
|
1256
|
+
proxy: Proxy URL to use to make the request.
|
|
595
1257
|
|
|
596
1258
|
Returns:
|
|
597
1259
|
Response object
|
|
@@ -605,6 +1267,11 @@ def options(
|
|
|
605
1267
|
headers: dict[str, str] | None = None,
|
|
606
1268
|
timeout: float | None = None,
|
|
607
1269
|
force_http3: bool | None = None,
|
|
1270
|
+
follow_redirects: bool | None = None,
|
|
1271
|
+
max_redirects: int | None = None,
|
|
1272
|
+
cookie_jar: CookieJar | None = None,
|
|
1273
|
+
cookies: Cookies | None = None,
|
|
1274
|
+
proxy: str | None = None,
|
|
608
1275
|
) -> Response:
|
|
609
1276
|
"""Make an OPTIONS request without creating a client instance.
|
|
610
1277
|
|
|
@@ -615,6 +1282,11 @@ def options(
|
|
|
615
1282
|
headers: HTTP headers
|
|
616
1283
|
timeout: Request timeout in seconds (overrides default timeout)
|
|
617
1284
|
force_http3: Force HTTP/3 protocol
|
|
1285
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1286
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1287
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1288
|
+
cookies: httpx-compatible cookies object.
|
|
1289
|
+
proxy: Proxy URL to use to make the request.
|
|
618
1290
|
"""
|
|
619
1291
|
|
|
620
1292
|
|
|
@@ -625,6 +1297,11 @@ def trace(
|
|
|
625
1297
|
headers: dict[str, str] | None = None,
|
|
626
1298
|
timeout: float | None = None,
|
|
627
1299
|
force_http3: bool | None = None,
|
|
1300
|
+
follow_redirects: bool | None = None,
|
|
1301
|
+
max_redirects: int | None = None,
|
|
1302
|
+
cookie_jar: CookieJar | None = None,
|
|
1303
|
+
cookies: Cookies | None = None,
|
|
1304
|
+
proxy: str | None = None,
|
|
628
1305
|
) -> Response:
|
|
629
1306
|
"""Make a TRACE request without creating a client instance.
|
|
630
1307
|
|
|
@@ -635,4 +1312,9 @@ def trace(
|
|
|
635
1312
|
headers: HTTP headers
|
|
636
1313
|
timeout: Request timeout in seconds (overrides default timeout)
|
|
637
1314
|
force_http3: Force HTTP/3 protocol
|
|
1315
|
+
follow_redirects: Whether to follow redirects (default: False)
|
|
1316
|
+
max_redirects: Maximum number of redirects to follow (default: 20)
|
|
1317
|
+
cookie_jar: Cookie jar to store cookies in.
|
|
1318
|
+
cookies: httpx-compatible cookies object.
|
|
1319
|
+
proxy: Proxy URL to use to make the request.
|
|
638
1320
|
"""
|
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: impit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Environment :: Console
|
|
5
6
|
Classifier: Intended Audience :: Developers
|
|
6
7
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
7
8
|
Classifier: Operating System :: OS Independent
|
|
8
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
14
|
Classifier: Topic :: Software Development :: Libraries
|
|
14
15
|
Summary: A library for making HTTP requests through browser impersonation
|
|
15
16
|
Keywords: apify,http,requests,browser,impersonation
|
|
16
17
|
Author: Jindřich Bär
|
|
17
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
18
19
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
19
|
-
Project-URL: Homepage, https://apify.github.io/impit/
|
|
20
|
+
Project-URL: Homepage, https://apify.github.io/impit/python
|
|
20
21
|
Project-URL: Apify homepage, https://apify.com
|
|
21
|
-
Project-URL: Changelog, https://github.com/apify/impit/blob/master/CHANGELOG.md
|
|
22
|
-
Project-URL: Documentation, https://apify.github.io/impit/
|
|
22
|
+
Project-URL: Changelog, https://github.com/apify/impit/blob/master/impit-python/CHANGELOG.md
|
|
23
|
+
Project-URL: Documentation, https://apify.github.io/impit/python
|
|
23
24
|
Project-URL: Issue tracker, https://github.com/apify/impit/issues
|
|
24
25
|
Project-URL: Repository, https://github.com/apify/impit
|
|
25
26
|
|
|
26
27
|
# `impit` for Python
|
|
27
28
|
|
|
29
|
+
> This documents the `impit` Python package, which provides bindings for the `impit` library.
|
|
30
|
+
>
|
|
31
|
+
> See documentation for the JavaScript/TypeScript version of `impit` [here](https://apify.github.io/impit/js/).
|
|
32
|
+
|
|
28
33
|
`impit` is a Python package that provides bindings for the [`impit`](https://github.com/apify/impit) library.
|
|
29
34
|
|
|
30
35
|
It allows you to switch the TLS fingerprints and the HTTP headers of your requests, while still using the same API as `httpx` or `requests`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
impit-0.9.0.dist-info/METADATA,sha256=rUem-pBLg6yAp-5ARgqksLUOut5yrVmou21VKAv3Qy0,2833
|
|
2
|
+
impit-0.9.0.dist-info/WHEEL,sha256=k_XiDtlLA7dbYGdvJSsIA4CTI9KZm58eaZX7iYUYBGQ,108
|
|
3
|
+
impit.libs/libgcc_s-98a1ef30.so.1,sha256=XOVRhHznCIpbSdhFoozhla-OfRqBtXftKPQ4cSMKjrs,433441
|
|
4
|
+
impit/__init__.py,sha256=uZsD0XMFOt7QwVUgNcnVQkDF85y9n5slxoxR0wim7ug,1664
|
|
5
|
+
impit/cookies.py,sha256=bfNPkMk2Y0bG-RqqMMJxIfvCMZp_fDclYAnSlsCwkIg,7009
|
|
6
|
+
impit/impit.cpython-313-x86_64-linux-musl.so,sha256=UlbcfPotbcezKVMCglRUniWlPDVXd8go_hfY75MGhBU,14832353
|
|
7
|
+
impit/impit.pyi,sha256=IxLCuSkx3mHQcwcSS65lveoM5QYvMeJcU-Mhhdelyng,45730
|
|
8
|
+
impit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
impit-0.9.0.dist-info/RECORD,,
|
|
Binary file
|
impit-0.2.1.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
impit-0.2.1.dist-info/METADATA,sha256=FhD2ueaHS0b6c14Vi2Z_IGwTpgKz7aJrggdmg98VBoQ,2562
|
|
2
|
-
impit-0.2.1.dist-info/WHEEL,sha256=FhhfBGxpw7lUS1vCU0xMKSc8xZ-17phLfvnaRtwemzY,107
|
|
3
|
-
impit.libs/libgcc_s-1e52349c.so.1,sha256=Ani0u_W_tB8ESsFePO4D2mn2lrgWLhyFdw6RETxBTYM,437537
|
|
4
|
-
impit/__init__.py,sha256=112MOaJD7xu13dnU1b1GHQO41EI61pQE6LdTeojqWqg,400
|
|
5
|
-
impit/impit.pyi,sha256=wPx29TNIyK9xTazeb-KPnXeYrNc0wMZnqSXzYCPPfRc,19775
|
|
6
|
-
impit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
impit/impit.cpython-313-x86_64-linux-musl.so,sha256=NpYA1D-bxIVeNNZ5ruehi1S9Fb_mrUckvjxXMkrYnH0,14520689
|
|
8
|
-
impit-0.2.1.dist-info/RECORD,,
|