pywebflx 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.
- pywebflx-0.1.0/.github/workflows/ci.yml +39 -0
- pywebflx-0.1.0/.github/workflows/publish.yml +33 -0
- pywebflx-0.1.0/.gitignore +9 -0
- pywebflx-0.1.0/CLAUDE.md +50 -0
- pywebflx-0.1.0/PKG-INFO +90 -0
- pywebflx-0.1.0/README.md +57 -0
- pywebflx-0.1.0/docs/api/browser.md +100 -0
- pywebflx-0.1.0/docs/api/browser.pt.md +100 -0
- pywebflx-0.1.0/docs/api/config.md +95 -0
- pywebflx-0.1.0/docs/api/config.pt.md +95 -0
- pywebflx-0.1.0/docs/api/exceptions.md +97 -0
- pywebflx-0.1.0/docs/api/exceptions.pt.md +97 -0
- pywebflx-0.1.0/docs/api/extraction.md +183 -0
- pywebflx-0.1.0/docs/api/extraction.pt.md +183 -0
- pywebflx-0.1.0/docs/api/inspect.md +103 -0
- pywebflx-0.1.0/docs/api/inspect.pt.md +103 -0
- pywebflx-0.1.0/docs/api/interaction.md +188 -0
- pywebflx-0.1.0/docs/api/interaction.pt.md +188 -0
- pywebflx-0.1.0/docs/api/javascript.md +54 -0
- pywebflx-0.1.0/docs/api/javascript.pt.md +54 -0
- pywebflx-0.1.0/docs/api/navigation.md +63 -0
- pywebflx-0.1.0/docs/api/navigation.pt.md +63 -0
- pywebflx-0.1.0/docs/api/synchronization.md +113 -0
- pywebflx-0.1.0/docs/api/synchronization.pt.md +113 -0
- pywebflx-0.1.0/docs/backlog.md +26 -0
- pywebflx-0.1.0/docs/examples/rpa.md +118 -0
- pywebflx-0.1.0/docs/examples/rpa.pt.md +118 -0
- pywebflx-0.1.0/docs/examples/scraping.md +116 -0
- pywebflx-0.1.0/docs/examples/scraping.pt.md +116 -0
- pywebflx-0.1.0/docs/index.md +68 -0
- pywebflx-0.1.0/docs/index.pt.md +68 -0
- pywebflx-0.1.0/docs/installation.md +55 -0
- pywebflx-0.1.0/docs/installation.pt.md +55 -0
- pywebflx-0.1.0/docs/superpowers/plans/2026-03-28-pywebflx-implementation.md +4341 -0
- pywebflx-0.1.0/docs/superpowers/specs/2026-03-28-pywebflx-design.md +604 -0
- pywebflx-0.1.0/extension/background.js +660 -0
- pywebflx-0.1.0/extension/icons/icon128.png +0 -0
- pywebflx-0.1.0/extension/icons/icon16.png +0 -0
- pywebflx-0.1.0/extension/icons/icon48.png +0 -0
- pywebflx-0.1.0/extension/manifest.json +24 -0
- pywebflx-0.1.0/mkdocs.yml +88 -0
- pywebflx-0.1.0/pyproject.toml +63 -0
- pywebflx-0.1.0/setup.cfg +4 -0
- pywebflx-0.1.0/src/pywebflx/__init__.py +58 -0
- pywebflx-0.1.0/src/pywebflx/__pycache__/__init__.cpython-314.pyc +0 -0
- pywebflx-0.1.0/src/pywebflx/actions/__init__.py +1 -0
- pywebflx-0.1.0/src/pywebflx/browser.py +591 -0
- pywebflx-0.1.0/src/pywebflx/cli.py +206 -0
- pywebflx-0.1.0/src/pywebflx/config.py +110 -0
- pywebflx-0.1.0/src/pywebflx/connection.py +200 -0
- pywebflx-0.1.0/src/pywebflx/exceptions.py +230 -0
- pywebflx-0.1.0/src/pywebflx/extension/background.js +660 -0
- pywebflx-0.1.0/src/pywebflx/extension/icons/icon128.png +0 -0
- pywebflx-0.1.0/src/pywebflx/extension/icons/icon16.png +0 -0
- pywebflx-0.1.0/src/pywebflx/extension/icons/icon48.png +0 -0
- pywebflx-0.1.0/src/pywebflx/extension/manifest.json +24 -0
- pywebflx-0.1.0/src/pywebflx/logging.py +187 -0
- pywebflx-0.1.0/src/pywebflx/protocol.py +107 -0
- pywebflx-0.1.0/src/pywebflx/selectors.py +143 -0
- pywebflx-0.1.0/src/pywebflx/tab_manager.py +83 -0
- pywebflx-0.1.0/src/pywebflx.egg-info/PKG-INFO +90 -0
- pywebflx-0.1.0/src/pywebflx.egg-info/SOURCES.txt +78 -0
- pywebflx-0.1.0/src/pywebflx.egg-info/dependency_links.txt +1 -0
- pywebflx-0.1.0/src/pywebflx.egg-info/entry_points.txt +2 -0
- pywebflx-0.1.0/src/pywebflx.egg-info/requires.txt +10 -0
- pywebflx-0.1.0/src/pywebflx.egg-info/top_level.txt +1 -0
- pywebflx-0.1.0/teste.py +24 -0
- pywebflx-0.1.0/teste_scraping.py +123 -0
- pywebflx-0.1.0/teste_toscrape.py +35 -0
- pywebflx-0.1.0/tests/conftest.py +1 -0
- pywebflx-0.1.0/tests/test_browser.py +59 -0
- pywebflx-0.1.0/tests/test_cli.py +28 -0
- pywebflx-0.1.0/tests/test_config.py +115 -0
- pywebflx-0.1.0/tests/test_connection.py +66 -0
- pywebflx-0.1.0/tests/test_exceptions.py +113 -0
- pywebflx-0.1.0/tests/test_logging.py +131 -0
- pywebflx-0.1.0/tests/test_protocol.py +84 -0
- pywebflx-0.1.0/tests/test_regression_e2e.py +464 -0
- pywebflx-0.1.0/tests/test_selectors.py +72 -0
- pywebflx-0.1.0/tests/test_tab_manager.py +114 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master, main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master, main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, windows-latest]
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
python -m pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Run unit tests
|
|
31
|
+
run: python -m pytest tests/ -v --ignore=tests/test_regression_e2e.py
|
|
32
|
+
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: |
|
|
35
|
+
python -m pip install build
|
|
36
|
+
python -m build
|
|
37
|
+
|
|
38
|
+
- name: Verify extension in wheel
|
|
39
|
+
run: python -c "import pywebflx.cli; print(pywebflx.cli._get_extension_dir())"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.13"
|
|
21
|
+
|
|
22
|
+
- name: Install build tools
|
|
23
|
+
run: |
|
|
24
|
+
python -m pip install --upgrade pip
|
|
25
|
+
python -m pip install build
|
|
26
|
+
|
|
27
|
+
- name: Build package
|
|
28
|
+
run: python -m build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
32
|
+
with:
|
|
33
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
pywebflx-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# PyWebFlx - Project Instructions
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
PyWebFlx is a Python browser automation library that connects to already-open Chrome pages via Chrome Extension (Manifest V3) + WebSocket. The Python side runs a WebSocket server; the extension connects as a client.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
- **Python package**: `src/pywebflx/` (async/await API)
|
|
10
|
+
- **Chrome Extension**: `extension/` (Manifest V3, service worker)
|
|
11
|
+
- **Documentation**: `docs/` (MkDocs Material, bilingual EN + PT-BR)
|
|
12
|
+
- **Tests**: `tests/`
|
|
13
|
+
|
|
14
|
+
## Documentation Rules
|
|
15
|
+
|
|
16
|
+
- **ALWAYS** update documentation when changing the API, adding features, or modifying behavior.
|
|
17
|
+
- Documentation lives in `docs/` with MkDocs Material + mkdocs-static-i18n.
|
|
18
|
+
- Every `.md` file has a `.pt.md` counterpart (PT-BR translation). **Always update both**.
|
|
19
|
+
- PT-BR docs must use proper accents (acentuacao correta).
|
|
20
|
+
- After updating docs, deploy with: `"C:/Program Files/Python313/python.exe" -m mkdocs gh-deploy --force`
|
|
21
|
+
- Do NOT mention UiPath in any documentation.
|
|
22
|
+
|
|
23
|
+
## Key Commands
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Run tests
|
|
27
|
+
"C:/Program Files/Python313/python.exe" -m pytest tests/ -v
|
|
28
|
+
|
|
29
|
+
# Deploy docs
|
|
30
|
+
"C:/Program Files/Python313/python.exe" -m mkdocs gh-deploy --force
|
|
31
|
+
|
|
32
|
+
# Install in dev mode
|
|
33
|
+
"C:/Program Files/Python313/python.exe" -m pip install -e ".[dev]"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Conventions
|
|
37
|
+
|
|
38
|
+
- Python path on this machine: `C:/Program Files/Python313/python.exe`
|
|
39
|
+
- WebSocket port: 9819
|
|
40
|
+
- All Python code uses async/await with asyncio
|
|
41
|
+
- Extension uses chrome.scripting.executeScript (no eval/new Function due to CSP)
|
|
42
|
+
- The `open` parameter in `use_browser()` follows: `"if_not_open"` (default), `"open"`, `"never"`
|
|
43
|
+
- URL and title matching is partial (contains), not exact
|
|
44
|
+
|
|
45
|
+
## Git
|
|
46
|
+
|
|
47
|
+
- Remote: https://github.com/theflexa/pywebflx
|
|
48
|
+
- Author: Flexa <the.flexa@outlook.com>
|
|
49
|
+
- Docs site: https://theflexa.github.io/pywebflx/
|
|
50
|
+
- Main branch: `master`
|
pywebflx-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pywebflx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Browser automation for already-open Chrome pages via Extension + WebSocket
|
|
5
|
+
Author-email: Flexa <the.flexa@outlook.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/theflexa/pywebflx
|
|
8
|
+
Project-URL: Documentation, https://theflexa.github.io/pywebflx/
|
|
9
|
+
Project-URL: Repository, https://github.com/theflexa/pywebflx
|
|
10
|
+
Project-URL: Issues, https://github.com/theflexa/pywebflx/issues
|
|
11
|
+
Keywords: browser,automation,chrome,rpa,websocket,scraping
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Testing
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
21
|
+
Classifier: Framework :: AsyncIO
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: websockets>=12.0
|
|
25
|
+
Requires-Dist: loguru>=0.7.0
|
|
26
|
+
Requires-Dist: click>=8.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
31
|
+
Requires-Dist: build; extra == "dev"
|
|
32
|
+
Requires-Dist: twine; extra == "dev"
|
|
33
|
+
|
|
34
|
+
# PyWebFlx
|
|
35
|
+
|
|
36
|
+
**Automação de navegador para páginas Chrome já abertas.**
|
|
37
|
+
|
|
38
|
+
PyWebFlx conecta a páginas já abertas no Chrome via uma extensão (Manifest V3) + WebSocket. Diferente do Selenium, não cria sandbox — você automatiza o navegador real.
|
|
39
|
+
|
|
40
|
+
## Instalacao
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install pywebflx
|
|
44
|
+
pywebflx install-extension
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import asyncio
|
|
51
|
+
from pywebflx import use_browser
|
|
52
|
+
|
|
53
|
+
async def main():
|
|
54
|
+
async with use_browser(url="https://quotes.toscrape.com/") as browser:
|
|
55
|
+
# Inspect — entender a pagina
|
|
56
|
+
print(await browser.inspect(depth=5))
|
|
57
|
+
|
|
58
|
+
# Extrair dados
|
|
59
|
+
quotes = await browser.extract_data(
|
|
60
|
+
container="body",
|
|
61
|
+
row=".quote",
|
|
62
|
+
columns={"texto": ".text", "autor": ".author"}
|
|
63
|
+
)
|
|
64
|
+
for q in quotes:
|
|
65
|
+
print(f"{q['autor']}: {q['texto'][:60]}...")
|
|
66
|
+
|
|
67
|
+
asyncio.run(main())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Principais funcionalidades
|
|
71
|
+
|
|
72
|
+
| Metodo | Descricao |
|
|
73
|
+
|--------|-----------|
|
|
74
|
+
| `use_browser()` | Conecta a aba por URL/titulo, abre se nao existir |
|
|
75
|
+
| `click()` | Clica (CSS, XPath ou texto) |
|
|
76
|
+
| `type_into()` | Digita em campos |
|
|
77
|
+
| `get_text()` | Extrai texto de elemento |
|
|
78
|
+
| `extract_data()` | Extrai dados estruturados (cards, listas) |
|
|
79
|
+
| `extract_table()` | Extrai tabelas HTML com paginacao |
|
|
80
|
+
| `inspect()` | Visao resumida do DOM (otimizado pra IA) |
|
|
81
|
+
| `wait_element()` | Espera elemento aparecer |
|
|
82
|
+
| `execute_js()` | Executa JavaScript na pagina |
|
|
83
|
+
|
|
84
|
+
## Documentacao
|
|
85
|
+
|
|
86
|
+
[https://theflexa.github.io/pywebflx/](https://theflexa.github.io/pywebflx/)
|
|
87
|
+
|
|
88
|
+
## Licenca
|
|
89
|
+
|
|
90
|
+
MIT
|
pywebflx-0.1.0/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# PyWebFlx
|
|
2
|
+
|
|
3
|
+
**Automação de navegador para páginas Chrome já abertas.**
|
|
4
|
+
|
|
5
|
+
PyWebFlx conecta a páginas já abertas no Chrome via uma extensão (Manifest V3) + WebSocket. Diferente do Selenium, não cria sandbox — você automatiza o navegador real.
|
|
6
|
+
|
|
7
|
+
## Instalacao
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install pywebflx
|
|
11
|
+
pywebflx install-extension
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import asyncio
|
|
18
|
+
from pywebflx import use_browser
|
|
19
|
+
|
|
20
|
+
async def main():
|
|
21
|
+
async with use_browser(url="https://quotes.toscrape.com/") as browser:
|
|
22
|
+
# Inspect — entender a pagina
|
|
23
|
+
print(await browser.inspect(depth=5))
|
|
24
|
+
|
|
25
|
+
# Extrair dados
|
|
26
|
+
quotes = await browser.extract_data(
|
|
27
|
+
container="body",
|
|
28
|
+
row=".quote",
|
|
29
|
+
columns={"texto": ".text", "autor": ".author"}
|
|
30
|
+
)
|
|
31
|
+
for q in quotes:
|
|
32
|
+
print(f"{q['autor']}: {q['texto'][:60]}...")
|
|
33
|
+
|
|
34
|
+
asyncio.run(main())
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Principais funcionalidades
|
|
38
|
+
|
|
39
|
+
| Metodo | Descricao |
|
|
40
|
+
|--------|-----------|
|
|
41
|
+
| `use_browser()` | Conecta a aba por URL/titulo, abre se nao existir |
|
|
42
|
+
| `click()` | Clica (CSS, XPath ou texto) |
|
|
43
|
+
| `type_into()` | Digita em campos |
|
|
44
|
+
| `get_text()` | Extrai texto de elemento |
|
|
45
|
+
| `extract_data()` | Extrai dados estruturados (cards, listas) |
|
|
46
|
+
| `extract_table()` | Extrai tabelas HTML com paginacao |
|
|
47
|
+
| `inspect()` | Visao resumida do DOM (otimizado pra IA) |
|
|
48
|
+
| `wait_element()` | Espera elemento aparecer |
|
|
49
|
+
| `execute_js()` | Executa JavaScript na pagina |
|
|
50
|
+
|
|
51
|
+
## Documentacao
|
|
52
|
+
|
|
53
|
+
[https://theflexa.github.io/pywebflx/](https://theflexa.github.io/pywebflx/)
|
|
54
|
+
|
|
55
|
+
## Licenca
|
|
56
|
+
|
|
57
|
+
MIT
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# use_browser
|
|
2
|
+
|
|
3
|
+
Main entry point for PyWebFlx. Connects to a Chrome tab and returns a `BrowserContext` with all automation methods.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
async with use_browser(
|
|
9
|
+
url: str = None,
|
|
10
|
+
title: str = None,
|
|
11
|
+
open: str = "if_not_open",
|
|
12
|
+
config: PyWebFlxConfig = None,
|
|
13
|
+
) as browser:
|
|
14
|
+
...
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Parameters
|
|
18
|
+
|
|
19
|
+
| Parameter | Type | Default | Description |
|
|
20
|
+
|-----------|------|---------|-------------|
|
|
21
|
+
| `url` | `str` | `None` | URL to search (partial match) and/or open |
|
|
22
|
+
| `title` | `str` | `None` | Tab title to search (partial match) |
|
|
23
|
+
| `open` | `str` | `"if_not_open"` | Opening behavior (see below) |
|
|
24
|
+
| `config` | `PyWebFlxConfig` | `None` | Custom configuration |
|
|
25
|
+
|
|
26
|
+
!!! info "Partial match"
|
|
27
|
+
Both `url` and `title` use **partial match** (contains). You don't need the full URL — just the fixed part is enough. This is especially useful for dynamic URLs:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
# Matches any tab at mycon.gupy.io/candidates/...
|
|
31
|
+
async with use_browser(url="mycon.gupy.io/candidates") as browser:
|
|
32
|
+
...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
!!! tip "Combining `url` and `title`"
|
|
36
|
+
You can use both parameters together. The search uses **AND** logic — the tab must match both criteria:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# Tab must contain "gupy.io" in URL AND "Candidatos" in title
|
|
40
|
+
async with use_browser(url="gupy.io", title="Candidatos") as browser:
|
|
41
|
+
...
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### `open` parameter
|
|
45
|
+
|
|
46
|
+
| Value | Behavior |
|
|
47
|
+
|-------|----------|
|
|
48
|
+
| `"if_not_open"` | Searches for an existing tab. If not found, opens the `url`. **(default)** |
|
|
49
|
+
| `"open"` | Always opens a new tab with the `url`. |
|
|
50
|
+
| `"never"` | Only connects to an existing tab. Never opens. |
|
|
51
|
+
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
### Basic -- opens if not found
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
async with use_browser(url="https://quotes.toscrape.com/") as browser:
|
|
58
|
+
await browser.click("#btn")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Search by title
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
async with use_browser(title="Portal Sicoob", open="never") as browser:
|
|
65
|
+
balance = await browser.get_text(".balance")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Always open a new tab
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
async with use_browser(url="https://example.com", open="open") as browser:
|
|
72
|
+
await browser.type_into("#email", text="user@email.com")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### With custom configuration
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pywebflx import PyWebFlxConfig
|
|
79
|
+
|
|
80
|
+
config = PyWebFlxConfig(default_timeout=30, retry_count=3)
|
|
81
|
+
|
|
82
|
+
async with use_browser(url="https://example.com", config=config) as browser:
|
|
83
|
+
await browser.click("#btn", timeout=60) # 60 overrides 30
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Multiple tabs
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
async with use_browser(url="https://portal.com") as tab1:
|
|
90
|
+
async with use_browser(url="https://email.com") as tab2:
|
|
91
|
+
data = await tab1.get_text(".balance")
|
|
92
|
+
await tab2.type_into("#body", text=data)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Exceptions
|
|
96
|
+
|
|
97
|
+
| Exception | When |
|
|
98
|
+
|-----------|------|
|
|
99
|
+
| `BrowserNotFoundError` | Tab not found and `open="never"` |
|
|
100
|
+
| `ExtensionNotConnectedError` | Chrome extension is not connected |
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# use_browser
|
|
2
|
+
|
|
3
|
+
Ponto de entrada principal do PyWebFlx. Conecta a uma aba do Chrome e retorna um `BrowserContext` com todos os métodos de automação.
|
|
4
|
+
|
|
5
|
+
## Assinatura
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
async with use_browser(
|
|
9
|
+
url: str = None,
|
|
10
|
+
title: str = None,
|
|
11
|
+
open: str = "if_not_open",
|
|
12
|
+
config: PyWebFlxConfig = None,
|
|
13
|
+
) as browser:
|
|
14
|
+
...
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Parâmetros
|
|
18
|
+
|
|
19
|
+
| Parâmetro | Tipo | Padrão | Descrição |
|
|
20
|
+
|-----------|------|--------|-----------|
|
|
21
|
+
| `url` | `str` | `None` | URL para buscar (correspondência parcial) e/ou abrir |
|
|
22
|
+
| `title` | `str` | `None` | Título da aba para buscar (correspondência parcial) |
|
|
23
|
+
| `open` | `str` | `"if_not_open"` | Comportamento de abertura (veja abaixo) |
|
|
24
|
+
| `config` | `PyWebFlxConfig` | `None` | Configuração personalizada |
|
|
25
|
+
|
|
26
|
+
!!! info "Correspondência parcial"
|
|
27
|
+
Tanto `url` quanto `title` usam **correspondência parcial** (contém). Não é necessário a URL completa — basta o trecho fixo. Isso é especialmente útil para URLs dinâmicas:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
# Encontra qualquer aba em mycon.gupy.io/candidates/...
|
|
31
|
+
async with use_browser(url="mycon.gupy.io/candidates") as browser:
|
|
32
|
+
...
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
!!! tip "Combinando `url` e `title`"
|
|
36
|
+
Você pode usar ambos os parâmetros juntos. A busca usa lógica **AND** — a aba precisa corresponder aos dois critérios:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# Aba deve conter "gupy.io" na URL E "Candidatos" no título
|
|
40
|
+
async with use_browser(url="gupy.io", title="Candidatos") as browser:
|
|
41
|
+
...
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Parâmetro `open`
|
|
45
|
+
|
|
46
|
+
| Valor | Comportamento |
|
|
47
|
+
|-------|---------------|
|
|
48
|
+
| `"if_not_open"` | Busca uma aba existente. Se não encontrar, abre a `url`. **(padrão)** |
|
|
49
|
+
| `"open"` | Sempre abre uma nova aba com a `url`. |
|
|
50
|
+
| `"never"` | Apenas conecta a uma aba existente. Nunca abre. |
|
|
51
|
+
|
|
52
|
+
## Exemplos
|
|
53
|
+
|
|
54
|
+
### Básico -- abre se não encontrar
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
async with use_browser(url="https://quotes.toscrape.com/") as browser:
|
|
58
|
+
await browser.click("#btn")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Buscar por título
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
async with use_browser(title="Portal Sicoob", open="never") as browser:
|
|
65
|
+
balance = await browser.get_text(".balance")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Sempre abrir uma nova aba
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
async with use_browser(url="https://example.com", open="open") as browser:
|
|
72
|
+
await browser.type_into("#email", text="user@email.com")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Com configuração personalizada
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pywebflx import PyWebFlxConfig
|
|
79
|
+
|
|
80
|
+
config = PyWebFlxConfig(default_timeout=30, retry_count=3)
|
|
81
|
+
|
|
82
|
+
async with use_browser(url="https://example.com", config=config) as browser:
|
|
83
|
+
await browser.click("#btn", timeout=60) # 60 sobrescreve 30
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Múltiplas abas
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
async with use_browser(url="https://portal.com") as tab1:
|
|
90
|
+
async with use_browser(url="https://email.com") as tab2:
|
|
91
|
+
data = await tab1.get_text(".balance")
|
|
92
|
+
await tab2.type_into("#body", text=data)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Exceções
|
|
96
|
+
|
|
97
|
+
| Exceção | Quando |
|
|
98
|
+
|---------|--------|
|
|
99
|
+
| `BrowserNotFoundError` | Aba não encontrada e `open="never"` |
|
|
100
|
+
| `ExtensionNotConnectedError` | Extensão do Chrome não está conectada |
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
`PyWebFlxConfig` controls timeouts, retries, WebSocket port, and logging.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from pywebflx import PyWebFlxConfig
|
|
9
|
+
|
|
10
|
+
config = PyWebFlxConfig(
|
|
11
|
+
default_timeout=10,
|
|
12
|
+
delay_between_actions=0.3,
|
|
13
|
+
retry_count=0,
|
|
14
|
+
on_error="raise",
|
|
15
|
+
ws_port=9819,
|
|
16
|
+
log_level="INFO",
|
|
17
|
+
)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Parameters
|
|
21
|
+
|
|
22
|
+
| Parameter | Type | Default | Description |
|
|
23
|
+
|-----------|------|---------|-------------|
|
|
24
|
+
| `default_timeout` | `float` | `10` | Default timeout in seconds |
|
|
25
|
+
| `delay_between_actions` | `float` | `0.3` | Delay between consecutive actions |
|
|
26
|
+
| `retry_count` | `int` | `0` | Default retry attempts on failure |
|
|
27
|
+
| `on_error` | `str` | `"raise"` | `"raise"` or `"continue"` |
|
|
28
|
+
| `ws_port` | `int` | `9819` | WebSocket server port |
|
|
29
|
+
| `log_level` | `str` | `"INFO"` | Log level |
|
|
30
|
+
|
|
31
|
+
## Priority
|
|
32
|
+
|
|
33
|
+
Parameter resolution follows this order:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
action parameter > use_browser config > global defaults
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
# Global: timeout = 10 (default)
|
|
41
|
+
# Config: timeout = 20
|
|
42
|
+
# Action: timeout = 60 (wins)
|
|
43
|
+
|
|
44
|
+
config = PyWebFlxConfig(default_timeout=20)
|
|
45
|
+
|
|
46
|
+
async with use_browser(url="https://example.com", config=config) as browser:
|
|
47
|
+
await browser.click("#btn", timeout=60) # uses 60
|
|
48
|
+
await browser.click("#btn2") # uses 20
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Global defaults
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# Affect all new instances
|
|
55
|
+
PyWebFlxConfig.set_defaults(default_timeout=15, retry_count=3)
|
|
56
|
+
|
|
57
|
+
# Reset to original values
|
|
58
|
+
PyWebFlxConfig.reset_defaults()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Logging
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pywebflx import configure_logging
|
|
65
|
+
|
|
66
|
+
configure_logging(level="INFO") # console (default)
|
|
67
|
+
configure_logging(level="DEBUG", sink="automation.log") # file
|
|
68
|
+
configure_logging(level="DISABLED") # silent
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Levels
|
|
72
|
+
|
|
73
|
+
| Level | What it logs |
|
|
74
|
+
|-------|-------------|
|
|
75
|
+
| `ERROR` | Final failure (after all retries) |
|
|
76
|
+
| `WARN` | Retry, reconnection, fallback |
|
|
77
|
+
| `INFO` | Action executed successfully |
|
|
78
|
+
| `DEBUG` | Internal details (selector, JSON payload) |
|
|
79
|
+
| `TRACE` | Everything (WebSocket bytes, partial DOM) |
|
|
80
|
+
| `DISABLED` | Nothing |
|
|
81
|
+
|
|
82
|
+
### Format
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
2026-03-28 14:32:05.123 [INFO] [browser.click] #btn-save -> tab:42 -> success (120ms)
|
|
86
|
+
2026-03-28 14:32:06.102 [WARN] [browser.click] #btn-send -> tab:42 -> retry 1/3
|
|
87
|
+
2026-03-28 14:32:08.001 [ERROR] [browser.extract_table] #table -> tab:42 -> failed: TabClosedError
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Per-action override
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
await browser.click("#critical-btn", log_level="DEBUG") # more detail
|
|
94
|
+
await browser.click(row_selector, log_level="WARN") # less noise in loops
|
|
95
|
+
```
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Configuração
|
|
2
|
+
|
|
3
|
+
`PyWebFlxConfig` controla timeouts, retentativas, porta WebSocket e logging.
|
|
4
|
+
|
|
5
|
+
## Assinatura
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from pywebflx import PyWebFlxConfig
|
|
9
|
+
|
|
10
|
+
config = PyWebFlxConfig(
|
|
11
|
+
default_timeout=10,
|
|
12
|
+
delay_between_actions=0.3,
|
|
13
|
+
retry_count=0,
|
|
14
|
+
on_error="raise",
|
|
15
|
+
ws_port=9819,
|
|
16
|
+
log_level="INFO",
|
|
17
|
+
)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Parâmetros
|
|
21
|
+
|
|
22
|
+
| Parâmetro | Tipo | Padrão | Descrição |
|
|
23
|
+
|-----------|------|--------|-----------|
|
|
24
|
+
| `default_timeout` | `float` | `10` | Timeout padrão em segundos |
|
|
25
|
+
| `delay_between_actions` | `float` | `0.3` | Atraso entre ações consecutivas |
|
|
26
|
+
| `retry_count` | `int` | `0` | Tentativas de retentativa padrão em caso de falha |
|
|
27
|
+
| `on_error` | `str` | `"raise"` | `"raise"` ou `"continue"` |
|
|
28
|
+
| `ws_port` | `int` | `9819` | Porta do servidor WebSocket |
|
|
29
|
+
| `log_level` | `str` | `"INFO"` | Nível de log |
|
|
30
|
+
|
|
31
|
+
## Prioridade
|
|
32
|
+
|
|
33
|
+
A resolução de parâmetros segue esta ordem:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
parâmetro da ação > config do use_browser > padrões globais
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
# Global: timeout = 10 (padrão)
|
|
41
|
+
# Config: timeout = 20
|
|
42
|
+
# Ação: timeout = 60 (vence)
|
|
43
|
+
|
|
44
|
+
config = PyWebFlxConfig(default_timeout=20)
|
|
45
|
+
|
|
46
|
+
async with use_browser(url="https://example.com", config=config) as browser:
|
|
47
|
+
await browser.click("#btn", timeout=60) # usa 60
|
|
48
|
+
await browser.click("#btn2") # usa 20
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Padrões globais
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# Afeta todas as novas instancias
|
|
55
|
+
PyWebFlxConfig.set_defaults(default_timeout=15, retry_count=3)
|
|
56
|
+
|
|
57
|
+
# Resetar para valores originais
|
|
58
|
+
PyWebFlxConfig.reset_defaults()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Logging
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from pywebflx import configure_logging
|
|
65
|
+
|
|
66
|
+
configure_logging(level="INFO") # console (padrão)
|
|
67
|
+
configure_logging(level="DEBUG", sink="automation.log") # arquivo
|
|
68
|
+
configure_logging(level="DISABLED") # silencioso
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Níveis
|
|
72
|
+
|
|
73
|
+
| Nível | O que registra |
|
|
74
|
+
|-------|----------------|
|
|
75
|
+
| `ERROR` | Falha final (após todas as retentativas) |
|
|
76
|
+
| `WARN` | Retentativa, reconexão, fallback |
|
|
77
|
+
| `INFO` | Ação executada com sucesso |
|
|
78
|
+
| `DEBUG` | Detalhes internos (seletor, payload JSON) |
|
|
79
|
+
| `TRACE` | Tudo (bytes WebSocket, DOM parcial) |
|
|
80
|
+
| `DISABLED` | Nada |
|
|
81
|
+
|
|
82
|
+
### Formato
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
2026-03-28 14:32:05.123 [INFO] [browser.click] #btn-save -> tab:42 -> sucesso (120ms)
|
|
86
|
+
2026-03-28 14:32:06.102 [WARN] [browser.click] #btn-send -> tab:42 -> retentativa 1/3
|
|
87
|
+
2026-03-28 14:32:08.001 [ERROR] [browser.extract_table] #table -> tab:42 -> falhou: TabClosedError
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Override por ação
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
await browser.click("#critical-btn", log_level="DEBUG") # mais detalhes
|
|
94
|
+
await browser.click(row_selector, log_level="WARN") # menos ruído em loops
|
|
95
|
+
```
|