mirror of
https://github.com/browser-use/browser-use.git
synced 2026-09-14 19:59:47 +08:00
6c53f20d11
- Add new `extract_data` action that uses CSS selectors (querySelectorAll) to extract structured data directly from the page without LLM cost. Supports extracting text, href, src, or any HTML attribute from matching elements. Returns structured JSON and optionally saves to CSV/JSONL/JSON files. - Add `save_to_file` parameter to existing `extract` action so LLM-extracted content can be written directly to a file instead of consuming context window tokens. Enables accumulating data across multiple pages. - Update both system prompts with a new `<data_extraction>` section that guides the agent on when to use extract_data vs extract vs evaluate, and documents the multi-page extraction pattern (extract → click Next → extract → repeat with save_to_file appending to same file). These changes address the core data extraction inefficiency where the agent was forced to output all extracted tokens through the LLM, making bulk extraction (100+ items, multi-page scraping) slow and expensive. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
128 lines
4.3 KiB
Python
128 lines
4.3 KiB
Python
from typing import Generic, TypeVar
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
# Action Input Models
|
|
class ExtractAction(BaseModel):
|
|
query: str
|
|
extract_links: bool = Field(
|
|
default=False, description='Set True to true if the query requires links, else false to safe tokens'
|
|
)
|
|
start_from_char: int = Field(
|
|
default=0, description='Use this for long markdowns to start from a specific character (not index in browser_state)'
|
|
)
|
|
save_to_file: str | None = Field(
|
|
default=None,
|
|
description='If set, append extracted content to this file (e.g. "results.csv") instead of returning all tokens. Use for multi-page extraction to accumulate data across pages.',
|
|
)
|
|
|
|
|
|
class ExtractDataAction(BaseModel):
|
|
selector: str = Field(description='CSS selector to match elements, e.g. "table tr", ".product-card", "ul.results > li"')
|
|
attributes: list[str] = Field(
|
|
default=['text'],
|
|
description='List of attributes to extract per element. Use "text" for innerText, "href" for links, or any HTML attribute name like "src", "data-id", etc.',
|
|
)
|
|
save_to_file: str | None = Field(
|
|
default=None,
|
|
description='If set, append extracted rows to this file as JSON lines (e.g. "data.jsonl") or CSV (e.g. "data.csv"). Keeps data out of context window.',
|
|
)
|
|
max_results: int = Field(
|
|
default=10000,
|
|
description='Maximum number of elements to extract. Prevents accidental extraction of huge DOMs.',
|
|
)
|
|
|
|
|
|
class SearchAction(BaseModel):
|
|
query: str
|
|
engine: str = Field(
|
|
default='duckduckgo', description='duckduckgo, google, bing (use duckduckgo by default because less captchas)'
|
|
)
|
|
|
|
|
|
# Backward compatibility alias
|
|
SearchAction = SearchAction
|
|
|
|
|
|
class NavigateAction(BaseModel):
|
|
url: str
|
|
new_tab: bool = Field(default=False)
|
|
|
|
|
|
# Backward compatibility alias
|
|
GoToUrlAction = NavigateAction
|
|
|
|
|
|
class ClickElementAction(BaseModel):
|
|
index: int | None = Field(default=None, ge=1, description='Element index from browser_state')
|
|
coordinate_x: int | None = Field(default=None, description='Horizontal coordinate relative to viewport left edge')
|
|
coordinate_y: int | None = Field(default=None, description='Vertical coordinate relative to viewport top edge')
|
|
# expect_download: bool = Field(default=False, description='set True if expecting a download, False otherwise') # moved to downloads_watchdog.py
|
|
# click_count: int = 1 # TODO
|
|
|
|
|
|
class ClickElementActionIndexOnly(BaseModel):
|
|
model_config = ConfigDict(title='ClickElementAction')
|
|
|
|
index: int = Field(ge=1, description='Element index from browser_state')
|
|
|
|
|
|
class InputTextAction(BaseModel):
|
|
index: int = Field(ge=0, description='from browser_state')
|
|
text: str
|
|
clear: bool = Field(default=True, description='1=clear, 0=append')
|
|
|
|
|
|
class DoneAction(BaseModel):
|
|
text: str = Field(description='Final user message in the format the user requested')
|
|
success: bool = Field(default=True, description='True if user_request completed successfully')
|
|
files_to_display: list[str] | None = Field(default=[])
|
|
|
|
|
|
T = TypeVar('T', bound=BaseModel)
|
|
|
|
|
|
class StructuredOutputAction(BaseModel, Generic[T]):
|
|
success: bool = Field(default=True, description='True if user_request completed successfully')
|
|
data: T = Field(description='The actual output data matching the requested schema')
|
|
|
|
|
|
class SwitchTabAction(BaseModel):
|
|
tab_id: str = Field(min_length=4, max_length=4, description='4-char id')
|
|
|
|
|
|
class CloseTabAction(BaseModel):
|
|
tab_id: str = Field(min_length=4, max_length=4, description='4-char id')
|
|
|
|
|
|
class ScrollAction(BaseModel):
|
|
down: bool = Field(default=True, description='down=True=scroll down, down=False scroll up')
|
|
pages: float = Field(default=1.0, description='0.5=half page, 1=full page, 10=to bottom/top')
|
|
index: int | None = Field(default=None, description='Optional element index to scroll within specific element')
|
|
|
|
|
|
class SendKeysAction(BaseModel):
|
|
keys: str = Field(description='keys (Escape, Enter, PageDown) or shortcuts (Control+o)')
|
|
|
|
|
|
class UploadFileAction(BaseModel):
|
|
index: int
|
|
path: str
|
|
|
|
|
|
class NoParamsAction(BaseModel):
|
|
model_config = ConfigDict(extra='ignore')
|
|
|
|
# Optional field required by Gemini API which errors on empty objects in response_schema
|
|
description: str | None = Field(None, description='Optional description for the action')
|
|
|
|
|
|
class GetDropdownOptionsAction(BaseModel):
|
|
index: int
|
|
|
|
|
|
class SelectDropdownOptionAction(BaseModel):
|
|
index: int
|
|
text: str = Field(description='exact text/value')
|