alterlab 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alterlab/__init__.py +73 -0
- alterlab/client.py +1332 -0
- alterlab-2.0.0.dist-info/METADATA +375 -0
- alterlab-2.0.0.dist-info/RECORD +5 -0
- alterlab-2.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: alterlab
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Official Python SDK for the AlterLab Web Scraping API - Extract data from any website with intelligent anti-bot bypass
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: web-scraping,scraping,api,data-extraction,anti-bot,javascript-rendering,headless-browser,proxy,alterlab
|
|
7
|
+
Author: AlterLab
|
|
8
|
+
Author-email: dev@alterlab.io
|
|
9
|
+
Requires-Python: >=3.8,<4.0
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Dist: httpx (>=0.24.0)
|
|
26
|
+
Project-URL: Bug Tracker, https://github.com/alterlab/alterlab-python/issues
|
|
27
|
+
Project-URL: Changelog, https://github.com/alterlab/alterlab-python/releases
|
|
28
|
+
Project-URL: Documentation, https://alterlab.io/docs/sdk/python
|
|
29
|
+
Project-URL: Homepage, https://alterlab.io
|
|
30
|
+
Project-URL: Repository, https://github.com/alterlab/alterlab-python
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# AlterLab Python SDK
|
|
34
|
+
|
|
35
|
+
Official Python SDK for the [AlterLab Web Scraping API](https://alterlab.io). Extract data from any website with intelligent anti-bot bypass, JavaScript rendering, and structured extraction.
|
|
36
|
+
|
|
37
|
+
[](https://badge.fury.io/py/alterlab)
|
|
38
|
+
[](https://www.python.org/downloads/)
|
|
39
|
+
[](https://opensource.org/licenses/MIT)
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- **Simple API**: 3 lines of code to scrape any website
|
|
44
|
+
- **Intelligent Anti-Bot Bypass**: Automatic tier escalation (curl → HTTP → stealth → browser)
|
|
45
|
+
- **JavaScript Rendering**: Full Playwright browser for JS-heavy sites
|
|
46
|
+
- **Structured Extraction**: JSON Schema, prompts, and pre-built profiles
|
|
47
|
+
- **BYOP Support**: Bring Your Own Proxy for 20% discount
|
|
48
|
+
- **Async Support**: Native asyncio for concurrent scraping
|
|
49
|
+
- **Type Hints**: Full typing support for IDE autocomplete
|
|
50
|
+
- **Cost Controls**: Set budgets, prefer cost/speed, fail-fast options
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install alterlab
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from alterlab import AlterLab
|
|
62
|
+
|
|
63
|
+
# Initialize client
|
|
64
|
+
client = AlterLab(api_key="sk_live_...") # or set ALTERLAB_API_KEY env var
|
|
65
|
+
|
|
66
|
+
# Scrape a website
|
|
67
|
+
result = client.scrape("https://example.com")
|
|
68
|
+
print(result.text) # Extracted text
|
|
69
|
+
print(result.json) # Structured JSON (Schema.org, metadata)
|
|
70
|
+
print(result.billing.cost_dollars) # Cost breakdown
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Pricing
|
|
74
|
+
|
|
75
|
+
Pay-as-you-go pricing with no subscriptions. **$1 = 5,000 scrapes** (Tier 1).
|
|
76
|
+
|
|
77
|
+
| Tier | Name | Price | Per $1 | Use Case |
|
|
78
|
+
|------|------|-------|--------|----------|
|
|
79
|
+
| 1 | Curl | $0.0002 | 5,000 | Static HTML sites |
|
|
80
|
+
| 2 | HTTP | $0.0003 | 3,333 | Sites with TLS fingerprinting |
|
|
81
|
+
| 3 | Stealth | $0.0005 | 2,000 | Sites with browser checks |
|
|
82
|
+
| 4 | Browser | $0.001 | 1,000 | JS-heavy SPAs |
|
|
83
|
+
| 5 | Captcha | $0.02 | 50 | Sites with CAPTCHAs |
|
|
84
|
+
|
|
85
|
+
The API automatically escalates through tiers until successful, charging only for the tier used.
|
|
86
|
+
|
|
87
|
+
## Usage Examples
|
|
88
|
+
|
|
89
|
+
### Basic Scraping
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from alterlab import AlterLab
|
|
93
|
+
|
|
94
|
+
client = AlterLab(api_key="sk_live_...")
|
|
95
|
+
|
|
96
|
+
# Auto mode - intelligent tier escalation
|
|
97
|
+
result = client.scrape("https://example.com")
|
|
98
|
+
|
|
99
|
+
# Force HTML-only (fastest, cheapest)
|
|
100
|
+
result = client.scrape_html("https://example.com")
|
|
101
|
+
|
|
102
|
+
# JavaScript rendering
|
|
103
|
+
result = client.scrape_js("https://spa-app.com", screenshot=True)
|
|
104
|
+
print(result.screenshot_url)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Structured Extraction
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
# Extract specific fields with JSON Schema
|
|
111
|
+
result = client.scrape(
|
|
112
|
+
"https://store.com/product/123",
|
|
113
|
+
extraction_schema={
|
|
114
|
+
"type": "object",
|
|
115
|
+
"properties": {
|
|
116
|
+
"name": {"type": "string"},
|
|
117
|
+
"price": {"type": "number"},
|
|
118
|
+
"in_stock": {"type": "boolean"}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
print(result.json) # {"name": "...", "price": 29.99, "in_stock": true}
|
|
123
|
+
|
|
124
|
+
# Or use a pre-built profile
|
|
125
|
+
result = client.scrape(
|
|
126
|
+
"https://store.com/product/123",
|
|
127
|
+
extraction_profile="product"
|
|
128
|
+
)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Cost Controls
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from alterlab import AlterLab, CostControls
|
|
135
|
+
|
|
136
|
+
client = AlterLab(api_key="sk_live_...")
|
|
137
|
+
|
|
138
|
+
# Limit to cheap tiers only
|
|
139
|
+
result = client.scrape(
|
|
140
|
+
"https://example.com",
|
|
141
|
+
cost_controls=CostControls(
|
|
142
|
+
max_tier="2", # Don't go above HTTP tier
|
|
143
|
+
prefer_cost=True, # Optimize for lowest cost
|
|
144
|
+
fail_fast=True # Error instead of escalating
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Estimate cost before scraping
|
|
149
|
+
estimate = client.estimate_cost("https://linkedin.com")
|
|
150
|
+
print(f"Estimated: ${estimate.estimated_cost_dollars:.4f}")
|
|
151
|
+
print(f"Confidence: {estimate.confidence}")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Advanced Options
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from alterlab import AlterLab, AdvancedOptions
|
|
158
|
+
|
|
159
|
+
client = AlterLab(api_key="sk_live_...")
|
|
160
|
+
|
|
161
|
+
# Full browser with screenshot and PDF
|
|
162
|
+
result = client.scrape(
|
|
163
|
+
"https://example.com",
|
|
164
|
+
mode="js",
|
|
165
|
+
advanced=AdvancedOptions(
|
|
166
|
+
render_js=True,
|
|
167
|
+
screenshot=True,
|
|
168
|
+
generate_pdf=True,
|
|
169
|
+
markdown=True,
|
|
170
|
+
wait_condition="networkidle"
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
print(result.screenshot_url)
|
|
175
|
+
print(result.pdf_url)
|
|
176
|
+
print(result.markdown_content)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### BYOP (Bring Your Own Proxy)
|
|
180
|
+
|
|
181
|
+
Get 20% discount when using your own proxy:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from alterlab import AlterLab, AdvancedOptions
|
|
185
|
+
|
|
186
|
+
client = AlterLab(api_key="sk_live_...")
|
|
187
|
+
|
|
188
|
+
# Use your configured proxy integration
|
|
189
|
+
result = client.scrape(
|
|
190
|
+
"https://example.com",
|
|
191
|
+
advanced=AdvancedOptions(
|
|
192
|
+
use_own_proxy=True,
|
|
193
|
+
proxy_country="US" # Optional: request specific geo
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
# Check if BYOP was applied
|
|
198
|
+
if result.billing.byop_applied:
|
|
199
|
+
print(f"Saved {result.billing.byop_discount_percent}%!")
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Async Support
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
import asyncio
|
|
206
|
+
from alterlab import AsyncAlterLab
|
|
207
|
+
|
|
208
|
+
async def main():
|
|
209
|
+
async with AsyncAlterLab(api_key="sk_live_...") as client:
|
|
210
|
+
# Single request
|
|
211
|
+
result = await client.scrape("https://example.com")
|
|
212
|
+
|
|
213
|
+
# Concurrent requests
|
|
214
|
+
urls = [
|
|
215
|
+
"https://example.com/page1",
|
|
216
|
+
"https://example.com/page2",
|
|
217
|
+
"https://example.com/page3",
|
|
218
|
+
]
|
|
219
|
+
results = await asyncio.gather(*[client.scrape(url) for url in urls])
|
|
220
|
+
|
|
221
|
+
for r in results:
|
|
222
|
+
print(r.title, r.billing.cost_dollars)
|
|
223
|
+
|
|
224
|
+
asyncio.run(main())
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Caching
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
# Enable caching (opt-in)
|
|
231
|
+
result = client.scrape(
|
|
232
|
+
"https://example.com",
|
|
233
|
+
cache=True, # Enable caching
|
|
234
|
+
cache_ttl=3600, # Cache for 1 hour
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
if result.cached:
|
|
238
|
+
print("Cache hit - no credits charged!")
|
|
239
|
+
|
|
240
|
+
# Force refresh
|
|
241
|
+
result = client.scrape(
|
|
242
|
+
"https://example.com",
|
|
243
|
+
cache=True,
|
|
244
|
+
force_refresh=True # Bypass cache
|
|
245
|
+
)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### PDF and Image Extraction
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
# Extract text from PDF
|
|
252
|
+
result = client.scrape_pdf(
|
|
253
|
+
"https://example.com/document.pdf",
|
|
254
|
+
format="markdown"
|
|
255
|
+
)
|
|
256
|
+
print(result.text)
|
|
257
|
+
|
|
258
|
+
# OCR for images
|
|
259
|
+
result = client.scrape_ocr(
|
|
260
|
+
"https://example.com/image.png",
|
|
261
|
+
language="eng"
|
|
262
|
+
)
|
|
263
|
+
print(result.text)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Error Handling
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from alterlab import (
|
|
270
|
+
AlterLab,
|
|
271
|
+
AuthenticationError,
|
|
272
|
+
InsufficientCreditsError,
|
|
273
|
+
RateLimitError,
|
|
274
|
+
ScrapeError,
|
|
275
|
+
TimeoutError
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
client = AlterLab(api_key="sk_live_...")
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
result = client.scrape("https://example.com")
|
|
282
|
+
except AuthenticationError:
|
|
283
|
+
print("Invalid API key")
|
|
284
|
+
except InsufficientCreditsError:
|
|
285
|
+
print("Please top up your balance")
|
|
286
|
+
except RateLimitError as e:
|
|
287
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
288
|
+
except ScrapeError as e:
|
|
289
|
+
print(f"Scraping failed: {e.message}")
|
|
290
|
+
except TimeoutError:
|
|
291
|
+
print("Request timed out")
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Check Usage & Balance
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
usage = client.get_usage()
|
|
298
|
+
print(f"Balance: ${usage.balance_dollars:.2f}")
|
|
299
|
+
print(f"Used this month: {usage.credits_used_month} credits")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## API Reference
|
|
303
|
+
|
|
304
|
+
### AlterLab Client
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
AlterLab(
|
|
308
|
+
api_key: str = None, # API key (or ALTERLAB_API_KEY env var)
|
|
309
|
+
base_url: str = None, # Custom API URL
|
|
310
|
+
timeout: int = 120, # Request timeout in seconds
|
|
311
|
+
max_retries: int = 3, # Retry count for transient failures
|
|
312
|
+
retry_delay: float = 1.0 # Initial retry delay (exponential backoff)
|
|
313
|
+
)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### scrape() Method
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
client.scrape(
|
|
320
|
+
url: str, # URL to scrape
|
|
321
|
+
mode: str = "auto", # "auto", "html", "js", "pdf", "ocr"
|
|
322
|
+
sync: bool = True, # Wait for result vs return job ID
|
|
323
|
+
advanced: AdvancedOptions, # Advanced scraping options
|
|
324
|
+
cost_controls: CostControls, # Budget and optimization settings
|
|
325
|
+
cache: bool = False, # Enable response caching
|
|
326
|
+
cache_ttl: int = None, # Cache TTL in seconds (60-86400)
|
|
327
|
+
formats: list = None, # Output formats: ["text", "json", "html", "markdown"]
|
|
328
|
+
extraction_schema: dict, # JSON Schema for structured extraction
|
|
329
|
+
extraction_prompt: str, # Natural language extraction instructions
|
|
330
|
+
extraction_profile: str, # Pre-built profile: "product", "article", etc.
|
|
331
|
+
wait_for: str = None, # CSS selector to wait for (JS mode)
|
|
332
|
+
screenshot: bool = False, # Capture screenshot (JS mode)
|
|
333
|
+
) -> ScrapeResult
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### ScrapeResult
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
result.url # Scraped URL
|
|
340
|
+
result.status_code # HTTP status
|
|
341
|
+
result.text # Extracted text content
|
|
342
|
+
result.html # HTML content
|
|
343
|
+
result.json # Structured JSON content
|
|
344
|
+
result.title # Page title
|
|
345
|
+
result.author # Author (if detected)
|
|
346
|
+
result.billing # BillingDetails object
|
|
347
|
+
result.billing.tier_used # Tier that succeeded
|
|
348
|
+
result.billing.cost_dollars # Final cost in USD
|
|
349
|
+
result.screenshot_url # Screenshot URL (if requested)
|
|
350
|
+
result.pdf_url # PDF URL (if requested)
|
|
351
|
+
result.cached # Whether result was from cache
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Environment Variables
|
|
355
|
+
|
|
356
|
+
| Variable | Description |
|
|
357
|
+
|----------|-------------|
|
|
358
|
+
| `ALTERLAB_API_KEY` | Your API key (alternative to passing in constructor) |
|
|
359
|
+
|
|
360
|
+
## Requirements
|
|
361
|
+
|
|
362
|
+
- Python 3.8+
|
|
363
|
+
- httpx >= 0.24.0
|
|
364
|
+
|
|
365
|
+
## Support
|
|
366
|
+
|
|
367
|
+
- **Documentation**: [https://alterlab.io/docs](https://alterlab.io/docs)
|
|
368
|
+
- **API Status**: [https://status.alterlab.io](https://status.alterlab.io)
|
|
369
|
+
- **Support**: support@alterlab.io
|
|
370
|
+
- **Issues**: [GitHub Issues](https://github.com/alterlab/alterlab-python/issues)
|
|
371
|
+
|
|
372
|
+
## License
|
|
373
|
+
|
|
374
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
375
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
alterlab/__init__.py,sha256=YrOb55dxqsQATdcmUdulBGG2iE2ZuLmD2qceCrh12Xo,1471
|
|
2
|
+
alterlab/client.py,sha256=qcbz4KwsxloUpvNVeRfPU0I4NNy_2SeXNC-fgoc3LTY,45436
|
|
3
|
+
alterlab-2.0.0.dist-info/METADATA,sha256=Q8uqV2fRkiBotnIirlICX2BdSQfVGPR965QI3oByKJQ,10903
|
|
4
|
+
alterlab-2.0.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
5
|
+
alterlab-2.0.0.dist-info/RECORD,,
|