crawlerflow 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.
- crawlerflow-0.1.0/.gitignore +10 -0
- crawlerflow-0.1.0/LICENSE +21 -0
- crawlerflow-0.1.0/PKG-INFO +131 -0
- crawlerflow-0.1.0/README.md +93 -0
- crawlerflow-0.1.0/crawlerflow/__init__.py +9 -0
- crawlerflow-0.1.0/crawlerflow/__main__.py +6 -0
- crawlerflow-0.1.0/crawlerflow/browser/__init__.py +8 -0
- crawlerflow-0.1.0/crawlerflow/browser/base.py +72 -0
- crawlerflow-0.1.0/crawlerflow/browser/factory.py +40 -0
- crawlerflow-0.1.0/crawlerflow/browser/pydoll.py +311 -0
- crawlerflow-0.1.0/crawlerflow/cli/__init__.py +4 -0
- crawlerflow-0.1.0/crawlerflow/cli/app.py +237 -0
- crawlerflow-0.1.0/crawlerflow/engine/__init__.py +9 -0
- crawlerflow-0.1.0/crawlerflow/engine/context.py +164 -0
- crawlerflow-0.1.0/crawlerflow/engine/executor.py +273 -0
- crawlerflow-0.1.0/crawlerflow/engine/registry.py +107 -0
- crawlerflow-0.1.0/crawlerflow/engine/runner.py +170 -0
- crawlerflow-0.1.0/crawlerflow/events/__init__.py +17 -0
- crawlerflow-0.1.0/crawlerflow/events/bus.py +38 -0
- crawlerflow-0.1.0/crawlerflow/events/console_logger.py +56 -0
- crawlerflow-0.1.0/crawlerflow/events/json_logger.py +53 -0
- crawlerflow-0.1.0/crawlerflow/events/models.py +31 -0
- crawlerflow-0.1.0/crawlerflow/expressions/__init__.py +21 -0
- crawlerflow-0.1.0/crawlerflow/expressions/conditions.py +83 -0
- crawlerflow-0.1.0/crawlerflow/expressions/engine.py +191 -0
- crawlerflow-0.1.0/crawlerflow/plugins/__init__.py +35 -0
- crawlerflow-0.1.0/crawlerflow/plugins/base.py +37 -0
- crawlerflow-0.1.0/crawlerflow/plugins/manager.py +212 -0
- crawlerflow-0.1.0/crawlerflow/py.typed +0 -0
- crawlerflow-0.1.0/crawlerflow/steps/__init__.py +20 -0
- crawlerflow-0.1.0/crawlerflow/steps/browser.py +572 -0
- crawlerflow-0.1.0/crawlerflow/steps/control.py +706 -0
- crawlerflow-0.1.0/crawlerflow/steps/utility.py +1487 -0
- crawlerflow-0.1.0/crawlerflow/workflow/__init__.py +9 -0
- crawlerflow-0.1.0/crawlerflow/workflow/loader.py +34 -0
- crawlerflow-0.1.0/crawlerflow/workflow/models.py +171 -0
- crawlerflow-0.1.0/docs/adr/0001-core-boundaries.md +21 -0
- crawlerflow-0.1.0/docs/architecture.md +47 -0
- crawlerflow-0.1.0/docs/control-flow.md +215 -0
- crawlerflow-0.1.0/docs/html-output.md +197 -0
- crawlerflow-0.1.0/docs/http-requests.md +99 -0
- crawlerflow-0.1.0/docs/plugins.md +132 -0
- crawlerflow-0.1.0/docs/pydoll.md +61 -0
- crawlerflow-0.1.0/docs/retries-and-logging.md +89 -0
- crawlerflow-0.1.0/docs/runtime-variables.md +39 -0
- crawlerflow-0.1.0/examples/basic.yaml +17 -0
- crawlerflow-0.1.0/examples/control-flow.yaml +26 -0
- crawlerflow-0.1.0/examples/loops-macros.yaml +20 -0
- crawlerflow-0.1.0/examples/plugins/example/README.md +15 -0
- crawlerflow-0.1.0/examples/plugins/example/pyproject.toml +17 -0
- crawlerflow-0.1.0/examples/plugins/example/src/crawlerflow_example_plugin/__init__.py +66 -0
- crawlerflow-0.1.0/examples/plugins/example/workflow.yaml +18 -0
- crawlerflow-0.1.0/examples/pydoll.yaml +29 -0
- crawlerflow-0.1.0/examples/retry-logging.yaml +32 -0
- crawlerflow-0.1.0/pyproject.toml +83 -0
- crawlerflow-0.1.0/tests/test_browser_steps.py +753 -0
- crawlerflow-0.1.0/tests/test_cli.py +372 -0
- crawlerflow-0.1.0/tests/test_conditions.py +26 -0
- crawlerflow-0.1.0/tests/test_control_flow.py +328 -0
- crawlerflow-0.1.0/tests/test_example_plugin.py +33 -0
- crawlerflow-0.1.0/tests/test_expressions.py +78 -0
- crawlerflow-0.1.0/tests/test_loops_and_macros.py +443 -0
- crawlerflow-0.1.0/tests/test_plugins.py +385 -0
- crawlerflow-0.1.0/tests/test_pydoll_adapter.py +221 -0
- crawlerflow-0.1.0/tests/test_retry_and_logging.py +277 -0
- crawlerflow-0.1.0/tests/test_workflow.py +512 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mehmet Emin Eker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: crawlerflow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Declarative YAML workflow engine for browser automation and web scraping
|
|
5
|
+
Project-URL: Homepage, https://github.com/mehmetemineker/crawlerflow
|
|
6
|
+
Project-URL: Repository, https://github.com/mehmetemineker/crawlerflow
|
|
7
|
+
Project-URL: Issues, https://github.com/mehmetemineker/crawlerflow/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/mehmetemineker/crawlerflow/tree/main/docs
|
|
9
|
+
Author: Mehmet Emin Eker
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: automation,browser,crawler,scraping,workflow,yaml
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: pydantic>=2.8
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: rich>=13.7
|
|
29
|
+
Requires-Dist: typer>=0.12
|
|
30
|
+
Provides-Extra: browser
|
|
31
|
+
Requires-Dist: pydoll-python<3,>=2.23; extra == 'browser'
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pydoll-python<3,>=2.23; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# CrawlerFlow
|
|
40
|
+
|
|
41
|
+
CrawlerFlow is a declarative, YAML-based workflow engine for browser automation and web
|
|
42
|
+
scraping. Workflows describe what should happen; adapters and steps decide how it happens.
|
|
43
|
+
|
|
44
|
+
## Current foundation
|
|
45
|
+
|
|
46
|
+
- Versioned YAML workflow loading and validation
|
|
47
|
+
- Browser-independent adapter contract and a lazy-starting Pydoll implementation
|
|
48
|
+
- Extensible step registry
|
|
49
|
+
- Isolated plugin API with typed YAML settings, lifecycle hooks, steps, filters, and subscribers
|
|
50
|
+
- Async workflow executor and event bus
|
|
51
|
+
- Variable interpolation and a built-in expression engine
|
|
52
|
+
- Per-run `today` and `now` date variables
|
|
53
|
+
- Nested `foreach`, `foreach_date`, `foreach_select`, and declarative `if` control flow
|
|
54
|
+
- Reusable parameterized workflow macros
|
|
55
|
+
- Per-step retry and continue/fail error policies
|
|
56
|
+
- JSON Lines workflow, step, retry, and request event logging
|
|
57
|
+
- Built-in navigation, interaction, cookies, downloads, screenshots, and selective HTML output
|
|
58
|
+
- `run`, `validate`, `list-steps`, `list-plugins`, and `doctor` CLI commands
|
|
59
|
+
|
|
60
|
+
## Development
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
python -m pip install -e ".[dev]"
|
|
64
|
+
pytest
|
|
65
|
+
crawlerflow validate examples/basic.yaml
|
|
66
|
+
crawlerflow run examples/basic.yaml
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Run multiple workflows sequentially by supplying more paths. Execution stops at the first failed
|
|
70
|
+
workflow:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
crawlerflow run examples/first-site.yaml examples/second-site.yaml
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A directory argument discovers its directly contained `.yaml` and `.yml` files and runs them in
|
|
77
|
+
alphabetical order:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
crawlerflow run examples
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use asynchronous mode to run every supplied workflow in parallel. All workflows are allowed to
|
|
84
|
+
finish; the command exits with code `1` if any workflow fails:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
crawlerflow run --mode async examples/first-site.yaml examples/second-site.yaml
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Directory discovery can also be combined with parallel execution:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
crawlerflow run --mode async examples
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Add `--progress` to display a live progress bar based on the total workflow count. The bar advances
|
|
97
|
+
as each workflow succeeds or fails in both sequential and asynchronous modes:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
crawlerflow run --mode async --progress examples
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use `--concurrency` (or `-c`) to limit how many workflows run at the same time in asynchronous
|
|
104
|
+
mode. This avoids starting every HTTP client or browser session simultaneously:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
crawlerflow run --mode async --concurrency 8 --progress examples
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Omitting the option preserves unlimited parallel execution. `--concurrency` accepts positive
|
|
111
|
+
integers and can only be used with `--mode async`.
|
|
112
|
+
|
|
113
|
+
Install browser support and select Pydoll in a workflow:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
python -m pip install -e ".[browser,dev]"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
browser:
|
|
121
|
+
engine: pydoll
|
|
122
|
+
headless: true
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Browser-free workflows omit the `browser` section. Applications can also inject another
|
|
126
|
+
`BrowserAdapter` into `WorkflowRunner`. See `docs/http-requests.md` for direct HTTP requests and
|
|
127
|
+
shortened map URL coordinate resolution.
|
|
128
|
+
|
|
129
|
+
External extensions can register entry points under `crawlerflow.plugins`; workflows activate only
|
|
130
|
+
the plugins they list. See `docs/plugins.md` and the installable `examples/plugins/example` package
|
|
131
|
+
for the plugin contract, discovery command, and packaging example.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# CrawlerFlow
|
|
2
|
+
|
|
3
|
+
CrawlerFlow is a declarative, YAML-based workflow engine for browser automation and web
|
|
4
|
+
scraping. Workflows describe what should happen; adapters and steps decide how it happens.
|
|
5
|
+
|
|
6
|
+
## Current foundation
|
|
7
|
+
|
|
8
|
+
- Versioned YAML workflow loading and validation
|
|
9
|
+
- Browser-independent adapter contract and a lazy-starting Pydoll implementation
|
|
10
|
+
- Extensible step registry
|
|
11
|
+
- Isolated plugin API with typed YAML settings, lifecycle hooks, steps, filters, and subscribers
|
|
12
|
+
- Async workflow executor and event bus
|
|
13
|
+
- Variable interpolation and a built-in expression engine
|
|
14
|
+
- Per-run `today` and `now` date variables
|
|
15
|
+
- Nested `foreach`, `foreach_date`, `foreach_select`, and declarative `if` control flow
|
|
16
|
+
- Reusable parameterized workflow macros
|
|
17
|
+
- Per-step retry and continue/fail error policies
|
|
18
|
+
- JSON Lines workflow, step, retry, and request event logging
|
|
19
|
+
- Built-in navigation, interaction, cookies, downloads, screenshots, and selective HTML output
|
|
20
|
+
- `run`, `validate`, `list-steps`, `list-plugins`, and `doctor` CLI commands
|
|
21
|
+
|
|
22
|
+
## Development
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python -m pip install -e ".[dev]"
|
|
26
|
+
pytest
|
|
27
|
+
crawlerflow validate examples/basic.yaml
|
|
28
|
+
crawlerflow run examples/basic.yaml
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run multiple workflows sequentially by supplying more paths. Execution stops at the first failed
|
|
32
|
+
workflow:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
crawlerflow run examples/first-site.yaml examples/second-site.yaml
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
A directory argument discovers its directly contained `.yaml` and `.yml` files and runs them in
|
|
39
|
+
alphabetical order:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
crawlerflow run examples
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use asynchronous mode to run every supplied workflow in parallel. All workflows are allowed to
|
|
46
|
+
finish; the command exits with code `1` if any workflow fails:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
crawlerflow run --mode async examples/first-site.yaml examples/second-site.yaml
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Directory discovery can also be combined with parallel execution:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
crawlerflow run --mode async examples
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Add `--progress` to display a live progress bar based on the total workflow count. The bar advances
|
|
59
|
+
as each workflow succeeds or fails in both sequential and asynchronous modes:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
crawlerflow run --mode async --progress examples
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use `--concurrency` (or `-c`) to limit how many workflows run at the same time in asynchronous
|
|
66
|
+
mode. This avoids starting every HTTP client or browser session simultaneously:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
crawlerflow run --mode async --concurrency 8 --progress examples
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Omitting the option preserves unlimited parallel execution. `--concurrency` accepts positive
|
|
73
|
+
integers and can only be used with `--mode async`.
|
|
74
|
+
|
|
75
|
+
Install browser support and select Pydoll in a workflow:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m pip install -e ".[browser,dev]"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
browser:
|
|
83
|
+
engine: pydoll
|
|
84
|
+
headless: true
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Browser-free workflows omit the `browser` section. Applications can also inject another
|
|
88
|
+
`BrowserAdapter` into `WorkflowRunner`. See `docs/http-requests.md` for direct HTTP requests and
|
|
89
|
+
shortened map URL coordinate resolution.
|
|
90
|
+
|
|
91
|
+
External extensions can register entry points under `crawlerflow.plugins`; workflows activate only
|
|
92
|
+
the plugins they list. See `docs/plugins.md` and the installable `examples/plugins/example` package
|
|
93
|
+
for the plugin contract, discovery command, and packaging example.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Browser adapter contracts and implementations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from crawlerflow.browser.base import BrowserAdapter, BrowserResponse
|
|
6
|
+
from crawlerflow.browser.factory import create_browser_adapter
|
|
7
|
+
|
|
8
|
+
__all__ = ["BrowserAdapter", "BrowserResponse", "create_browser_adapter"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Browser abstraction used by workflow steps."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(slots=True, frozen=True)
|
|
12
|
+
class BrowserResponse:
|
|
13
|
+
"""Serializable response returned by browser-session requests."""
|
|
14
|
+
|
|
15
|
+
status_code: int
|
|
16
|
+
headers: dict[str, str] = field(default_factory=dict)
|
|
17
|
+
body: str | bytes | None = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BrowserAdapter(ABC):
|
|
21
|
+
"""Backend-neutral asynchronous browser contract."""
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
async def goto(self, url: str) -> None: ...
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def click(self, selector: str) -> None: ...
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
async def fill(self, selector: str, value: str) -> None: ...
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
async def select(self, selector: str, value: str) -> None: ...
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
async def wait(self, selector: str, timeout_seconds: float | None = None) -> None: ...
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
async def wait_network(self, timeout_seconds: float | None = None) -> None: ...
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
async def html(self) -> str: ...
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
async def evaluate(self, script: str) -> Any: ...
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
async def cookies(self) -> dict[str, str]: ...
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
async def set_cookies(self, cookies: dict[str, str]) -> None: ...
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
async def request(
|
|
55
|
+
self,
|
|
56
|
+
method: str,
|
|
57
|
+
url: str,
|
|
58
|
+
*,
|
|
59
|
+
headers: dict[str, str] | None = None,
|
|
60
|
+
data: Any = None,
|
|
61
|
+
) -> BrowserResponse: ...
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
async def download(self, url: str, path: Path) -> Path: ...
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
async def screenshot(self, path: Path) -> Path: ...
|
|
68
|
+
|
|
69
|
+
async def close(self) -> None:
|
|
70
|
+
"""Release browser resources when an adapter owns them."""
|
|
71
|
+
|
|
72
|
+
return None
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Create browser adapters from workflow configuration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from crawlerflow.browser.base import BrowserAdapter
|
|
8
|
+
from crawlerflow.workflow.models import BrowserSettings
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_browser_adapter(
|
|
12
|
+
settings: BrowserSettings,
|
|
13
|
+
*,
|
|
14
|
+
base_path: Path,
|
|
15
|
+
) -> BrowserAdapter | None:
|
|
16
|
+
"""Create the configured browser adapter, if browser execution is enabled."""
|
|
17
|
+
|
|
18
|
+
if settings.engine is None:
|
|
19
|
+
return None
|
|
20
|
+
if settings.engine == "pydoll":
|
|
21
|
+
from crawlerflow.browser.pydoll import PydollBrowserAdapter, PydollBrowserConfig
|
|
22
|
+
|
|
23
|
+
binary_location = settings.binary_location
|
|
24
|
+
if binary_location is not None and not binary_location.is_absolute():
|
|
25
|
+
binary_location = base_path / binary_location
|
|
26
|
+
download_directory = settings.download_directory
|
|
27
|
+
if download_directory is not None and not download_directory.is_absolute():
|
|
28
|
+
download_directory = base_path / download_directory
|
|
29
|
+
return PydollBrowserAdapter(
|
|
30
|
+
PydollBrowserConfig(
|
|
31
|
+
headless=settings.headless,
|
|
32
|
+
binary_location=binary_location,
|
|
33
|
+
arguments=tuple(settings.arguments),
|
|
34
|
+
start_timeout=settings.start_timeout,
|
|
35
|
+
default_wait_timeout=settings.default_wait_timeout,
|
|
36
|
+
network_idle_period=settings.network_idle_period,
|
|
37
|
+
download_directory=download_directory,
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
raise ValueError(f"Unsupported browser engine: {settings.engine}")
|