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.
@@ -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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any