bas-http 1.0.0__tar.gz
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.
- bas_http-1.0.0/PKG-INFO +25 -0
- bas_http-1.0.0/bas/README.md +244 -0
- bas_http-1.0.0/bas/__init__.py +97 -0
- bas_http-1.0.0/bas/_curl.py +393 -0
- bas_http-1.0.0/bas/async_session.py +352 -0
- bas_http-1.0.0/bas/cookies.py +643 -0
- bas_http-1.0.0/bas/curl_parser.py +267 -0
- bas_http-1.0.0/bas/impersonate.py +424 -0
- bas_http-1.0.0/bas/models.py +339 -0
- bas_http-1.0.0/bas/session.py +648 -0
- bas_http-1.0.0/bas/simple_session.py +299 -0
- bas_http-1.0.0/bas/tests/__init__.py +0 -0
- bas_http-1.0.0/bas/tests/test_cookies.py +778 -0
- bas_http-1.0.0/bas/tests/test_curl_parser.py +172 -0
- bas_http-1.0.0/bas/tests/test_real_request.py +142 -0
- bas_http-1.0.0/bas/tests/test_spaceshooter.py +213 -0
- bas_http-1.0.0/bas_http.egg-info/PKG-INFO +25 -0
- bas_http-1.0.0/bas_http.egg-info/SOURCES.txt +20 -0
- bas_http-1.0.0/bas_http.egg-info/dependency_links.txt +1 -0
- bas_http-1.0.0/bas_http.egg-info/top_level.txt +1 -0
- bas_http-1.0.0/pyproject.toml +42 -0
- bas_http-1.0.0/setup.cfg +4 -0
bas_http-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bas-http
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Copy-paste your browser's request, bas handles the rest. No impersonation needed.
|
|
5
|
+
Author: bas
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/bas
|
|
8
|
+
Project-URL: Repository, https://github.com/yourusername/bas
|
|
9
|
+
Project-URL: Issues, https://github.com/yourusername/bas/issues
|
|
10
|
+
Keywords: http,curl,cookie,scraping,browser,devtools
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# bas
|
|
2
|
+
|
|
3
|
+
Copy-paste your browser's request, bas handles the rest.
|
|
4
|
+
|
|
5
|
+
No impersonation. No fingerprint chasing. No broken cookies.
|
|
6
|
+
|
|
7
|
+
## Why bas?
|
|
8
|
+
|
|
9
|
+
| Issue | curlcffi | bas |
|
|
10
|
+
|-------|----------|----------|
|
|
11
|
+
| Cookies lost during redirects | **BUG** | **Fixed** |
|
|
12
|
+
| Cookies not accumulating across requests | **BUG** | **Fixed** |
|
|
13
|
+
| Manual Cookie header construction needed | Yes | **Auto-injected** |
|
|
14
|
+
| Requires curl-impersonate | Yes | **No — pure Python** |
|
|
15
|
+
| Browser fingerprint updates needed | Yes | **None — you bring your own headers** |
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install bas
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Zero external dependencies. Works with Python's built-in `urllib`.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Method 1: Paste a curl command from DevTools
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
1. Open browser DevTools → Network tab
|
|
31
|
+
2. Right-click any request → "Copy as cURL"
|
|
32
|
+
3. Paste into bas
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import bas
|
|
37
|
+
|
|
38
|
+
# Paste your curl command (use raw string r'' to preserve backslashes)
|
|
39
|
+
s = bas.from_curl(r'''curl "https://spaceshooter.net/faucet/ltc" ^
|
|
40
|
+
-H "accept: text/html" ^
|
|
41
|
+
-H "user-agent: Mozilla/5.0 ..." ^
|
|
42
|
+
-b "cf_clearance=abc123; session=xyz"''')
|
|
43
|
+
|
|
44
|
+
# Now make requests — cookies and headers auto-injected
|
|
45
|
+
r = s.get("https://spaceshooter.net/faucet/ltc")
|
|
46
|
+
print(r.status_code, r.text)
|
|
47
|
+
|
|
48
|
+
# Add more cookies manually if needed
|
|
49
|
+
s.set_cookie("new_cookie", "value", domain="spaceshooter.net")
|
|
50
|
+
r = s.get("https://spaceshooter.net/dashboard")
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Method 2: Build headers manually
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import bas
|
|
57
|
+
|
|
58
|
+
s = bas.Session()
|
|
59
|
+
|
|
60
|
+
# Paste your headers from DevTools
|
|
61
|
+
s.headers = {
|
|
62
|
+
"accept": "text/html,application/xhtml+xml,...",
|
|
63
|
+
"accept-language": "en-US,en;q=0.9",
|
|
64
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
|
|
65
|
+
"sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
|
|
66
|
+
"sec-ch-ua-mobile": "?0",
|
|
67
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
68
|
+
"sec-fetch-dest": "document",
|
|
69
|
+
"sec-fetch-mode": "navigate",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Add your cookies
|
|
73
|
+
s.set_cookie("cf_clearance", "abc123", domain="spaceshooter.net")
|
|
74
|
+
s.set_cookie("session", "xyz", domain="spaceshooter.net")
|
|
75
|
+
|
|
76
|
+
# Go!
|
|
77
|
+
r = s.get("https://spaceshooter.net/faucet/ltc")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Method 3: Build from headers + cookies dict
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import bas
|
|
84
|
+
|
|
85
|
+
s = bas.from_headers(
|
|
86
|
+
url="https://spaceshooter.net/faucet/ltc",
|
|
87
|
+
headers={
|
|
88
|
+
"user-agent": "Mozilla/5.0 ...",
|
|
89
|
+
"accept": "text/html,...",
|
|
90
|
+
},
|
|
91
|
+
cookies={
|
|
92
|
+
"cf_clearance": "abc123",
|
|
93
|
+
"session": "xyz",
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
r = s.get("https://spaceshooter.net/faucet/ltc")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Parse a curl command without making a request
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from bas.curl_parser import parse_curl, print_curl_summary
|
|
104
|
+
|
|
105
|
+
# See what's in a curl command
|
|
106
|
+
print_curl_summary(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
|
|
107
|
+
|
|
108
|
+
# Or get it as a dict
|
|
109
|
+
parsed = parse_curl(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
|
|
110
|
+
print(parsed["method"]) # "GET"
|
|
111
|
+
print(parsed["url"]) # "https://example.com"
|
|
112
|
+
print(parsed["headers"]) # {"User-Agent": "..."}
|
|
113
|
+
print(parsed["cookies"]) # {"cookie": "value"}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Cookie Management
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
import bas
|
|
120
|
+
|
|
121
|
+
with bas.Session() as s:
|
|
122
|
+
# Pre-load cookies
|
|
123
|
+
s.set_cookie("session", "abc123", domain="example.com")
|
|
124
|
+
|
|
125
|
+
# Cookies are auto-injected into every request
|
|
126
|
+
r = s.get("https://example.com")
|
|
127
|
+
print("Cookie header:", r.request.headers.get("Cookie"))
|
|
128
|
+
|
|
129
|
+
# Save cookies to file
|
|
130
|
+
s.save_cookies("cookies.json")
|
|
131
|
+
|
|
132
|
+
# Load cookies from file
|
|
133
|
+
s.load_cookies("cookies.json")
|
|
134
|
+
|
|
135
|
+
# Check what cookies we have for a URL
|
|
136
|
+
print(s.get_cookies("https://example.com"))
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Cookie Jar (direct access)
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from bas.cookies import Cookie, CookieJar
|
|
143
|
+
|
|
144
|
+
jar = CookieJar()
|
|
145
|
+
|
|
146
|
+
# Parse Set-Cookie headers from responses
|
|
147
|
+
jar.parse_set_cookie(
|
|
148
|
+
"session=abc123; Domain=.example.com; Path=/; Secure; HttpOnly",
|
|
149
|
+
"https://www.example.com/"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Get cookies for a URL (RFC 6265 compliant)
|
|
153
|
+
cookies = jar.match("https://www.example.com/page")
|
|
154
|
+
|
|
155
|
+
# Build Cookie header automatically
|
|
156
|
+
header = jar.to_header("https://www.example.com/")
|
|
157
|
+
print(header) # "session=abc123"
|
|
158
|
+
|
|
159
|
+
# Save/load
|
|
160
|
+
jar.save_json("cookies.json")
|
|
161
|
+
jar.load_json("cookies.json")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## How bas Fixes curlcffi's Cookie Bugs
|
|
165
|
+
|
|
166
|
+
### Fix #1: Cookies Accumulate
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# curlcffi (BROKEN):
|
|
170
|
+
r1 = s.get(url)
|
|
171
|
+
r2 = s.get(url) # LOST cookies from r1!
|
|
172
|
+
|
|
173
|
+
# bas (FIXED):
|
|
174
|
+
r1 = s.get(url)
|
|
175
|
+
r2 = s.get(url) # Has ALL cookies from r1 + new ones
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Fix #2: Redirect Cookie Inheritance
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
# curlcffi (BROKEN):
|
|
182
|
+
r = s.get("https://example.com/page") # 302 redirect
|
|
183
|
+
# Cookies LOST during redirect!
|
|
184
|
+
|
|
185
|
+
# bas (FIXED):
|
|
186
|
+
r = s.get("https://example.com/page") # 302 redirect
|
|
187
|
+
# Cookies properly inherited + new Set-Cookie headers captured
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Fix #3: Auto Cookie Injection
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
# curlcffi (manual):
|
|
194
|
+
r = s.get(url, headers={"Cookie": "session=abc123; token=xyz"})
|
|
195
|
+
|
|
196
|
+
# bas (automatic):
|
|
197
|
+
s.set_cookie("session", "abc123")
|
|
198
|
+
s.set_cookie("token", "xyz")
|
|
199
|
+
r = s.get(url) # Cookie header auto-generated!
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## API Reference
|
|
203
|
+
|
|
204
|
+
### `from_curl(curl_cmd, **kwargs)` → Session
|
|
205
|
+
|
|
206
|
+
Create a Session from a curl command. The main entry point.
|
|
207
|
+
|
|
208
|
+
### `from_headers(url, headers, cookies, **kwargs)` → Session
|
|
209
|
+
|
|
210
|
+
Create a Session from raw headers and cookies.
|
|
211
|
+
|
|
212
|
+
### `Session(headers=None, cookies=None, ...)`
|
|
213
|
+
|
|
214
|
+
Simple HTTP session. Pure Python, no curl needed.
|
|
215
|
+
|
|
216
|
+
**Parameters:**
|
|
217
|
+
- `headers` (dict): Default headers
|
|
218
|
+
- `cookies` (dict): Pre-set cookies
|
|
219
|
+
- `verify` (bool): SSL verification (default: True)
|
|
220
|
+
- `timeout` (float): Request timeout (default: 30)
|
|
221
|
+
- `allow_redirects` (bool): Follow redirects (default: True)
|
|
222
|
+
- `max_redirects` (int): Max redirects (default: 20)
|
|
223
|
+
|
|
224
|
+
**Methods:**
|
|
225
|
+
- `get(url, **kwargs)` → Response
|
|
226
|
+
- `post(url, **kwargs)` → Response
|
|
227
|
+
- `put(url, **kwargs)` → Response
|
|
228
|
+
- `delete(url, **kwargs)` → Response
|
|
229
|
+
- `set_cookie(name, value, domain, path)` → None
|
|
230
|
+
- `get_cookies(url)` → dict
|
|
231
|
+
- `save_cookies(filepath, format)` → None
|
|
232
|
+
- `load_cookies(filepath, format)` → None
|
|
233
|
+
|
|
234
|
+
### `Cookie(name, value, domain, path, ...)`
|
|
235
|
+
|
|
236
|
+
Individual cookie with RFC 6265 compliance.
|
|
237
|
+
|
|
238
|
+
### `CookieJar`
|
|
239
|
+
|
|
240
|
+
Thread-safe cookie container. Full RFC 6265 domain/path matching.
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
MIT
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
bas — Copy-paste your browser's request, bas handles the rest.
|
|
3
|
+
|
|
4
|
+
No impersonation needed. No fingerprint chasing. Just grab your
|
|
5
|
+
headers + cookies from DevTools and go.
|
|
6
|
+
|
|
7
|
+
Workflow:
|
|
8
|
+
1. Open browser DevTools -> Network tab
|
|
9
|
+
2. Right-click request -> "Copy as cURL"
|
|
10
|
+
3. Paste into bas
|
|
11
|
+
|
|
12
|
+
>>> import bas
|
|
13
|
+
>>> s = bas.from_curl('''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
|
|
14
|
+
>>> r = s.get("https://example.com/other-page")
|
|
15
|
+
>>> print(r.status_code, r.text)
|
|
16
|
+
|
|
17
|
+
Or manually:
|
|
18
|
+
|
|
19
|
+
>>> s = bas.Session()
|
|
20
|
+
>>> s.headers["User-Agent"] = "Mozilla/5.0 ..."
|
|
21
|
+
>>> s.set_cookie("session", "abc123", domain="example.com")
|
|
22
|
+
>>> r = s.get("https://example.com/page")
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import Optional
|
|
26
|
+
from .simple_session import Session
|
|
27
|
+
from .curl_parser import parse_curl, curl_to_session, print_curl_summary
|
|
28
|
+
from .cookies import Cookie, CookieJar
|
|
29
|
+
from .models import Headers, PreparedRequest, Response, HTTPError
|
|
30
|
+
|
|
31
|
+
__version__ = "1.0.0"
|
|
32
|
+
__author__ = "bas"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def from_curl(curl_cmd: str, **kwargs) -> Session:
|
|
36
|
+
"""
|
|
37
|
+
Create a Session from a curl command.
|
|
38
|
+
|
|
39
|
+
This is the main entry point. Copy a curl command from your browser
|
|
40
|
+
and bas parses all headers, cookies, and settings from it.
|
|
41
|
+
|
|
42
|
+
Usage:
|
|
43
|
+
>>> s = bas.from_curl('''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
|
|
44
|
+
>>> r = s.get("https://example.com/page")
|
|
45
|
+
"""
|
|
46
|
+
return curl_to_session(curl_cmd, **kwargs)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def from_headers(
|
|
50
|
+
url: str,
|
|
51
|
+
headers: dict,
|
|
52
|
+
cookies: Optional[dict] = None,
|
|
53
|
+
**kwargs,
|
|
54
|
+
) -> Session:
|
|
55
|
+
"""
|
|
56
|
+
Create a Session from raw headers and cookies.
|
|
57
|
+
|
|
58
|
+
Use this when you have headers/cookies from browser DevTools
|
|
59
|
+
but not a curl command.
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
>>> s = bas.from_headers(
|
|
63
|
+
... url="https://example.com",
|
|
64
|
+
... headers={"User-Agent": "Mozilla/5.0 ...", ...},
|
|
65
|
+
... cookies={"session": "abc123", ...},
|
|
66
|
+
... )
|
|
67
|
+
>>> r = s.get("https://example.com/page")
|
|
68
|
+
"""
|
|
69
|
+
s = Session(**kwargs)
|
|
70
|
+
s.headers.update(headers)
|
|
71
|
+
|
|
72
|
+
if cookies:
|
|
73
|
+
from urllib.parse import urlparse
|
|
74
|
+
domain = urlparse(url).hostname or ""
|
|
75
|
+
for name, value in cookies.items():
|
|
76
|
+
s.set_cookie(name, value, domain=domain, path="/")
|
|
77
|
+
|
|
78
|
+
return s
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
__all__ = [
|
|
82
|
+
# Core
|
|
83
|
+
"Session",
|
|
84
|
+
"Cookie",
|
|
85
|
+
"CookieJar",
|
|
86
|
+
"Headers",
|
|
87
|
+
"PreparedRequest",
|
|
88
|
+
"Response",
|
|
89
|
+
"HTTPError",
|
|
90
|
+
# Quick start
|
|
91
|
+
"from_curl",
|
|
92
|
+
"from_headers",
|
|
93
|
+
# Parser
|
|
94
|
+
"parse_curl",
|
|
95
|
+
"curl_to_session",
|
|
96
|
+
"print_curl_summary",
|
|
97
|
+
]
|