smartwright 0.2.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.
- smartwright-0.2.0/LICENSE +21 -0
- smartwright-0.2.0/PKG-INFO +523 -0
- smartwright-0.2.0/README.md +487 -0
- smartwright-0.2.0/pyproject.toml +81 -0
- smartwright-0.2.0/setup.cfg +4 -0
- smartwright-0.2.0/smartwright/__init__.py +1224 -0
- smartwright-0.2.0/smartwright/_logging.py +27 -0
- smartwright-0.2.0/smartwright/ai_recovery/__init__.py +1 -0
- smartwright-0.2.0/smartwright/ai_recovery/groq_provider.py +82 -0
- smartwright-0.2.0/smartwright/ai_recovery/recovery.py +41 -0
- smartwright-0.2.0/smartwright/api_executor/__init__.py +1 -0
- smartwright-0.2.0/smartwright/api_executor/executor.py +22 -0
- smartwright-0.2.0/smartwright/constants.py +74 -0
- smartwright-0.2.0/smartwright/core/__init__.py +1 -0
- smartwright-0.2.0/smartwright/core/engine.py +146 -0
- smartwright-0.2.0/smartwright/core/models.py +33 -0
- smartwright-0.2.0/smartwright/core/store.py +68 -0
- smartwright-0.2.0/smartwright/exceptions.py +63 -0
- smartwright-0.2.0/smartwright/fingerprint/__init__.py +1 -0
- smartwright-0.2.0/smartwright/fingerprint/tracker.py +17 -0
- smartwright-0.2.0/smartwright/healing/__init__.py +1 -0
- smartwright-0.2.0/smartwright/healing/layer.py +18 -0
- smartwright-0.2.0/smartwright/intent/__init__.py +1 -0
- smartwright-0.2.0/smartwright/intent/manager.py +39 -0
- smartwright-0.2.0/smartwright/network_learning/__init__.py +1 -0
- smartwright-0.2.0/smartwright/network_learning/observer.py +466 -0
- smartwright-0.2.0/smartwright/py.typed +0 -0
- smartwright-0.2.0/smartwright/recorder/__init__.py +799 -0
- smartwright-0.2.0/smartwright/resolver/__init__.py +4 -0
- smartwright-0.2.0/smartwright/resolver/_base.py +109 -0
- smartwright-0.2.0/smartwright/resolver/_capture.py +325 -0
- smartwright-0.2.0/smartwright/resolver/_content.py +365 -0
- smartwright-0.2.0/smartwright/resolver/_debug.py +197 -0
- smartwright-0.2.0/smartwright/resolver/_form.py +146 -0
- smartwright-0.2.0/smartwright/resolver/_helpers.py +453 -0
- smartwright-0.2.0/smartwright/resolver/_interact.py +413 -0
- smartwright-0.2.0/smartwright/resolver/_page.py +774 -0
- smartwright-0.2.0/smartwright/resolver/_replay.py +598 -0
- smartwright-0.2.0/smartwright/resolver/_response.py +539 -0
- smartwright-0.2.0/smartwright/resolver/_run_json.py +845 -0
- smartwright-0.2.0/smartwright/resolver/adaptive.py +107 -0
- smartwright-0.2.0/smartwright/resolver/adaptive_replay.py +578 -0
- smartwright-0.2.0/smartwright/resolver/dom_serializer.py +528 -0
- smartwright-0.2.0/smartwright/resolver/emergency.py +56 -0
- smartwright-0.2.0/smartwright/resolver/replay_mode.py +185 -0
- smartwright-0.2.0/smartwright/semantic_finder/__init__.py +1 -0
- smartwright-0.2.0/smartwright/semantic_finder/finder.py +35 -0
- smartwright-0.2.0/smartwright/stealth/__init__.py +447 -0
- smartwright-0.2.0/smartwright.egg-info/PKG-INFO +523 -0
- smartwright-0.2.0/smartwright.egg-info/SOURCES.txt +52 -0
- smartwright-0.2.0/smartwright.egg-info/dependency_links.txt +1 -0
- smartwright-0.2.0/smartwright.egg-info/requires.txt +11 -0
- smartwright-0.2.0/smartwright.egg-info/top_level.txt +1 -0
- smartwright-0.2.0/tests/test_smartwright_complete.py +539 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 smartwright contributors
|
|
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,523 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smartwright
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Adaptive web automation engine — intent-driven, type+index, replay, DOM serializer, network learning
|
|
5
|
+
Author: smartwright contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/lhabacuc/smartwright/tree/main/docs
|
|
8
|
+
Project-URL: Repository, https://github.com/lhabacuc/smartwright
|
|
9
|
+
Project-URL: Changelog, https://github.com/lhabacuc/smartwright/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: playwright,automation,web,scraping,testing,replay,adaptive
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
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
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: playwright<2.0,>=1.45
|
|
27
|
+
Provides-Extra: ai
|
|
28
|
+
Requires-Dist: groq<1.0,>=0.9.0; extra == "ai"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# Smartwright
|
|
38
|
+
|
|
39
|
+
Motor de automacao web adaptativo com gravacao ao vivo, replay inteligente e 270+ funcoes para qualquer tarefa.
|
|
40
|
+
|
|
41
|
+
## Instalacao
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install playwright
|
|
45
|
+
playwright install chromium
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## CLI (qwen-cap.py)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python qwen-cap.py record # grava acoes em recording.json
|
|
52
|
+
python qwen-cap.py record meu_fluxo.json # grava em arquivo customizado
|
|
53
|
+
python qwen-cap.py replay # replay com debug (padrao)
|
|
54
|
+
python qwen-cap.py replay --no-debug # sem screenshots/highlight
|
|
55
|
+
python qwen-cap.py replay --mode rapido # modo rapido
|
|
56
|
+
python qwen-cap.py replay --mode mix # modo mais resiliente
|
|
57
|
+
python qwen-cap.py full # record + replay sequencial
|
|
58
|
+
python qwen-cap.py full --mode forcado # full com modo forcado
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Modos de execucao
|
|
62
|
+
|
|
63
|
+
| Modo | Descricao | Uso ideal |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `rapido` | Sem verificacao, timeouts curtos, sem delay | Paginas estaveis, testes rapidos |
|
|
66
|
+
| `padrao` | Todos os 6 steps de resolucao (default) | Uso geral |
|
|
67
|
+
| `por_index` | So usa tag + indice ordinal | DOM fixo |
|
|
68
|
+
| `por_id_e_class` | So CSS com #id, .class, [data-testid] | Sites com bons IDs |
|
|
69
|
+
| `forcado` | Resolucao completa + force click | Overlays, modais |
|
|
70
|
+
| `mix` | Todos os steps + 3 retries + scroll | Maximo resiliencia |
|
|
71
|
+
|
|
72
|
+
## Uso como biblioteca — exemplos rapidos
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from smartwright import Smartwright
|
|
76
|
+
from smartwright.recorder import ActionRecorder
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Gravacao e Replay
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# Gravar acoes do usuario
|
|
83
|
+
recorder = ActionRecorder(save_path="acoes.json")
|
|
84
|
+
page = await recorder.start(url="https://example.com")
|
|
85
|
+
actions = await recorder.wait_until_closed()
|
|
86
|
+
|
|
87
|
+
# Replay com modo
|
|
88
|
+
smart = Smartwright(page=page, request_context=context.request)
|
|
89
|
+
results = await smart.emergency_replay_actions(actions, mode="mix")
|
|
90
|
+
|
|
91
|
+
# Replay direto do JSON
|
|
92
|
+
results = await smart.emergency_replay_json("acoes.json", mode="padrao")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Executar JSON manual (run_json)
|
|
96
|
+
|
|
97
|
+
Executa JSONs escritos a mao, tolerante com campos em falta. So precisa de `action` + os campos essenciais.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# ── Inline (lista de dicts) ──
|
|
101
|
+
results = await smart.run_json([
|
|
102
|
+
{"action": "goto", "url": "https://example.com"},
|
|
103
|
+
{"action": "fill", "selector": "#email", "value": "user@test.com"},
|
|
104
|
+
{"action": "fill", "text": "Password", "value": "123456"},
|
|
105
|
+
{"action": "click", "text": "Login"},
|
|
106
|
+
{"action": "wait", "ms": 2000},
|
|
107
|
+
{"action": "screenshot", "path": "resultado.png"},
|
|
108
|
+
])
|
|
109
|
+
|
|
110
|
+
# ── De arquivo JSON ──
|
|
111
|
+
results = await smart.run_json_file("meu_fluxo.json", mode="mix")
|
|
112
|
+
|
|
113
|
+
# ── Com base_url (URLs relativas) ──
|
|
114
|
+
results = await smart.run_json([
|
|
115
|
+
{"action": "goto", "url": "/login"},
|
|
116
|
+
{"action": "fill", "selector": "input[name=user]", "value": "admin"},
|
|
117
|
+
], base_url="https://meusite.com")
|
|
118
|
+
|
|
119
|
+
# ── Parar no primeiro erro ──
|
|
120
|
+
results = await smart.run_json(actions, continue_on_error=False)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**JSON minimo por acao:**
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
[
|
|
127
|
+
{"action": "goto", "url": "https://example.com"},
|
|
128
|
+
{"action": "click", "selector": "#btn"},
|
|
129
|
+
{"action": "click", "text": "Enviar"},
|
|
130
|
+
{"action": "fill", "selector": "#email", "value": "a@b.com"},
|
|
131
|
+
{"action": "fill", "text": "Senha", "value": "123"},
|
|
132
|
+
{"action": "select", "selector": "#pais", "value": "BR"},
|
|
133
|
+
{"action": "select", "selector": "#pais", "option": "Brasil"},
|
|
134
|
+
{"action": "check", "selector": "#aceito", "checked": true},
|
|
135
|
+
{"action": "press", "key": "Enter"},
|
|
136
|
+
{"action": "scroll", "dir": "down", "px": 500},
|
|
137
|
+
{"action": "wait", "ms": 3000},
|
|
138
|
+
{"action": "wait_text", "text": "Sucesso"},
|
|
139
|
+
{"action": "wait_element", "selector": "div.resultado"},
|
|
140
|
+
{"action": "screenshot", "path": "captura.png"},
|
|
141
|
+
{"action": "back"},
|
|
142
|
+
{"action": "js", "code": "document.title"},
|
|
143
|
+
{"action": "drag", "from": "#item", "to": "#destino"},
|
|
144
|
+
{"action": "upload", "selector": "input[type=file]", "file": "/path/doc.pdf"},
|
|
145
|
+
{"action": "download", "dir": "downloads"},
|
|
146
|
+
{"action": "copy", "value": "texto copiado"}
|
|
147
|
+
]
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Aliases aceitos:** `press`/`key`/`keys` → press_keys, `type`/`write`/`input` → fill, `go`/`open`/`nav` → goto, `tap` → click, `sleep`/`delay`/`pause` → wait, `snap` → screenshot, `back` → go_back, `forward` → go_forward, `refresh` → reload, `js`/`eval` → eval_js, `drag`/`dnd` → drag_drop, `checkbox` → check, `radio` → select_radio, `dropdown` → select_custom, `download` → wait_download, `clipboard` → wait_clipboard, `copy` → copy_to_clipboard, `alert`/`confirm`/`prompt` → dialog.
|
|
151
|
+
|
|
152
|
+
**Campos flexiveis:** `selector`/`css`/`sel`, `element_type`/`type`/`tag`, `index`/`idx`/`i`, `text`/`label`/`contains`, `value`/`val`, `url`/`href`/`link`, `timeout_ms`/`timeout`.
|
|
153
|
+
|
|
154
|
+
### Clicks
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
await smart.emergency_click("button", 0) # por tipo + indice
|
|
158
|
+
await smart.emergency_click_by_text("Enviar") # por texto visivel
|
|
159
|
+
await smart.emergency_click_by_role("button", 2) # por role ARIA
|
|
160
|
+
await smart.emergency_click_first_type_containing("button", "*salvar*") # glob pattern
|
|
161
|
+
await smart.emergency_click_by_type_at_index_containing("a", 0, "*download*") # tipo + pos + texto
|
|
162
|
+
await smart.emergency_click_link("Ver mais") # link por texto
|
|
163
|
+
await smart.double_click("div", 3) # duplo click
|
|
164
|
+
await smart.right_click("tr", 0) # click direito
|
|
165
|
+
await smart.click_at_coordinates(500, 300) # x, y absoluto
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Fill / Input
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
await smart.emergency_fill("input", 0, "user@test.com") # por tipo + indice
|
|
172
|
+
await smart.emergency_fill_first_type_containing("input", "*email*", "a@b.com") # glob pattern
|
|
173
|
+
await smart.emergency_fill_by_type_at_index_containing("input", 2, "*senha*", "123")
|
|
174
|
+
await smart.emergency_fill_by_label("Email", "user@test.com") # por label do campo
|
|
175
|
+
await smart.emergency_clear_input("input", 0) # limpar campo
|
|
176
|
+
val = await smart.emergency_read_input_value("input", 0) # ler valor atual
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Read / Extrair texto
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
text = await smart.emergency_read("span", 5) # texto por tipo + indice
|
|
183
|
+
text = await smart.emergency_read_by_text("Total:", 0) # por texto visivel
|
|
184
|
+
text = await smart.emergency_read_first_type_containing("div", "*preco*") # glob pattern
|
|
185
|
+
text = await smart.emergency_read_by_type_at_index_containing("p", 0, "*desc*") # tipo + pos + texto
|
|
186
|
+
texts = await smart.emergency_read_all("li") # todos os textos de um tipo
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Formularios
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
await smart.emergency_select_option("select", 0, "opcao_valor") # select por value
|
|
193
|
+
await smart.emergency_select_option_by_label("select", 0, "Opcao visivel") # select por label
|
|
194
|
+
sel = await smart.emergency_read_selected_option("select", 0) # ler selecionado
|
|
195
|
+
opts = await smart.emergency_read_all_options("select", 0) # listar opcoes
|
|
196
|
+
|
|
197
|
+
await smart.emergency_check("input", 3, checked=True) # marcar checkbox
|
|
198
|
+
await smart.emergency_toggle_checkbox("input", 3) # alternar checkbox
|
|
199
|
+
await smart.emergency_select_radio("genero", "masculino") # radio por name+value
|
|
200
|
+
await smart.emergency_upload_file("input", 0, "/path/file.pdf") # upload arquivo
|
|
201
|
+
|
|
202
|
+
state = await smart.emergency_read_form_state(0) # estado de todos os campos
|
|
203
|
+
await smart.emergency_submit_form(0) # submit form
|
|
204
|
+
await smart.emergency_reset_form(0) # reset form
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Tabelas
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
cell = await smart.emergency_read_table_cell(0, 2, 3) # tabela[0] linha 2 col 3
|
|
211
|
+
row = await smart.emergency_read_table_row(0, 1) # linha inteira
|
|
212
|
+
data = await smart.emergency_read_full_table(0) # tabela completa -> [[str]]
|
|
213
|
+
await smart.emergency_click_table_cell(0, 1, 2) # clicar na celula
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Listas
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
items = await smart.emergency_read_list_items(0, list_type="ul") # textos de uma <ul>
|
|
220
|
+
await smart.emergency_click_list_item(0, 2, list_type="ul") # clicar no 3o item
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Links
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
await smart.emergency_click_link("Saiba mais", index=0) # clicar link por texto
|
|
227
|
+
href = await smart.emergency_get_link_href("Download") # pegar href
|
|
228
|
+
links = await smart.emergency_capture_all_links() # todos os links da pagina
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Hover / Scroll / Teclas
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
await smart.emergency_hover("div", 0) # hover no elemento
|
|
235
|
+
await smart.emergency_scroll_page("down", 500) # scroll direcional
|
|
236
|
+
await smart.emergency_scroll_to_top() # topo da pagina
|
|
237
|
+
await smart.emergency_scroll_to_bottom() # final da pagina
|
|
238
|
+
await smart.emergency_scroll_to("div.footer") # scroll ate elemento
|
|
239
|
+
await smart.mouse_move(400, 200) # mover mouse
|
|
240
|
+
await smart.mouse_wheel(0, -500) # scroll por mouse wheel
|
|
241
|
+
await smart.emergency_press_keys("Enter") # teclas especiais
|
|
242
|
+
await smart.emergency_press_keys("Control+a") # atalhos
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Waits / Esperas
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
el = await smart.emergency_wait_for_element("div.resultado", timeout_ms=15000) # esperar elemento
|
|
249
|
+
await smart.emergency_wait_for_text("Sucesso!", timeout_ms=10000) # esperar texto visivel
|
|
250
|
+
url = await smart.emergency_wait_for_url_contains("/dashboard") # esperar URL mudar
|
|
251
|
+
await smart.wait_for_load("networkidle", timeout_ms=30000) # esperar pagina carregar
|
|
252
|
+
resp = await smart.wait_for_response("*/api/data*", timeout_ms=30000) # esperar resposta HTTP
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Download e Clipboard
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
dl = await smart.emergency_wait_download(save_dir="downloads", timeout_ms=30000)
|
|
259
|
+
print(dl["filename"], dl["path"], dl["size"])
|
|
260
|
+
|
|
261
|
+
clip = await smart.emergency_wait_clipboard(timeout_ms=10000)
|
|
262
|
+
print(clip["text"])
|
|
263
|
+
|
|
264
|
+
await smart.emergency_copy_to_clipboard("texto copiado")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Estado dos elementos
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
exists = await smart.element_exists("div.modal") # existe no DOM?
|
|
271
|
+
count = await smart.element_count("li.item") # quantos existem?
|
|
272
|
+
vis = await smart.is_visible("button", 0) # visivel?
|
|
273
|
+
ena = await smart.is_enabled("input", 1) # habilitado?
|
|
274
|
+
chk = await smart.is_checked("input", 3) # marcado?
|
|
275
|
+
has = await smart.has_class("div", 0, "active") # tem classe?
|
|
276
|
+
cls = await smart.get_classes("div", 0) # listar classes
|
|
277
|
+
box = await smart.get_bounding_box("img", 0) # x, y, width, height
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Pagina
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
title = await smart.emergency_get_page_title() # titulo da pagina
|
|
284
|
+
url = await smart.emergency_get_page_url() # URL atual
|
|
285
|
+
await smart.page_screenshot("captura.png", full_page=True) # screenshot
|
|
286
|
+
text = await smart.page_text() # todo texto visivel
|
|
287
|
+
html = await smart.page_html() # HTML completo
|
|
288
|
+
await smart.page_pdf("relatorio.pdf") # exportar PDF
|
|
289
|
+
await smart.set_viewport(1920, 1080) # mudar tamanho
|
|
290
|
+
await smart.go_back() # voltar
|
|
291
|
+
await smart.go_forward() # avancar
|
|
292
|
+
await smart.reload() # recarregar
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### CSS / Estilo
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
style = await smart.emergency_get_computed_style("div", 0, "background-color")
|
|
299
|
+
attr = await smart.emergency_get_attribute("a", 0, "href")
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Cookies
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
cookies = await smart.get_cookies() # listar cookies
|
|
306
|
+
await smart.set_cookie("token", "abc123", domain=".example.com") # criar cookie
|
|
307
|
+
await smart.clear_cookies() # limpar tudo
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### LocalStorage e SessionStorage
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
val = await smart.get_local_storage("user_id") # ler
|
|
314
|
+
await smart.set_local_storage("theme", "dark") # salvar
|
|
315
|
+
await smart.remove_local_storage("temp") # remover chave
|
|
316
|
+
all_data = await smart.get_all_local_storage() # tudo
|
|
317
|
+
await smart.clear_local_storage() # limpar tudo
|
|
318
|
+
|
|
319
|
+
val = await smart.get_session_storage("cart") # session storage
|
|
320
|
+
await smart.set_session_storage("step", "2")
|
|
321
|
+
await smart.clear_session_storage()
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### JavaScript
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
result = await smart.eval_js("document.title") # executar JS
|
|
328
|
+
result = await smart.eval_js("el => el.dataset.id", element) # JS com argumento
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Iframes
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
frame = await smart.emergency_switch_to_iframe(0) # por indice
|
|
335
|
+
frame = await smart.emergency_switch_to_iframe("#meu-iframe") # por selector
|
|
336
|
+
await smart.emergency_switch_to_main_frame() # voltar ao principal
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Dialogs (alert, confirm, prompt)
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
msg = await smart.emergency_handle_dialog("accept") # aceitar alert
|
|
343
|
+
msg = await smart.emergency_handle_dialog("dismiss") # cancelar
|
|
344
|
+
msg = await smart.emergency_handle_dialog("accept", prompt_text="resposta") # prompt com texto
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Drag and Drop
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
await smart.emergency_drag_and_drop("#item", "#destino")
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Media (audio/video)
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
await smart.emergency_control_media("video", 0, "play") # play/pause/mute
|
|
357
|
+
state = await smart.emergency_get_media_state("video", 0) # currentTime, paused, etc.
|
|
358
|
+
src = await smart.emergency_get_media_src("video", 0) # URL do media
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Imagens
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
info = await smart.emergency_get_image_info(0) # src, alt, naturalWidth...
|
|
365
|
+
images = await smart.emergency_capture_all_images() # todas as imagens
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Captura em massa
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
inputs = await smart.emergency_capture_all_inputs() # todos os inputs
|
|
372
|
+
buttons = await smart.emergency_capture_all_buttons() # todos os botoes
|
|
373
|
+
selects = await smart.emergency_capture_all_selects() # todos os selects
|
|
374
|
+
heads = await smart.emergency_capture_all_headings() # todos os h1-h6
|
|
375
|
+
full = await smart.emergency_capture_page_elements() # mapa completo da pagina
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Capture e Relocate (persistir referencia a elementos)
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
cap = await smart.emergency_capture("button", 0) # captura snapshot
|
|
382
|
+
cap = await smart.emergency_capture_by_selector("#btn-enviar") # por selector
|
|
383
|
+
cap = await smart.emergency_capture_containing("button", "*enviar*") # por glob
|
|
384
|
+
|
|
385
|
+
# Mais tarde, relocar o mesmo elemento (mesmo se DOM mudou)
|
|
386
|
+
el = await smart.emergency_relocate(cap)
|
|
387
|
+
await smart.emergency_click_captured(cap)
|
|
388
|
+
await smart.emergency_fill_captured(cap, "novo valor")
|
|
389
|
+
text = await smart.emergency_read_captured(cap)
|
|
390
|
+
await smart.emergency_hover_captured(cap)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Intent-driven (modo avancado)
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
smart = Smartwright(page=page, request_context=context.request)
|
|
397
|
+
await smart.goto("https://example.com")
|
|
398
|
+
await smart.fill("email_field", "user@test.com")
|
|
399
|
+
await smart.fill("password_field", "senha123")
|
|
400
|
+
await smart.click("login_button")
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Resposta de assistente (chat bots)
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
answer = await smart.wait_response_text(timeout_ms=60000, stable_rounds=3)
|
|
407
|
+
await smart.wait_and_click_copy_button()
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### File system
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
data = await smart.read_file("config.json") # ler arquivo
|
|
414
|
+
await smart.write_file("output.txt", "resultado") # escrever
|
|
415
|
+
await smart.write_file("log.txt", "nova linha\n", append=True) # append
|
|
416
|
+
files = await smart.list_files("downloads", "*.pdf", recursive=True) # listar
|
|
417
|
+
exists = await smart.file_exists("data.csv") # existe?
|
|
418
|
+
info = await smart.file_info("report.pdf") # metadata
|
|
419
|
+
await smart.copy_file("a.txt", "backup/a.txt") # copiar
|
|
420
|
+
await smart.move_file("temp.txt", "final.txt") # mover
|
|
421
|
+
await smart.delete_file("temp.txt") # deletar
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Anti-deteccao (stealth)
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
from smartwright.stealth import StealthConfig, apply_stealth, get_stealth_args
|
|
428
|
+
|
|
429
|
+
# Config com todas protecoes (default)
|
|
430
|
+
cfg = StealthConfig()
|
|
431
|
+
|
|
432
|
+
# Ou config minima (so webdriver + automation flags)
|
|
433
|
+
cfg = StealthConfig.minimal()
|
|
434
|
+
|
|
435
|
+
# Usar no recorder
|
|
436
|
+
recorder = ActionRecorder(save_path="acoes.json", stealth=True)
|
|
437
|
+
|
|
438
|
+
# Usar manualmente
|
|
439
|
+
browser = await p.chromium.launch(
|
|
440
|
+
args=get_stealth_args(cfg),
|
|
441
|
+
ignore_default_args=["--enable-automation"],
|
|
442
|
+
)
|
|
443
|
+
context = await browser.new_context(**get_context_options(cfg))
|
|
444
|
+
await apply_stealth(context, cfg)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Protecoes: navigator.webdriver, plugins, chrome object, permissions, hardware (concurrency/memory/platform), WebGL vendor/renderer, canvas noise, audio noise, connection rtt, WebRTC IP leak. 6 fingerprint profiles (Windows/Mac/Linux com GPUs realistas).
|
|
448
|
+
|
|
449
|
+
### Gravacao de video e HAR
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
# Gravar com video
|
|
453
|
+
recorder = ActionRecorder(
|
|
454
|
+
save_path="acoes.json",
|
|
455
|
+
record_video_dir="videos/",
|
|
456
|
+
record_har_path="network.har",
|
|
457
|
+
)
|
|
458
|
+
page = await recorder.start(url="https://example.com")
|
|
459
|
+
actions = await recorder.wait_until_closed()
|
|
460
|
+
print(recorder.video_paths) # paths dos videos
|
|
461
|
+
|
|
462
|
+
# Ler HAR
|
|
463
|
+
har = await smart.read_har("network.har") # parse HAR
|
|
464
|
+
print(har["total_entries"])
|
|
465
|
+
|
|
466
|
+
# Extrair APIs do HAR
|
|
467
|
+
apis = await smart.extract_har_apis("network.har", "/api/") # so endpoints API
|
|
468
|
+
for api in apis:
|
|
469
|
+
print(api["method"], api["url"], api["status"])
|
|
470
|
+
|
|
471
|
+
# Gerar GIF dos screenshots de debug
|
|
472
|
+
gif = await smart.generate_gif("debug_screenshots/", "replay.gif", duration_ms=600)
|
|
473
|
+
print(gif["frames"], "frames,", gif["size"], "bytes")
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
CLI:
|
|
477
|
+
```bash
|
|
478
|
+
python qwen-cap.py record --stealth # record com anti-deteccao
|
|
479
|
+
python qwen-cap.py record --record-video # grava video da sessao
|
|
480
|
+
python qwen-cap.py record --record-har network.har # grava trace HTTP
|
|
481
|
+
python qwen-cap.py replay --stealth --record-video # replay stealth + video
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
## Resolucao de elementos (6 steps)
|
|
485
|
+
|
|
486
|
+
O replay busca cada elemento nesta ordem:
|
|
487
|
+
|
|
488
|
+
1. **Capture CSS selectors** — selectors gravados (#id, .class, [data-testid])
|
|
489
|
+
2. **Action selector** — selector do JSON com proximidade de bbox
|
|
490
|
+
3. **Type + index + text** — N-esimo elemento com texto correspondente
|
|
491
|
+
4. **Type + text** — primeiro match por texto (resiliente a mudancas de DOM)
|
|
492
|
+
5. **Type + index** — lookup ordinal por tag
|
|
493
|
+
6. **Coordenadas** — click por posicao x/y (ultimo recurso)
|
|
494
|
+
|
|
495
|
+
## Arquitetura
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
smartwright/
|
|
499
|
+
__init__.py Facade (Smartwright class, 290+ metodos)
|
|
500
|
+
recorder/
|
|
501
|
+
__init__.py ActionRecorder — gravacao ao vivo com JS injection
|
|
502
|
+
resolver/
|
|
503
|
+
emergency.py EmergencyResolver — replay, resolucao, click/fill/etc.
|
|
504
|
+
replay_mode.py ReplayMode enum + ModeConfig — configuracao dos modos
|
|
505
|
+
adaptive.py Resolver adaptativo com score de estrategia
|
|
506
|
+
stealth/
|
|
507
|
+
__init__.py StealthConfig, apply_stealth — anti-deteccao
|
|
508
|
+
core/ Decision engine, modelos, knowledge store
|
|
509
|
+
intent/ Mapeamento de intencoes semanticas
|
|
510
|
+
semantic_finder/ Busca por conceito
|
|
511
|
+
healing/ Auto-repair adaptativo
|
|
512
|
+
fingerprint/ Deteccao de mudancas estruturais
|
|
513
|
+
network_learning/ Descoberta automatica de APIs
|
|
514
|
+
api_executor/ Execucao direta via HTTP
|
|
515
|
+
ai_recovery/ Fallback com IA
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Persistencia
|
|
519
|
+
|
|
520
|
+
- `.smartwright_profile/` — perfil do Chromium (login, cookies, sessao)
|
|
521
|
+
- `.smartwright_knowledge.json` — historico de estrategias, APIs, scores
|
|
522
|
+
- `recording.json` — acoes gravadas
|
|
523
|
+
- `debug_screenshots/` — screenshots de debug por step
|