dataapp_crawler 0.1.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.
- dataapp_crawler/__init__.py +87 -0
- dataapp_crawler/__main__.py +35 -0
- dataapp_crawler/_logging.py +148 -0
- dataapp_crawler/_network_diagnostics.py +193 -0
- dataapp_crawler/browser_tools/__init__.py +23 -0
- dataapp_crawler/browser_tools/_common.py +66 -0
- dataapp_crawler/browser_tools/dom.py +49 -0
- dataapp_crawler/browser_tools/lists.py +125 -0
- dataapp_crawler/browser_tools/navigation.py +243 -0
- dataapp_crawler/browser_tools/readiness.py +42 -0
- dataapp_crawler/captcha.py +912 -0
- dataapp_crawler/checkpoint.py +487 -0
- dataapp_crawler/cloudflare.py +523 -0
- dataapp_crawler/context.py +231 -0
- dataapp_crawler/cookies.py +207 -0
- dataapp_crawler/crawler.py +1392 -0
- dataapp_crawler/errors.py +184 -0
- dataapp_crawler/http.py +223 -0
- dataapp_crawler/images.py +267 -0
- dataapp_crawler/local_auth.py +77 -0
- dataapp_crawler/models.py +285 -0
- dataapp_crawler/pagination.py +172 -0
- dataapp_crawler/parsing.py +13 -0
- dataapp_crawler/providers/__init__.py +20 -0
- dataapp_crawler/providers/browser.py +292 -0
- dataapp_crawler/providers/camoufox.py +184 -0
- dataapp_crawler/providers/dataos.py +174 -0
- dataapp_crawler/providers/pool.py +451 -0
- dataapp_crawler/providers/proxy.py +209 -0
- dataapp_crawler/py.typed +0 -0
- dataapp_crawler/routing.py +67 -0
- dataapp_crawler/scheduling.py +425 -0
- dataapp_crawler/sessions.py +735 -0
- dataapp_crawler-0.1.0.dist-info/METADATA +211 -0
- dataapp_crawler-0.1.0.dist-info/RECORD +36 -0
- dataapp_crawler-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dataapp_crawler
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Composable HTTP, browser and proxy acquisition SDK for DataOS DataApps
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: httpx<1,>=0.28
|
|
7
|
+
Provides-Extra: browser
|
|
8
|
+
Requires-Dist: playwright<2,>=1.64; extra == 'browser'
|
|
9
|
+
Provides-Extra: fingerprint
|
|
10
|
+
Requires-Dist: camoufox<0.6,>=0.5.9; extra == 'fingerprint'
|
|
11
|
+
Provides-Extra: parse
|
|
12
|
+
Requires-Dist: parsel<2,>=1.9; extra == 'parse'
|
|
13
|
+
Provides-Extra: resource-proxy
|
|
14
|
+
Requires-Dist: dataapp-resource-proxy<0.2,>=0.1.0; extra == 'resource-proxy'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Data App Crawler core
|
|
18
|
+
|
|
19
|
+
`dataapp-crawler` (imported as `dataapp_crawler`) is the provider-neutral website
|
|
20
|
+
acquisition library for Python 3.11+. It can run independently of the DataApp
|
|
21
|
+
Runtime. Core includes generic CDP/Playwright providers and an optional Camoufox
|
|
22
|
+
adapter, but no Relay- or AWS-specific provider.
|
|
23
|
+
|
|
24
|
+
> Dependency versions are defined in each package's `pyproject.toml`; an
|
|
25
|
+
> application uses its own `uv.lock`. Local source changes are not delivered to
|
|
26
|
+
> applications until matching packages and browser images are released and the
|
|
27
|
+
> application's lock is updated.
|
|
28
|
+
|
|
29
|
+
## Basic acquisition
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import asyncio
|
|
33
|
+
|
|
34
|
+
from dataapp_crawler import Crawler
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def main() -> None:
|
|
38
|
+
async with Crawler() as crawler:
|
|
39
|
+
capture = await crawler.fetch("https://example.com")
|
|
40
|
+
print(capture.status)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
asyncio.run(main())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Use an ordinary synchronous or asynchronous `process(capture, context)` function
|
|
47
|
+
for extraction, OCR/ASR calls, model extraction, or custom logic. The SDK does
|
|
48
|
+
not own a processing engine or Dataset:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
async def process(capture, context):
|
|
52
|
+
heading = capture.page.locator("h1").first
|
|
53
|
+
await context.run(heading.wait_for(state="visible"), timeout=10)
|
|
54
|
+
return {"title": (await context.run(heading.inner_text(), timeout=5)).strip()}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
An operation timeout raises `TimeoutError`; exhausting `Limits.max_duration`
|
|
58
|
+
raises `LimitReached`. Diagnostics are bounded and omit raw credentials, headers,
|
|
59
|
+
connection URLs, response bodies, and arbitrary upstream exception text.
|
|
60
|
+
|
|
61
|
+
## Page-driven flows
|
|
62
|
+
|
|
63
|
+
`Crawler.run_browser(collect)` hands one native Playwright `Page` and a
|
|
64
|
+
`CrawlContext` to ordinary application code. A single collection function can
|
|
65
|
+
submit a search, traverse list pages, open detail pages, and stop on business
|
|
66
|
+
conditions. It does not require one URL per `Crawler` object.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from dataapp_crawler import Crawler, Limits
|
|
70
|
+
from dataapp_crawler.browser_tools import open_url, text_of
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def collect(page, context):
|
|
74
|
+
await open_url(page, "https://example.com", context)
|
|
75
|
+
return {"title": await text_of(page.locator("h1"), context, required=True)}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async with Crawler(browser=browser_provider, limits=Limits(max_duration=120)) as crawler:
|
|
79
|
+
record = await crawler.run_browser(collect)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`browser_tools` includes bounded navigation and extraction helpers such as
|
|
83
|
+
`open_url`, `new_page`, `popup_page`, `same_tab`, `wait_until`, `text_of`,
|
|
84
|
+
`attribute_of`, and `iterate_list`. Page and Locator remain native Playwright
|
|
85
|
+
objects. Raw Playwright actions remain available, but they do not automatically
|
|
86
|
+
gain Crawler accounting, scheduling, diagnostics, or retry semantics.
|
|
87
|
+
|
|
88
|
+
The application defines selectors, stable business keys, pagination, readiness,
|
|
89
|
+
and side-effect boundaries. Do not automatically retry clicks or submissions
|
|
90
|
+
that can create duplicate actions.
|
|
91
|
+
|
|
92
|
+
Failure summaries include a fixed `exception_type` without its message. Browser
|
|
93
|
+
flow failures also report `root_host`, the root Page's hostname only. This may be
|
|
94
|
+
the list page rather than a failing detail tab. The original exception still
|
|
95
|
+
propagates, with cancellation and cleanup unchanged. A failure before the root
|
|
96
|
+
Page exists reports `phase=page_creation`.
|
|
97
|
+
|
|
98
|
+
To inspect a failing detail page, explicitly capture a bounded screenshot inside
|
|
99
|
+
that page's scope before it closes. Keep artifacts private, outside uploaded
|
|
100
|
+
source packages and logs. `diagnostics_dir` applies to request-driven captures;
|
|
101
|
+
it does not automatically snapshot every native Page action in `run_browser`.
|
|
102
|
+
An exception type or screenshot does not by itself establish whether the target
|
|
103
|
+
site blocked access, returned a different layout, or had not finished loading.
|
|
104
|
+
|
|
105
|
+
## Read-only CAPTCHA observations
|
|
106
|
+
|
|
107
|
+
`detect_captchas(html, ...)` and `await detect_captchas_on_page(page, ...)` classify
|
|
108
|
+
observable reCAPTCHA v2/Enterprise, Turnstile, Cloudflare full-page Challenge,
|
|
109
|
+
BotDetect, and image-CAPTCHA
|
|
110
|
+
integrations. Results contain fixed evidence codes, component matches, and missing
|
|
111
|
+
inputs. Script-only or unsupported evidence remains `unknown`; a form widget is
|
|
112
|
+
distinct from an access challenge. `CaptchaOverride` accepts explicit site knowledge.
|
|
113
|
+
|
|
114
|
+
Detection does not navigate, click, capture images, submit forms, or contact paid
|
|
115
|
+
services. A solver match is structural readiness, not authorization or proof of
|
|
116
|
+
website acceptance. `capture_image(locator)` separately captures the currently
|
|
117
|
+
rendered challenge for an explicitly chosen resource call. Keep submission and
|
|
118
|
+
site acceptance checks in ordinary application code.
|
|
119
|
+
|
|
120
|
+
The candidate `CloudflareChallengeCapture` API observes dynamic full-page
|
|
121
|
+
render parameters when installed before navigation, and applies a managed
|
|
122
|
+
token only to the same active challenge. See the [token workflow](docs/cloudflare-challenge.md).
|
|
123
|
+
The local contract and browser fixture have passed; real-site managed solving
|
|
124
|
+
and deployment acceptance remain pending. Ordinary Turnstile and full-page
|
|
125
|
+
Challenge tasks have different required fields. Detection alone never enables
|
|
126
|
+
a paid call, and the `cf_clearance` cookie mode is not exposed.
|
|
127
|
+
|
|
128
|
+
See the source template's [API reference](../../examples/crawler/relay_template/help/docs/API.zh-CN.md)
|
|
129
|
+
for parameters and the Runtime/resource boundary.
|
|
130
|
+
|
|
131
|
+
## Provider extensions
|
|
132
|
+
|
|
133
|
+
Supplier-specific provider implementations are separate distributions:
|
|
134
|
+
|
|
135
|
+
| Distribution | Import | Public provider types |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| `dataapp-crawler-octoparse-browser` | `dataapp_crawler_octoparse` | `BrowserEnvRelay`, `RelayBrowser` |
|
|
138
|
+
| `dataapp-crawler-agentcore` | `dataapp_crawler_agentcore` | `AgentCoreBrowser`, `AWSBrowser` |
|
|
139
|
+
|
|
140
|
+
The core wheel retains `CDPBrowser`, `PlaywrightBrowser`, and optional
|
|
141
|
+
`CamoufoxBrowser` as generic integrations. It does not contain either supplier
|
|
142
|
+
implementation and does not depend on Relay or boto3. Applications install one
|
|
143
|
+
supplier extension deliberately and import its provider from that extension.
|
|
144
|
+
This makes an accidental supplier-provider import fail at project-install time
|
|
145
|
+
instead of silently exposing an unsupported runtime path.
|
|
146
|
+
|
|
147
|
+
Source packages may share this repository; that does not merge their wheel
|
|
148
|
+
contents, dependency graphs, project templates, or base images.
|
|
149
|
+
|
|
150
|
+
## Proxy boundary
|
|
151
|
+
|
|
152
|
+
Browser and proxy selection are independent. Core provides the common proxy
|
|
153
|
+
contract and supports:
|
|
154
|
+
|
|
155
|
+
- direct access with `proxy=None`;
|
|
156
|
+
- a developer-provided `StaticProxy`;
|
|
157
|
+
- a managed `DataOSProxy` through the Resource Proxy client;
|
|
158
|
+
- a custom `ProxyProvider.acquire()` returning a `ProxyHandle`.
|
|
159
|
+
|
|
160
|
+
`DataOSProxy.proxy_group` accepts `auto`, `domestic_basic`,
|
|
161
|
+
`overseas_basic`, or `overseas_enhanced`. `auto` uses the basic managed
|
|
162
|
+
service. The SDK does not select or name proxy suppliers: Resource Proxy owns
|
|
163
|
+
the published supplier order and validates location and duration support.
|
|
164
|
+
|
|
165
|
+
If managed proxy acquisition fails, `DataOSProxy` raises `ConfigurationError`
|
|
166
|
+
with bounded `code` and `status_code` attributes. These diagnostics contain only
|
|
167
|
+
the documented resource error class and HTTP status; supplier responses, proxy
|
|
168
|
+
URLs, and credentials are never retained in the exception.
|
|
169
|
+
|
|
170
|
+
A proxy handle is tied to a session lifecycle. It is not a promise to rotate the
|
|
171
|
+
exit IP on every request. Credentials must remain outside inputs, source files,
|
|
172
|
+
logs, and build arguments.
|
|
173
|
+
|
|
174
|
+
## DataApp integration
|
|
175
|
+
|
|
176
|
+
Within a Data App, use `DataAppCrawler` from
|
|
177
|
+
`bazhuayu_dataapp.plugins.crawler`:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from bazhuayu_dataapp import DataApp
|
|
181
|
+
from bazhuayu_dataapp.plugins.crawler import DataAppCrawler
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async with DataApp:
|
|
185
|
+
async with DataAppCrawler(browser=browser_provider) as crawler:
|
|
186
|
+
records = await crawler.run_browser(collect)
|
|
187
|
+
await DataApp.push_data(records)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Runtime owns Run input, Dataset writes, progress, cancellation, and final state.
|
|
191
|
+
`DataAppCrawler` only joins Crawler cancellation and cleanup to that lifecycle.
|
|
192
|
+
|
|
193
|
+
## Logging
|
|
194
|
+
|
|
195
|
+
The standalone Crawler writes INFO stage logs to stderr by default. The DataApp
|
|
196
|
+
adapter uses `DataApp.log`, so the platform receives the same bounded stages.
|
|
197
|
+
Pass a standard logger to change verbosity:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import logging
|
|
201
|
+
|
|
202
|
+
logger = logging.getLogger("my_app.crawler")
|
|
203
|
+
logger.setLevel(logging.WARNING)
|
|
204
|
+
|
|
205
|
+
async with Crawler(logger=logger) as crawler:
|
|
206
|
+
capture = await crawler.fetch(target_url)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The SDK does not configure the root logger and never logs the value returned by a
|
|
210
|
+
process callback. Acquisition success is not Dataset success; the application
|
|
211
|
+
must push records explicitly and validate the Runtime outcome separately.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
dataapp_crawler/__init__.py,sha256=OTt7M2zw3Idh1q5faFWBHoTsth1vexEW0eCC97s4BRs,2204
|
|
2
|
+
dataapp_crawler/__main__.py,sha256=t7XCXtBYjYbAEJ8IPYHn6vksltGKStvU9WqzcAg2Av8,1136
|
|
3
|
+
dataapp_crawler/_logging.py,sha256=opVU_5vMTkpatuNB5wFbNhboy3aR0D-ncIKt5NQgtzM,5522
|
|
4
|
+
dataapp_crawler/_network_diagnostics.py,sha256=zr8JkLY5t7tts1-XpOPwOzk-4fn6oRDCddEz6gCnX0M,6270
|
|
5
|
+
dataapp_crawler/captcha.py,sha256=DBY0gjo6xngyTBFivsC078836Q7UXCv7xeOGpcmLhwM,33763
|
|
6
|
+
dataapp_crawler/checkpoint.py,sha256=1LO3pLF0hdenYas7zwgEhVraydCjrwOGWueK9xmQY-E,18104
|
|
7
|
+
dataapp_crawler/cloudflare.py,sha256=ee7aASD6zSU7pGmle2xBN0J6xnxwyEdrOqKBFIjGMIw,20888
|
|
8
|
+
dataapp_crawler/context.py,sha256=skW6-9TBUCT5EeZP5WsUJXHrsHIdEBDNv_Osa-NcTmA,9370
|
|
9
|
+
dataapp_crawler/cookies.py,sha256=8abIokM5jeIR7QoKxNhsEoKX3dTiXNdwfz6yBHdSeco,6938
|
|
10
|
+
dataapp_crawler/crawler.py,sha256=mkokLOq2mXlJNUhjbfGXMAESn1nYp6wXKAzYwAWmHkA,63818
|
|
11
|
+
dataapp_crawler/errors.py,sha256=y4Jx21weWZonMV62iOwSN__0FSzJxBPA0M8jvAEUPYU,6351
|
|
12
|
+
dataapp_crawler/http.py,sha256=pHmqhAhLbaa9FkEdV7ZIa9ZIPRpVGYolGuhlVE7_9c4,9621
|
|
13
|
+
dataapp_crawler/images.py,sha256=Z5v1pxYHL7nq16DjG8Ih1OUkfjmmBV-sG5qDuvn64XQ,10744
|
|
14
|
+
dataapp_crawler/local_auth.py,sha256=7ZqdCFhSj_P3BqZzHpfiObMcdjxLMfQaObGCmzWbInE,2762
|
|
15
|
+
dataapp_crawler/models.py,sha256=6IGtKimFwJQznj9X1E9Osboz8HvCDGRft09ds63iTdA,11124
|
|
16
|
+
dataapp_crawler/pagination.py,sha256=IqpAr5bK9vqUqdX3we8jOI5RLpkct1YHAQyO_FhJEuk,6675
|
|
17
|
+
dataapp_crawler/parsing.py,sha256=dyTJaPiezNRih8PVF__fY1zjyx0cbcXzWjUgtesmr5s,361
|
|
18
|
+
dataapp_crawler/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
dataapp_crawler/routing.py,sha256=zfoJo23c5ybPc9OQZlNyA-cKn-8gpF3gvVvdiWU0yrA,2502
|
|
20
|
+
dataapp_crawler/scheduling.py,sha256=o-trbcnwr-am4IkK8fm3P2kchLb4LKoHfiHXJ2suqNU,17103
|
|
21
|
+
dataapp_crawler/sessions.py,sha256=J_WNE800abJYoP7K-GnhbvnpVMixx-3yAZq2fcmngoE,29173
|
|
22
|
+
dataapp_crawler/browser_tools/__init__.py,sha256=K6qSh7wQjsTde0ae31XpdQsQ5zykzVp-LRDiNrfuPTc,645
|
|
23
|
+
dataapp_crawler/browser_tools/_common.py,sha256=py6Z-VMgCc1sb-kayjAMQO8fGUWuKfQgeTU37bcKCjs,1922
|
|
24
|
+
dataapp_crawler/browser_tools/dom.py,sha256=LjACkzOsSjmFnixz2Wa1ZxgoQH9RFKMj5iZBr4Lm2uY,1442
|
|
25
|
+
dataapp_crawler/browser_tools/lists.py,sha256=6o9W9SgBXqDBS2xNt0pkpDzgIQH2g1VHWUQl-GHJGE8,4699
|
|
26
|
+
dataapp_crawler/browser_tools/navigation.py,sha256=-l1PJgx60tmEFs-O8NNQSygYHgXSTomOeHIJu4qW3Gs,8813
|
|
27
|
+
dataapp_crawler/browser_tools/readiness.py,sha256=X-b__NsmNbANT1VDFR-jFbhKAAXldr-FBBdXtyxK8v4,1365
|
|
28
|
+
dataapp_crawler/providers/__init__.py,sha256=4A_xqgasRHuqj3rSmk_y1yrNGbfFuBlw6ancpEe-1zg,537
|
|
29
|
+
dataapp_crawler/providers/browser.py,sha256=RaBhNEtfJ8RqfLHqHAWlM5Qv8FKXJYkti2eL0p5eNg0,10701
|
|
30
|
+
dataapp_crawler/providers/camoufox.py,sha256=kcj84MGSY6m3_Ad5EgsENuSgNpnECKY5ZRw2VCy3gkM,6941
|
|
31
|
+
dataapp_crawler/providers/dataos.py,sha256=xWc_5PIiYmqRuthUcX1bVl5y2rx2X-5InbwhC0Jyu_k,5823
|
|
32
|
+
dataapp_crawler/providers/pool.py,sha256=TBpPHOTBSJfIsG9YuzvGqVrJzauspPItdp7DhqiQqV0,17511
|
|
33
|
+
dataapp_crawler/providers/proxy.py,sha256=_1PPxdAA-Ris9btKYTx3uFBP98N6Zw1ZBRY1GN719mA,7697
|
|
34
|
+
dataapp_crawler-0.1.0.dist-info/METADATA,sha256=dfnqV6FDa1LG2lvfINZaa_DTX9Gm-ebZha7pEBJbfT8,9065
|
|
35
|
+
dataapp_crawler-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
36
|
+
dataapp_crawler-0.1.0.dist-info/RECORD,,
|