dataapp_crawler 0.1.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.
Files changed (36) hide show
  1. dataapp_crawler-0.1.0/PKG-INFO +211 -0
  2. dataapp_crawler-0.1.0/README.md +195 -0
  3. dataapp_crawler-0.1.0/pyproject.toml +52 -0
  4. dataapp_crawler-0.1.0/src/dataapp_crawler/__init__.py +87 -0
  5. dataapp_crawler-0.1.0/src/dataapp_crawler/__main__.py +35 -0
  6. dataapp_crawler-0.1.0/src/dataapp_crawler/_logging.py +148 -0
  7. dataapp_crawler-0.1.0/src/dataapp_crawler/_network_diagnostics.py +193 -0
  8. dataapp_crawler-0.1.0/src/dataapp_crawler/browser_tools/__init__.py +23 -0
  9. dataapp_crawler-0.1.0/src/dataapp_crawler/browser_tools/_common.py +66 -0
  10. dataapp_crawler-0.1.0/src/dataapp_crawler/browser_tools/dom.py +49 -0
  11. dataapp_crawler-0.1.0/src/dataapp_crawler/browser_tools/lists.py +125 -0
  12. dataapp_crawler-0.1.0/src/dataapp_crawler/browser_tools/navigation.py +243 -0
  13. dataapp_crawler-0.1.0/src/dataapp_crawler/browser_tools/readiness.py +42 -0
  14. dataapp_crawler-0.1.0/src/dataapp_crawler/captcha.py +912 -0
  15. dataapp_crawler-0.1.0/src/dataapp_crawler/checkpoint.py +487 -0
  16. dataapp_crawler-0.1.0/src/dataapp_crawler/cloudflare.py +523 -0
  17. dataapp_crawler-0.1.0/src/dataapp_crawler/context.py +231 -0
  18. dataapp_crawler-0.1.0/src/dataapp_crawler/cookies.py +207 -0
  19. dataapp_crawler-0.1.0/src/dataapp_crawler/crawler.py +1392 -0
  20. dataapp_crawler-0.1.0/src/dataapp_crawler/errors.py +184 -0
  21. dataapp_crawler-0.1.0/src/dataapp_crawler/http.py +223 -0
  22. dataapp_crawler-0.1.0/src/dataapp_crawler/images.py +267 -0
  23. dataapp_crawler-0.1.0/src/dataapp_crawler/local_auth.py +77 -0
  24. dataapp_crawler-0.1.0/src/dataapp_crawler/models.py +285 -0
  25. dataapp_crawler-0.1.0/src/dataapp_crawler/pagination.py +172 -0
  26. dataapp_crawler-0.1.0/src/dataapp_crawler/parsing.py +13 -0
  27. dataapp_crawler-0.1.0/src/dataapp_crawler/providers/__init__.py +20 -0
  28. dataapp_crawler-0.1.0/src/dataapp_crawler/providers/browser.py +292 -0
  29. dataapp_crawler-0.1.0/src/dataapp_crawler/providers/camoufox.py +184 -0
  30. dataapp_crawler-0.1.0/src/dataapp_crawler/providers/dataos.py +174 -0
  31. dataapp_crawler-0.1.0/src/dataapp_crawler/providers/pool.py +451 -0
  32. dataapp_crawler-0.1.0/src/dataapp_crawler/providers/proxy.py +209 -0
  33. dataapp_crawler-0.1.0/src/dataapp_crawler/py.typed +0 -0
  34. dataapp_crawler-0.1.0/src/dataapp_crawler/routing.py +67 -0
  35. dataapp_crawler-0.1.0/src/dataapp_crawler/scheduling.py +425 -0
  36. dataapp_crawler-0.1.0/src/dataapp_crawler/sessions.py +735 -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,195 @@
1
+ # Data App Crawler core
2
+
3
+ `dataapp-crawler` (imported as `dataapp_crawler`) is the provider-neutral website
4
+ acquisition library for Python 3.11+. It can run independently of the DataApp
5
+ Runtime. Core includes generic CDP/Playwright providers and an optional Camoufox
6
+ adapter, but no Relay- or AWS-specific provider.
7
+
8
+ > Dependency versions are defined in each package's `pyproject.toml`; an
9
+ > application uses its own `uv.lock`. Local source changes are not delivered to
10
+ > applications until matching packages and browser images are released and the
11
+ > application's lock is updated.
12
+
13
+ ## Basic acquisition
14
+
15
+ ```python
16
+ import asyncio
17
+
18
+ from dataapp_crawler import Crawler
19
+
20
+
21
+ async def main() -> None:
22
+ async with Crawler() as crawler:
23
+ capture = await crawler.fetch("https://example.com")
24
+ print(capture.status)
25
+
26
+
27
+ asyncio.run(main())
28
+ ```
29
+
30
+ Use an ordinary synchronous or asynchronous `process(capture, context)` function
31
+ for extraction, OCR/ASR calls, model extraction, or custom logic. The SDK does
32
+ not own a processing engine or Dataset:
33
+
34
+ ```python
35
+ async def process(capture, context):
36
+ heading = capture.page.locator("h1").first
37
+ await context.run(heading.wait_for(state="visible"), timeout=10)
38
+ return {"title": (await context.run(heading.inner_text(), timeout=5)).strip()}
39
+ ```
40
+
41
+ An operation timeout raises `TimeoutError`; exhausting `Limits.max_duration`
42
+ raises `LimitReached`. Diagnostics are bounded and omit raw credentials, headers,
43
+ connection URLs, response bodies, and arbitrary upstream exception text.
44
+
45
+ ## Page-driven flows
46
+
47
+ `Crawler.run_browser(collect)` hands one native Playwright `Page` and a
48
+ `CrawlContext` to ordinary application code. A single collection function can
49
+ submit a search, traverse list pages, open detail pages, and stop on business
50
+ conditions. It does not require one URL per `Crawler` object.
51
+
52
+ ```python
53
+ from dataapp_crawler import Crawler, Limits
54
+ from dataapp_crawler.browser_tools import open_url, text_of
55
+
56
+
57
+ async def collect(page, context):
58
+ await open_url(page, "https://example.com", context)
59
+ return {"title": await text_of(page.locator("h1"), context, required=True)}
60
+
61
+
62
+ async with Crawler(browser=browser_provider, limits=Limits(max_duration=120)) as crawler:
63
+ record = await crawler.run_browser(collect)
64
+ ```
65
+
66
+ `browser_tools` includes bounded navigation and extraction helpers such as
67
+ `open_url`, `new_page`, `popup_page`, `same_tab`, `wait_until`, `text_of`,
68
+ `attribute_of`, and `iterate_list`. Page and Locator remain native Playwright
69
+ objects. Raw Playwright actions remain available, but they do not automatically
70
+ gain Crawler accounting, scheduling, diagnostics, or retry semantics.
71
+
72
+ The application defines selectors, stable business keys, pagination, readiness,
73
+ and side-effect boundaries. Do not automatically retry clicks or submissions
74
+ that can create duplicate actions.
75
+
76
+ Failure summaries include a fixed `exception_type` without its message. Browser
77
+ flow failures also report `root_host`, the root Page's hostname only. This may be
78
+ the list page rather than a failing detail tab. The original exception still
79
+ propagates, with cancellation and cleanup unchanged. A failure before the root
80
+ Page exists reports `phase=page_creation`.
81
+
82
+ To inspect a failing detail page, explicitly capture a bounded screenshot inside
83
+ that page's scope before it closes. Keep artifacts private, outside uploaded
84
+ source packages and logs. `diagnostics_dir` applies to request-driven captures;
85
+ it does not automatically snapshot every native Page action in `run_browser`.
86
+ An exception type or screenshot does not by itself establish whether the target
87
+ site blocked access, returned a different layout, or had not finished loading.
88
+
89
+ ## Read-only CAPTCHA observations
90
+
91
+ `detect_captchas(html, ...)` and `await detect_captchas_on_page(page, ...)` classify
92
+ observable reCAPTCHA v2/Enterprise, Turnstile, Cloudflare full-page Challenge,
93
+ BotDetect, and image-CAPTCHA
94
+ integrations. Results contain fixed evidence codes, component matches, and missing
95
+ inputs. Script-only or unsupported evidence remains `unknown`; a form widget is
96
+ distinct from an access challenge. `CaptchaOverride` accepts explicit site knowledge.
97
+
98
+ Detection does not navigate, click, capture images, submit forms, or contact paid
99
+ services. A solver match is structural readiness, not authorization or proof of
100
+ website acceptance. `capture_image(locator)` separately captures the currently
101
+ rendered challenge for an explicitly chosen resource call. Keep submission and
102
+ site acceptance checks in ordinary application code.
103
+
104
+ The candidate `CloudflareChallengeCapture` API observes dynamic full-page
105
+ render parameters when installed before navigation, and applies a managed
106
+ token only to the same active challenge. See the [token workflow](docs/cloudflare-challenge.md).
107
+ The local contract and browser fixture have passed; real-site managed solving
108
+ and deployment acceptance remain pending. Ordinary Turnstile and full-page
109
+ Challenge tasks have different required fields. Detection alone never enables
110
+ a paid call, and the `cf_clearance` cookie mode is not exposed.
111
+
112
+ See the source template's [API reference](../../examples/crawler/relay_template/help/docs/API.zh-CN.md)
113
+ for parameters and the Runtime/resource boundary.
114
+
115
+ ## Provider extensions
116
+
117
+ Supplier-specific provider implementations are separate distributions:
118
+
119
+ | Distribution | Import | Public provider types |
120
+ |---|---|---|
121
+ | `dataapp-crawler-octoparse-browser` | `dataapp_crawler_octoparse` | `BrowserEnvRelay`, `RelayBrowser` |
122
+ | `dataapp-crawler-agentcore` | `dataapp_crawler_agentcore` | `AgentCoreBrowser`, `AWSBrowser` |
123
+
124
+ The core wheel retains `CDPBrowser`, `PlaywrightBrowser`, and optional
125
+ `CamoufoxBrowser` as generic integrations. It does not contain either supplier
126
+ implementation and does not depend on Relay or boto3. Applications install one
127
+ supplier extension deliberately and import its provider from that extension.
128
+ This makes an accidental supplier-provider import fail at project-install time
129
+ instead of silently exposing an unsupported runtime path.
130
+
131
+ Source packages may share this repository; that does not merge their wheel
132
+ contents, dependency graphs, project templates, or base images.
133
+
134
+ ## Proxy boundary
135
+
136
+ Browser and proxy selection are independent. Core provides the common proxy
137
+ contract and supports:
138
+
139
+ - direct access with `proxy=None`;
140
+ - a developer-provided `StaticProxy`;
141
+ - a managed `DataOSProxy` through the Resource Proxy client;
142
+ - a custom `ProxyProvider.acquire()` returning a `ProxyHandle`.
143
+
144
+ `DataOSProxy.proxy_group` accepts `auto`, `domestic_basic`,
145
+ `overseas_basic`, or `overseas_enhanced`. `auto` uses the basic managed
146
+ service. The SDK does not select or name proxy suppliers: Resource Proxy owns
147
+ the published supplier order and validates location and duration support.
148
+
149
+ If managed proxy acquisition fails, `DataOSProxy` raises `ConfigurationError`
150
+ with bounded `code` and `status_code` attributes. These diagnostics contain only
151
+ the documented resource error class and HTTP status; supplier responses, proxy
152
+ URLs, and credentials are never retained in the exception.
153
+
154
+ A proxy handle is tied to a session lifecycle. It is not a promise to rotate the
155
+ exit IP on every request. Credentials must remain outside inputs, source files,
156
+ logs, and build arguments.
157
+
158
+ ## DataApp integration
159
+
160
+ Within a Data App, use `DataAppCrawler` from
161
+ `bazhuayu_dataapp.plugins.crawler`:
162
+
163
+ ```python
164
+ from bazhuayu_dataapp import DataApp
165
+ from bazhuayu_dataapp.plugins.crawler import DataAppCrawler
166
+
167
+
168
+ async with DataApp:
169
+ async with DataAppCrawler(browser=browser_provider) as crawler:
170
+ records = await crawler.run_browser(collect)
171
+ await DataApp.push_data(records)
172
+ ```
173
+
174
+ Runtime owns Run input, Dataset writes, progress, cancellation, and final state.
175
+ `DataAppCrawler` only joins Crawler cancellation and cleanup to that lifecycle.
176
+
177
+ ## Logging
178
+
179
+ The standalone Crawler writes INFO stage logs to stderr by default. The DataApp
180
+ adapter uses `DataApp.log`, so the platform receives the same bounded stages.
181
+ Pass a standard logger to change verbosity:
182
+
183
+ ```python
184
+ import logging
185
+
186
+ logger = logging.getLogger("my_app.crawler")
187
+ logger.setLevel(logging.WARNING)
188
+
189
+ async with Crawler(logger=logger) as crawler:
190
+ capture = await crawler.fetch(target_url)
191
+ ```
192
+
193
+ The SDK does not configure the root logger and never logs the value returned by a
194
+ process callback. Acquisition success is not Dataset success; the application
195
+ must push records explicitly and validate the Runtime outcome separately.
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "dataapp_crawler"
7
+ version = "0.1.0"
8
+ description = "Composable HTTP, browser and proxy acquisition SDK for DataOS DataApps"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = ["httpx>=0.28,<1"]
12
+
13
+ [project.optional-dependencies]
14
+ parse = ["parsel>=1.9,<2"]
15
+ browser = ["playwright>=1.64,<2"]
16
+ resource-proxy = ["dataapp-resource-proxy>=0.1.0,<0.2"]
17
+ fingerprint = ["camoufox>=0.5.9,<0.6"]
18
+
19
+ [tool.uv]
20
+ # Camoufox owns a Firefox build and currently requires Playwright <1.63.
21
+ # Select it in a separate environment from native Chromium/Relay extras.
22
+ conflicts = [
23
+ [{extra = "fingerprint"}, {extra = "browser"}],
24
+ ]
25
+
26
+ [dependency-groups]
27
+ dev = ["pytest>=8,<10", "pytest-asyncio>=0.24,<2", "ruff>=0.9", "mypy>=1.13"]
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/dataapp_crawler"]
31
+
32
+ [tool.hatch.build.targets.sdist]
33
+ only-include = ["src", "README.md", "pyproject.toml"]
34
+
35
+ [tool.pytest.ini_options]
36
+ asyncio_mode = "auto"
37
+ asyncio_default_fixture_loop_scope = "function"
38
+ testpaths = ["tests"]
39
+ addopts = "-ra"
40
+ pythonpath = ["src", "../crawler-octoparse/src", "../crawler-agentcore/src"]
41
+
42
+ [tool.ruff]
43
+ target-version = "py311"
44
+ line-length = 100
45
+
46
+ [tool.ruff.lint]
47
+ select = ["E", "F", "I", "UP", "B"]
48
+
49
+ [tool.mypy]
50
+ python_version = "3.11"
51
+ check_untyped_defs = true
52
+ ignore_missing_imports = true
@@ -0,0 +1,87 @@
1
+ """Developer-friendly acquisition, without a dependency on the DataOS Runtime."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from .captcha import (
6
+ CaptchaConfidence,
7
+ CaptchaEvidence,
8
+ CaptchaFamily,
9
+ CaptchaObservation,
10
+ CaptchaOverride,
11
+ CaptchaPresence,
12
+ SolverComponent,
13
+ SolverMatch,
14
+ detect_captchas,
15
+ detect_captchas_on_page,
16
+ )
17
+ from .checkpoint import CrawlCheckpoint
18
+ from .cloudflare import CloudflareChallengeCapture, CloudflareChallengeParameters
19
+ from .context import CrawlContext
20
+ from .crawler import Crawler
21
+ from .errors import (
22
+ ConfigurationError,
23
+ CrawlCancelled,
24
+ CrawlerError,
25
+ FetchError,
26
+ LimitReached,
27
+ ProcessingError,
28
+ QueueFull,
29
+ ResponseTooLarge,
30
+ )
31
+ from .local_auth import local_resource_environment, read_api_key_file, run_local_app
32
+ from .models import CaptureResult, CrawlItem, Failure, Identity, Limits, Request, Stats
33
+ from .pagination import Pagination
34
+ from .routing import RouteAlreadyRegistered, RouteNotFound, Router
35
+ from .scheduling import AdaptivePolicy, AutoConcurrency, ResourceSample
36
+ from .sessions import Session, SessionInfo, SessionPool
37
+
38
+ __all__ = [
39
+ "AdaptivePolicy",
40
+ "AutoConcurrency",
41
+ "CaptchaConfidence",
42
+ "CaptchaEvidence",
43
+ "CaptchaFamily",
44
+ "CaptchaObservation",
45
+ "CaptchaOverride",
46
+ "CaptchaPresence",
47
+ "CaptureResult",
48
+ "CrawlCheckpoint",
49
+ "CloudflareChallengeCapture",
50
+ "CloudflareChallengeParameters",
51
+ "ResourceSample",
52
+ "ConfigurationError",
53
+ "CrawlCancelled",
54
+ "CrawlContext",
55
+ "Crawler",
56
+ "CrawlerError",
57
+ "CrawlItem",
58
+ "Failure",
59
+ "FetchError",
60
+ "Identity",
61
+ "LimitReached",
62
+ "local_resource_environment",
63
+ "Limits",
64
+ "ProcessingError",
65
+ "QueueFull",
66
+ "Request",
67
+ "ResponseTooLarge",
68
+ "read_api_key_file",
69
+ "RouteAlreadyRegistered",
70
+ "RouteNotFound",
71
+ "run_local_app",
72
+ "Router",
73
+ "Pagination",
74
+ "Stats",
75
+ "Session",
76
+ "SessionInfo",
77
+ "SessionPool",
78
+ "SolverComponent",
79
+ "SolverMatch",
80
+ "detect_captchas",
81
+ "detect_captchas_on_page",
82
+ ]
83
+
84
+ try:
85
+ __version__ = version("dataapp_crawler")
86
+ except PackageNotFoundError:
87
+ __version__ = "0.0.0"
@@ -0,0 +1,35 @@
1
+ """Local Crawler SDK command helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from collections.abc import Sequence
7
+
8
+ from .errors import ConfigurationError
9
+ from .local_auth import run_local_app
10
+
11
+
12
+ def main(argv: Sequence[str] | None = None) -> int:
13
+ parser = argparse.ArgumentParser(prog="python -m dataapp_crawler")
14
+ commands = parser.add_subparsers(dest="command", required=True)
15
+ run = commands.add_parser(
16
+ "run",
17
+ help="run a local DataApp with a file-backed platform API Key",
18
+ )
19
+ run.add_argument("--api-key-file", required=True, help="regular file containing one API Key")
20
+ run.add_argument("app_command", nargs=argparse.REMAINDER, help="application command after --")
21
+ options = parser.parse_args(argv)
22
+ if options.command != "run":
23
+ return 2
24
+ command = list(options.app_command)
25
+ if command[:1] == ["--"]:
26
+ command = command[1:]
27
+ try:
28
+ return run_local_app(options.api_key_file, command)
29
+ except ConfigurationError as error:
30
+ print(f"配置错误:{error}")
31
+ return 2
32
+
33
+
34
+ if __name__ == "__main__":
35
+ raise SystemExit(main())