Skip to main content

Adaptive web automation engine — intent-driven, type+index, replay, DOM serializer, network learning

Project description

Smartwright

Motor de automacao web adaptativo com gravacao ao vivo, replay inteligente e 270+ funcoes para qualquer tarefa.

Instalacao

pip install playwright
playwright install chromium

CLI (qwen-cap.py)

python qwen-cap.py record                          # grava acoes em recording.json
python qwen-cap.py record meu_fluxo.json           # grava em arquivo customizado
python qwen-cap.py replay                           # replay com debug (padrao)
python qwen-cap.py replay --no-debug                # sem screenshots/highlight
python qwen-cap.py replay --mode rapido             # modo rapido
python qwen-cap.py replay --mode mix                # modo mais resiliente
python qwen-cap.py full                             # record + replay sequencial
python qwen-cap.py full --mode forcado              # full com modo forcado

Modos de execucao

Modo Descricao Uso ideal
rapido Sem verificacao, timeouts curtos, sem delay Paginas estaveis, testes rapidos
padrao Todos os 6 steps de resolucao (default) Uso geral
por_index So usa tag + indice ordinal DOM fixo
por_id_e_class So CSS com #id, .class, [data-testid] Sites com bons IDs
forcado Resolucao completa + force click Overlays, modais
mix Todos os steps + 3 retries + scroll Maximo resiliencia

Uso como biblioteca — exemplos rapidos

from smartwright import Smartwright
from smartwright.recorder import ActionRecorder

Gravacao e Replay

# Gravar acoes do usuario
recorder = ActionRecorder(save_path="acoes.json")
page = await recorder.start(url="https://example.com")
actions = await recorder.wait_until_closed()

# Replay com modo
smart = Smartwright(page=page, request_context=context.request)
results = await smart.emergency_replay_actions(actions, mode="mix")

# Replay direto do JSON
results = await smart.emergency_replay_json("acoes.json", mode="padrao")

Executar JSON manual (run_json)

Executa JSONs escritos a mao, tolerante com campos em falta. So precisa de action + os campos essenciais.

# ── Inline (lista de dicts) ──
results = await smart.run_json([
    {"action": "goto", "url": "https://example.com"},
    {"action": "fill", "selector": "#email", "value": "user@test.com"},
    {"action": "fill", "text": "Password", "value": "123456"},
    {"action": "click", "text": "Login"},
    {"action": "wait", "ms": 2000},
    {"action": "screenshot", "path": "resultado.png"},
])

# ── De arquivo JSON ──
results = await smart.run_json_file("meu_fluxo.json", mode="mix")

# ── Com base_url (URLs relativas) ──
results = await smart.run_json([
    {"action": "goto", "url": "/login"},
    {"action": "fill", "selector": "input[name=user]", "value": "admin"},
], base_url="https://meusite.com")

# ── Parar no primeiro erro ──
results = await smart.run_json(actions, continue_on_error=False)

JSON minimo por acao:

[
  {"action": "goto", "url": "https://example.com"},
  {"action": "click", "selector": "#btn"},
  {"action": "click", "text": "Enviar"},
  {"action": "fill", "selector": "#email", "value": "a@b.com"},
  {"action": "fill", "text": "Senha", "value": "123"},
  {"action": "select", "selector": "#pais", "value": "BR"},
  {"action": "select", "selector": "#pais", "option": "Brasil"},
  {"action": "check", "selector": "#aceito", "checked": true},
  {"action": "press", "key": "Enter"},
  {"action": "scroll", "dir": "down", "px": 500},
  {"action": "wait", "ms": 3000},
  {"action": "wait_text", "text": "Sucesso"},
  {"action": "wait_element", "selector": "div.resultado"},
  {"action": "screenshot", "path": "captura.png"},
  {"action": "back"},
  {"action": "js", "code": "document.title"},
  {"action": "drag", "from": "#item", "to": "#destino"},
  {"action": "upload", "selector": "input[type=file]", "file": "/path/doc.pdf"},
  {"action": "download", "dir": "downloads"},
  {"action": "copy", "value": "texto copiado"}
]

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.

Campos flexiveis: selector/css/sel, element_type/type/tag, index/idx/i, text/label/contains, value/val, url/href/link, timeout_ms/timeout.

Clicks

await smart.emergency_click("button", 0)                                        # por tipo + indice
await smart.emergency_click_by_text("Enviar")                                   # por texto visivel
await smart.emergency_click_by_role("button", 2)                                # por role ARIA
await smart.emergency_click_first_type_containing("button", "*salvar*")          # glob pattern
await smart.emergency_click_by_type_at_index_containing("a", 0, "*download*")   # tipo + pos + texto
await smart.emergency_click_link("Ver mais")                                     # link por texto
await smart.double_click("div", 3)                                               # duplo click
await smart.right_click("tr", 0)                                                 # click direito
await smart.click_at_coordinates(500, 300)                                       # x, y absoluto

Fill / Input

await smart.emergency_fill("input", 0, "user@test.com")                          # por tipo + indice
await smart.emergency_fill_first_type_containing("input", "*email*", "a@b.com")  # glob pattern
await smart.emergency_fill_by_type_at_index_containing("input", 2, "*senha*", "123")
await smart.emergency_fill_by_label("Email", "user@test.com")                    # por label do campo
await smart.emergency_clear_input("input", 0)                                    # limpar campo
val = await smart.emergency_read_input_value("input", 0)                         # ler valor atual

Read / Extrair texto

text = await smart.emergency_read("span", 5)                                     # texto por tipo + indice
text = await smart.emergency_read_by_text("Total:", 0)                           # por texto visivel
text = await smart.emergency_read_first_type_containing("div", "*preco*")        # glob pattern
text = await smart.emergency_read_by_type_at_index_containing("p", 0, "*desc*")  # tipo + pos + texto
texts = await smart.emergency_read_all("li")                                     # todos os textos de um tipo

Formularios

await smart.emergency_select_option("select", 0, "opcao_valor")                 # select por value
await smart.emergency_select_option_by_label("select", 0, "Opcao visivel")      # select por label
sel = await smart.emergency_read_selected_option("select", 0)                    # ler selecionado
opts = await smart.emergency_read_all_options("select", 0)                       # listar opcoes

await smart.emergency_check("input", 3, checked=True)                           # marcar checkbox
await smart.emergency_toggle_checkbox("input", 3)                                # alternar checkbox
await smart.emergency_select_radio("genero", "masculino")                        # radio por name+value
await smart.emergency_upload_file("input", 0, "/path/file.pdf")                  # upload arquivo

state = await smart.emergency_read_form_state(0)                                 # estado de todos os campos
await smart.emergency_submit_form(0)                                             # submit form
await smart.emergency_reset_form(0)                                              # reset form

Tabelas

cell = await smart.emergency_read_table_cell(0, 2, 3)                           # tabela[0] linha 2 col 3
row  = await smart.emergency_read_table_row(0, 1)                                # linha inteira
data = await smart.emergency_read_full_table(0)                                  # tabela completa -> [[str]]
await smart.emergency_click_table_cell(0, 1, 2)                                  # clicar na celula

Listas

items = await smart.emergency_read_list_items(0, list_type="ul")                 # textos de uma <ul>
await smart.emergency_click_list_item(0, 2, list_type="ul")                      # clicar no 3o item

Links

await smart.emergency_click_link("Saiba mais", index=0)                          # clicar link por texto
href = await smart.emergency_get_link_href("Download")                           # pegar href
links = await smart.emergency_capture_all_links()                                # todos os links da pagina

Hover / Scroll / Teclas

await smart.emergency_hover("div", 0)                                            # hover no elemento
await smart.emergency_scroll_page("down", 500)                                   # scroll direcional
await smart.emergency_scroll_to_top()                                            # topo da pagina
await smart.emergency_scroll_to_bottom()                                         # final da pagina
await smart.emergency_scroll_to("div.footer")                                    # scroll ate elemento
await smart.mouse_move(400, 200)                                                 # mover mouse
await smart.mouse_wheel(0, -500)                                                 # scroll por mouse wheel
await smart.emergency_press_keys("Enter")                                        # teclas especiais
await smart.emergency_press_keys("Control+a")                                    # atalhos

Waits / Esperas

el = await smart.emergency_wait_for_element("div.resultado", timeout_ms=15000)   # esperar elemento
await smart.emergency_wait_for_text("Sucesso!", timeout_ms=10000)                # esperar texto visivel
url = await smart.emergency_wait_for_url_contains("/dashboard")                  # esperar URL mudar
await smart.wait_for_load("networkidle", timeout_ms=30000)                       # esperar pagina carregar
resp = await smart.wait_for_response("*/api/data*", timeout_ms=30000)            # esperar resposta HTTP

Download e Clipboard

dl = await smart.emergency_wait_download(save_dir="downloads", timeout_ms=30000)
print(dl["filename"], dl["path"], dl["size"])

clip = await smart.emergency_wait_clipboard(timeout_ms=10000)
print(clip["text"])

await smart.emergency_copy_to_clipboard("texto copiado")

Estado dos elementos

exists = await smart.element_exists("div.modal")                                 # existe no DOM?
count  = await smart.element_count("li.item")                                    # quantos existem?
vis    = await smart.is_visible("button", 0)                                     # visivel?
ena    = await smart.is_enabled("input", 1)                                      # habilitado?
chk    = await smart.is_checked("input", 3)                                      # marcado?
has    = await smart.has_class("div", 0, "active")                               # tem classe?
cls    = await smart.get_classes("div", 0)                                       # listar classes
box    = await smart.get_bounding_box("img", 0)                                  # x, y, width, height

Pagina

title = await smart.emergency_get_page_title()                                   # titulo da pagina
url   = await smart.emergency_get_page_url()                                     # URL atual
await smart.page_screenshot("captura.png", full_page=True)                       # screenshot
text  = await smart.page_text()                                                  # todo texto visivel
html  = await smart.page_html()                                                  # HTML completo
await smart.page_pdf("relatorio.pdf")                                            # exportar PDF
await smart.set_viewport(1920, 1080)                                             # mudar tamanho
await smart.go_back()                                                            # voltar
await smart.go_forward()                                                         # avancar
await smart.reload()                                                             # recarregar

CSS / Estilo

style = await smart.emergency_get_computed_style("div", 0, "background-color")
attr  = await smart.emergency_get_attribute("a", 0, "href")

Cookies

cookies = await smart.get_cookies()                                              # listar cookies
await smart.set_cookie("token", "abc123", domain=".example.com")                 # criar cookie
await smart.clear_cookies()                                                      # limpar tudo

LocalStorage e SessionStorage

val = await smart.get_local_storage("user_id")                                   # ler
await smart.set_local_storage("theme", "dark")                                   # salvar
await smart.remove_local_storage("temp")                                         # remover chave
all_data = await smart.get_all_local_storage()                                   # tudo
await smart.clear_local_storage()                                                # limpar tudo

val = await smart.get_session_storage("cart")                                    # session storage
await smart.set_session_storage("step", "2")
await smart.clear_session_storage()

JavaScript

result = await smart.eval_js("document.title")                                   # executar JS
result = await smart.eval_js("el => el.dataset.id", element)                     # JS com argumento

Iframes

frame = await smart.emergency_switch_to_iframe(0)                                # por indice
frame = await smart.emergency_switch_to_iframe("#meu-iframe")                    # por selector
await smart.emergency_switch_to_main_frame()                                     # voltar ao principal

Dialogs (alert, confirm, prompt)

msg = await smart.emergency_handle_dialog("accept")                              # aceitar alert
msg = await smart.emergency_handle_dialog("dismiss")                             # cancelar
msg = await smart.emergency_handle_dialog("accept", prompt_text="resposta")      # prompt com texto

Drag and Drop

await smart.emergency_drag_and_drop("#item", "#destino")

Media (audio/video)

await smart.emergency_control_media("video", 0, "play")                          # play/pause/mute
state = await smart.emergency_get_media_state("video", 0)                        # currentTime, paused, etc.
src   = await smart.emergency_get_media_src("video", 0)                          # URL do media

Imagens

info   = await smart.emergency_get_image_info(0)                                 # src, alt, naturalWidth...
images = await smart.emergency_capture_all_images()                              # todas as imagens

Captura em massa

inputs  = await smart.emergency_capture_all_inputs()                             # todos os inputs
buttons = await smart.emergency_capture_all_buttons()                            # todos os botoes
selects = await smart.emergency_capture_all_selects()                            # todos os selects
heads   = await smart.emergency_capture_all_headings()                           # todos os h1-h6
full    = await smart.emergency_capture_page_elements()                          # mapa completo da pagina

Capture e Relocate (persistir referencia a elementos)

cap = await smart.emergency_capture("button", 0)                                # captura snapshot
cap = await smart.emergency_capture_by_selector("#btn-enviar")                   # por selector
cap = await smart.emergency_capture_containing("button", "*enviar*")             # por glob

# Mais tarde, relocar o mesmo elemento (mesmo se DOM mudou)
el = await smart.emergency_relocate(cap)
await smart.emergency_click_captured(cap)
await smart.emergency_fill_captured(cap, "novo valor")
text = await smart.emergency_read_captured(cap)
await smart.emergency_hover_captured(cap)

Intent-driven (modo avancado)

smart = Smartwright(page=page, request_context=context.request)
await smart.goto("https://example.com")
await smart.fill("email_field", "user@test.com")
await smart.fill("password_field", "senha123")
await smart.click("login_button")

Resposta de assistente (chat bots)

answer = await smart.wait_response_text(timeout_ms=60000, stable_rounds=3)
await smart.wait_and_click_copy_button()

File system

data = await smart.read_file("config.json")                                     # ler arquivo
await smart.write_file("output.txt", "resultado")                               # escrever
await smart.write_file("log.txt", "nova linha\n", append=True)                  # append
files = await smart.list_files("downloads", "*.pdf", recursive=True)            # listar
exists = await smart.file_exists("data.csv")                                    # existe?
info = await smart.file_info("report.pdf")                                      # metadata
await smart.copy_file("a.txt", "backup/a.txt")                                  # copiar
await smart.move_file("temp.txt", "final.txt")                                  # mover
await smart.delete_file("temp.txt")                                             # deletar

Anti-deteccao (stealth)

from smartwright.stealth import StealthConfig, apply_stealth, get_stealth_args

# Config com todas protecoes (default)
cfg = StealthConfig()

# Ou config minima (so webdriver + automation flags)
cfg = StealthConfig.minimal()

# Usar no recorder
recorder = ActionRecorder(save_path="acoes.json", stealth=True)

# Usar manualmente
browser = await p.chromium.launch(
    args=get_stealth_args(cfg),
    ignore_default_args=["--enable-automation"],
)
context = await browser.new_context(**get_context_options(cfg))
await apply_stealth(context, cfg)

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).

Gravacao de video e HAR

# Gravar com video
recorder = ActionRecorder(
    save_path="acoes.json",
    record_video_dir="videos/",
    record_har_path="network.har",
)
page = await recorder.start(url="https://example.com")
actions = await recorder.wait_until_closed()
print(recorder.video_paths)                                                      # paths dos videos

# Ler HAR
har = await smart.read_har("network.har")                                        # parse HAR
print(har["total_entries"])

# Extrair APIs do HAR
apis = await smart.extract_har_apis("network.har", "/api/")                      # so endpoints API
for api in apis:
    print(api["method"], api["url"], api["status"])

# Gerar GIF dos screenshots de debug
gif = await smart.generate_gif("debug_screenshots/", "replay.gif", duration_ms=600)
print(gif["frames"], "frames,", gif["size"], "bytes")

CLI:

python qwen-cap.py record --stealth                    # record com anti-deteccao
python qwen-cap.py record --record-video               # grava video da sessao
python qwen-cap.py record --record-har network.har     # grava trace HTTP
python qwen-cap.py replay --stealth --record-video     # replay stealth + video

Resolucao de elementos (6 steps)

O replay busca cada elemento nesta ordem:

  1. Capture CSS selectors — selectors gravados (#id, .class, [data-testid])
  2. Action selector — selector do JSON com proximidade de bbox
  3. Type + index + text — N-esimo elemento com texto correspondente
  4. Type + text — primeiro match por texto (resiliente a mudancas de DOM)
  5. Type + index — lookup ordinal por tag
  6. Coordenadas — click por posicao x/y (ultimo recurso)

Arquitetura

smartwright/
  __init__.py              Facade (Smartwright class, 290+ metodos)
  recorder/
    __init__.py            ActionRecorder — gravacao ao vivo com JS injection
  resolver/
    emergency.py           EmergencyResolver — replay, resolucao, click/fill/etc.
    replay_mode.py         ReplayMode enum + ModeConfig — configuracao dos modos
    adaptive.py            Resolver adaptativo com score de estrategia
  stealth/
    __init__.py            StealthConfig, apply_stealth — anti-deteccao
  core/                    Decision engine, modelos, knowledge store
  intent/                  Mapeamento de intencoes semanticas
  semantic_finder/         Busca por conceito
  healing/                 Auto-repair adaptativo
  fingerprint/             Deteccao de mudancas estruturais
  network_learning/        Descoberta automatica de APIs
  api_executor/            Execucao direta via HTTP
  ai_recovery/             Fallback com IA

Persistencia

  • .smartwright_profile/ — perfil do Chromium (login, cookies, sessao)
  • .smartwright_knowledge.json — historico de estrategias, APIs, scores
  • recording.json — acoes gravadas
  • debug_screenshots/ — screenshots de debug por step

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

smartwright-0.2.0.tar.gz (105.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

smartwright-0.2.0-py3-none-any.whl (109.0 kB view details)

Uploaded Python 3

File details

Details for the file smartwright-0.2.0.tar.gz.

File metadata

  • Download URL: smartwright-0.2.0.tar.gz
  • Upload date:
  • Size: 105.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for smartwright-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f9ec1569ac3ae48862e798ad4b59e2ad4441eea22e7fafe8bd20d19d031ef61e
MD5 8fb110b01e2e51959a0cb3ce9f85839b
BLAKE2b-256 27bb54697625dd34cd7553c7fca61b88a6a15088ed56e83b7d3d36fafb7c3a35

See more details on using hashes here.

File details

Details for the file smartwright-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: smartwright-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 109.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for smartwright-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a3ceb9a66c6e4f2d8a1cd5ad32e21794edb9e02e475917cc326f66450e13a353
MD5 78fe4993bb5136acc8c3b624fc36a19f
BLAKE2b-256 042ffd0ab772f802eab79984df9e5bfcfcc474d15f8bfec23613d37b8b0f48d9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page