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 290+ funcoes para qualquer tarefa.

Instalacao

pip install smartwright
playwright install chromium

CLI

smartwright version                                   # versao instalada
smartwright run flow.json                             # executa JSON manual
smartwright run flow.json --mode mix --no-headless    # modo mix com browser visivel
smartwright run flow.json --debug --screenshot-dir ss # debug visual + screenshots
smartwright run flow.json --base-url https://app.com  # URLs relativas
smartwright run flow.json --stop-on-error             # para no primeiro erro
smartwright record                                    # grava acoes em recording.json
smartwright record --output meu_fluxo.json --url https://example.com
smartwright replay recording.json                     # replay com modo padrao
smartwright replay recording.json --mode mix          # replay resiliente
smartwright replay recording.json --no-headless       # browser visivel

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

Multi-tab / Multi-page

# Abrir nova tab
new_page = await smart.new_tab("https://example.com/other")

# Listar tabs abertas
tabs = await smart.list_tabs()  # [{index, url, title}, ...]
print(f"{smart.tab_count} tabs abertas")

# Mudar para tab
await smart.switch_tab(1)

# Fechar tab (default = atual)
await smart.close_tab(1)
await smart.close_tab()  # fecha a tab atual

# Esperar popup
popup = await smart.wait_for_popup(lambda: smart.emergency_click("a", 0))

Session Persistence

# Salvar sessao completa (cookies + localStorage + sessionStorage)
path = await smart.save_session("sessao.json")

# Restaurar sessao
data = await smart.load_session("sessao.json")

# Limpar sessao
await smart.clear_session()

Captcha Solving

from smartwright.captcha.twocaptcha import TwoCaptchaSolver

solver = TwoCaptchaSolver(api_key="YOUR_KEY")
result = await smart.solve_captcha(solver)
# Auto-detecta tipo, resolve e injeta token

Logging

from smartwright import setup_logging
import logging

# Ativar logging (por default nao emite nada)
setup_logging(level=logging.DEBUG)

# Agora todas as operacoes logam:
# DEBUG smartwright: Retry attempt 1/3 after TimeoutError, delay=1.0s
# INFO smartwright: Session saved to /path/sessao.json
# WARNING smartwright: Proxy marked unhealthy: http://proxy:8080 (failures=3)

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:

smartwright record --url https://example.com             # record com URL inicial
smartwright replay recording.json --mode mix             # replay resiliente
smartwright run flow.json --debug                        # executa com debug visual

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)
  cli.py                   CLI entry point (run, record, replay, version)
  _logging.py              Logger + setup_logging()
  retry.py                 Retry engine com backoff strategies
  proxy.py                 Proxy rotation com health tracking
  constants.py             Timeouts, versao, configuracao central
  exceptions.py            Hierarquia de excepcoes
  recorder/
    __init__.py            ActionRecorder — gravacao ao vivo com JS injection
  resolver/
    emergency.py           EmergencyResolver — 12 mixins compostos
    _tabs.py               TabsMixin — multi-tab / multi-page
    _session.py            SessionMixin — persistencia de sessao
    _replay.py             ReplayMixin — replay com 6-step resolution
    _run_json.py           RunJsonMixin — execucao de JSON manual
    _debug.py              DebugMixin — cursor, highlight, screenshots
    replay_mode.py         ReplayMode enum + ModeConfig
    dom_serializer.py      Serializa DOM para LLMs
    dom_diff.py            Comparacao de snapshots
  captcha/
    solver.py              Deteccao + extracao + injecao de captcha
    twocaptcha.py          2Captcha.com solver
  stealth/
    __init__.py            StealthConfig, apply_stealth — anti-deteccao
  core/                    Decision engine, modelos, knowledge store
  network_learning/        Descoberta automatica de APIs

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.4.0.tar.gz (121.1 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.4.0-py3-none-any.whl (127.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for smartwright-0.4.0.tar.gz
Algorithm Hash digest
SHA256 8d0e67f44e7b96c4580aead2820d52d6a7324b80c9b57629b5f3aa535892116b
MD5 65f5a4503d47c7aa9e2da7ad8734fa5d
BLAKE2b-256 3226a5e8c94660af878f62e325efb2cbbd1c98afcabc9ccba88e76406ca32b55

See more details on using hashes here.

File details

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

File metadata

  • Download URL: smartwright-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 127.2 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.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a774b3dbdd84b119f1a34c40c4e068c1aee0a2f089a0289ee5ae6a4d1bafe213
MD5 cac71f1d36f8b36a5bb748889cc9bad8
BLAKE2b-256 13659227fd5553863a26d57b62a80db718fb711554a4ade47d5e0867e0d34c64

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