page-analysis
Analyze web page content, structure, and layout to understand what a page contains and how it is organized. Trigger when the user asks to: analyze a page, understand page structure, inspect a website, summarize page content, examine page layout, review a web page, or describe wha
Install
npx skills add https://github.com/billy-enrizky/openbrowser-ai/tree/main/plugin/skills/page-analysis
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install billy-enrizky-openbrowser-ai@llmmart
git clone https://github.com/billy-enrizky/openbrowser-ai.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole billy-enrizky/openbrowser-ai collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Page Analysis
Analyze and understand web page content, structure, and interactive elements using Python code execution. Produces a comprehensive breakdown of what is on the page and how it is organized.
All code runs via openbrowser-ai -c. The daemon starts automatically and persists variables across calls. All browser functions are async -- use await.
The CLI daemon also persists cookies and login state in ~/.config/openbrowser/profiles/daemon/storage_state.json, so authenticated sessions can be reused across later runs.
Setup
Before running, verify openbrowser-ai is installed:
openbrowser-ai --help
If not found, install:
# macOS/Linux
curl -fsSL https://raw.githubusercontent.com/billy-enrizky/openbrowser-ai/main/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/billy-enrizky/openbrowser-ai/main/install.ps1 | iex
Workflow
Step 1 -- Navigate and get overview
openbrowser-ai -c - <<'EOF'
await navigate("https://example.com")
state = await browser.get_browser_state_summary()
print(f"Title: {state.title}")
print(f"URL: {state.url}")
print(f"Interactive elements: {len(state.dom_state.selector_map)}")
print(f"Tabs: {len(state.tabs)}")
EOF
Step 2 -- Extract page metadata
openbrowser-ai -c - <<'EOF'
meta = await evaluate("""
(function(){
return {
title: document.title,
description: document.querySelector("meta[name='description']")?.content,
canonical: document.querySelector("link[rel='canonical']")?.href,
ogTitle: document.querySelector("meta[property='og:title']")?.content,
ogImage: document.querySelector("meta[property='og:image']")?.content,
lang: document.documentElement.lang,
charset: document.characterSet
};
})()
""")
import json
print(json.dumps(meta, indent=2))
EOF
Step 3 -- Detect frameworks and technologies
openbrowser-ai -c - <<'EOF'
tech = await evaluate("""
(function(){
const t = [];
if (window.__NEXT_DATA__) t.push("Next.js");
if (window.__NUXT__) t.push("Nuxt.js");
if (document.querySelector("[data-reactroot]") || document.querySelector("#__next")) t.push("React");
if (document.querySelector("[ng-version]")) t.push("Angular");
if (window.jQuery) t.push("jQuery");
if (window.Vue) t.push("Vue.js");
if (document.querySelector("[data-svelte]")) t.push("Svelte");
return t;
})()
""")
print(f"Technologies detected: {tech}")
EOF
Step 4 -- Content summary and statistics
openbrowser-ai -c - <<'EOF'
stats = await evaluate("""
(function(){
return {
headings: document.querySelectorAll("h1,h2,h3,h4,h5,h6").length,
paragraphs: document.querySelectorAll("p").length,
images: document.querySelectorAll("img").length,
links: document.querySelectorAll("a").length,
forms: document.querySelectorAll("form").length,
tables: document.querySelectorAll("table").length,
lists: document.querySelectorAll("ul,ol").length,
buttons: document.querySelectorAll("button,[role='button']").length,
inputs: document.querySelectorAll("input,textarea,select").length,
iframes: document.querySelectorAll("iframe").length,
scripts: document.querySelectorAll("script").length,
stylesheets: document.querySelectorAll("link[rel='stylesheet']").length
};
})()
""")
import json
print("Content statistics:")
print(json.dumps(stats, indent=2))
EOF
Step 5 -- Analyze heading structure
openbrowser-ai -c - <<'EOF'
headings = await evaluate("""
(function(){
return Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6")).map(h => ({
tag: h.tagName,
text: h.textContent.trim().substring(0, 80)
}));
})()
""")
for h in headings:
htag = h["tag"]
htext = h["text"]
indent = " " * (int(htag[1]) - 1)
print(f"{indent}{htag}: {htext}")
EOF
Step 6 -- Analyze interactive elements
openbrowser-ai -c - <<'EOF'
state = await browser.get_browser_state_summary()
elements_by_tag = {}
for idx, el in state.dom_state.selector_map.items():
tag = el.tag_name
elements_by_tag.setdefault(tag, []).append({
"index": idx,
"text": el.get_all_children_text(max_depth=1)[:50],
"type": el.attributes.get("type", ""),
"href": el.attributes.get("href", "")[:50] if el.attributes.get("href") else "",
})
for tag, elems in sorted(elements_by_tag.items()):
print(f"\n{tag} ({len(elems)} elements):")
for e in elems[:5]:
eidx = e["index"]
etxt = e["text"]
etype = e["type"]
ehref = e["href"]
print(f" [{eidx}] text=\"{etxt}\" type={etype} href={ehref}")
if len(elems) > 5:
print(f" ... and {len(elems) - 5} more")
EOF
Step 7 -- Page dimensions and scroll analysis
openbrowser-ai -c - <<'EOF'
dims = await evaluate("""
(function(){
return {
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight,
scrollHeight: document.body.scrollHeight,
scrollWidth: document.body.scrollWidth,
scrollable: document.body.scrollHeight > window.innerHeight
};
})()
""")
import json
print(json.dumps(dims, indent=2))
if dims["scrollable"]:
pages = dims["scrollHeight"] / dims["viewportHeight"]
print(f"Page is approximately {pages:.1f} viewport heights long")
EOF
Step 8 -- Search for specific content patterns
openbrowser-ai -c - <<'EOF'
import re
# Get page text for Python-side analysis
text_content = await evaluate("document.body.innerText")
# Find emails
emails = re.findall(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", text_content)
print(f"Emails found: {emails}")
# Find phone numbers
phones = re.findall(r"\+?\d[\d\s()-]{7,}", text_content)
print(f"Phone numbers found: {phones}")
# Find dates
dates = re.findall(r"\d{4}-\d{2}-\d{2}|\w+ \d{1,2},? \d{4}", text_content)
print(f"Dates found: {dates}")
EOF
Tips
- Code is piped via stdin using heredoc (
-c - <<'EOF'), so all Python syntax works without shell escaping issues. - Start with
evaluate()for metadata and DOM statistics -- gives a fast structured overview. - Use
browser.get_browser_state_summary()for interactive element analysis. - Use Python regex on extracted text for pattern matching (emails, phones, dates, prices).
- For long pages, use
await scroll(down=True)and re-extract to analyze below-fold content. - Variables persist between
-ccalls while the daemon is running, so you can build a comprehensive analysis incrementally.
Cleanup
This step is mandatory. Run it after the analysis finishes, whether extraction succeeded or the page failed to load. Without it, the daemon keeps Chrome running until its 10-minute idle timeout, leaving a stale browser process, a locked profile, and (on macOS/Linux desktop) a visible window.
Stop the daemon, then verify it is gone:
openbrowser-ai daemon stop
openbrowser-ai daemon status
daemon stop closes every tab, exits Chrome, flushes saved cookies/login state to the profile, and shuts down the daemon process. daemon status should report the daemon is not running. If it still reports running, the daemon is wedged, force-kill it:
pkill -f 'openbrowser.*daemon' || true
If your invocation can fail mid-workflow (timeout, navigation error, malformed DOM), guarantee cleanup with a shell trap so the browser is never left orphaned:
trap 'openbrowser-ai daemon stop >/dev/null 2>&1 || true' EXIT
# ... openbrowser-ai -c calls here ...
Do not rely on the idle timeout. Do not call done() as a substitute, done() only marks the task complete inside the agent loop, it does not close the browser.
Files (openbrowser-ai)
-
SKILL.md 7.9 KB
--- name: page-analysis description: | Analyze web page content, structure, and layout to understand what a page contains and how it is organized. Trigger when the user asks to: analyze a page, understand page structure, inspect a website, summarize page content, examine page layout, review a web page, or describe what is on a page. allowed-tools: Bash(openbrowser-ai:*) Bash(curl:*) Bash(uv:*) Bash(irm:*) Read Write --- # Page Analysis Analyze and understand web page content, structure, and interactive elements using Python code execution. Produces a comprehensive breakdown of what is on the page and how it is organized. All code runs via `openbrowser-ai -c`. The daemon starts automatically and persists variables across calls. All browser functions are async -- use `await`. The CLI daemon also persists cookies and login state in `~/.config/openbrowser/profiles/daemon/storage_state.json`, so authenticated sessions can be reused across later runs. ## Setup Before running, verify openbrowser-ai is installed: ```bash openbrowser-ai --help ``` If not found, install: ```bash # macOS/Linux curl -fsSL https://raw.githubusercontent.com/billy-enrizky/openbrowser-ai/main/install.sh | sh # Windows (PowerShell) irm https://raw.githubusercontent.com/billy-enrizky/openbrowser-ai/main/install.ps1 | iex ``` ## Workflow ### Step 1 -- Navigate and get overview ```bash openbrowser-ai -c - <<'EOF' await navigate("https://example.com") state = await browser.get_browser_state_summary() print(f"Title: {state.title}") print(f"URL: {state.url}") print(f"Interactive elements: {len(state.dom_state.selector_map)}") print(f"Tabs: {len(state.tabs)}") EOF ``` ### Step 2 -- Extract page metadata ```bash openbrowser-ai -c - <<'EOF' meta = await evaluate(""" (function(){ return { title: document.title, description: document.querySelector("meta[name='description']")?.content, canonical: document.querySelector("link[rel='canonical']")?.href, ogTitle: document.querySelector("meta[property='og:title']")?.content, ogImage: document.querySelector("meta[property='og:image']")?.content, lang: document.documentElement.lang, charset: document.characterSet }; })() """) import json print(json.dumps(meta, indent=2)) EOF ``` ### Step 3 -- Detect frameworks and technologies ```bash openbrowser-ai -c - <<'EOF' tech = await evaluate(""" (function(){ const t = []; if (window.__NEXT_DATA__) t.push("Next.js"); if (window.__NUXT__) t.push("Nuxt.js"); if (document.querySelector("[data-reactroot]") || document.querySelector("#__next")) t.push("React"); if (document.querySelector("[ng-version]")) t.push("Angular"); if (window.jQuery) t.push("jQuery"); if (window.Vue) t.push("Vue.js"); if (document.querySelector("[data-svelte]")) t.push("Svelte"); return t; })() """) print(f"Technologies detected: {tech}") EOF ``` ### Step 4 -- Content summary and statistics ```bash openbrowser-ai -c - <<'EOF' stats = await evaluate(""" (function(){ return { headings: document.querySelectorAll("h1,h2,h3,h4,h5,h6").length, paragraphs: document.querySelectorAll("p").length, images: document.querySelectorAll("img").length, links: document.querySelectorAll("a").length, forms: document.querySelectorAll("form").length, tables: document.querySelectorAll("table").length, lists: document.querySelectorAll("ul,ol").length, buttons: document.querySelectorAll("button,[role='button']").length, inputs: document.querySelectorAll("input,textarea,select").length, iframes: document.querySelectorAll("iframe").length, scripts: document.querySelectorAll("script").length, stylesheets: document.querySelectorAll("link[rel='stylesheet']").length }; })() """) import json print("Content statistics:") print(json.dumps(stats, indent=2)) EOF ``` ### Step 5 -- Analyze heading structure ```bash openbrowser-ai -c - <<'EOF' headings = await evaluate(""" (function(){ return Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6")).map(h => ({ tag: h.tagName, text: h.textContent.trim().substring(0, 80) })); })() """) for h in headings: htag = h["tag"] htext = h["text"] indent = " " * (int(htag[1]) - 1) print(f"{indent}{htag}: {htext}") EOF ``` ### Step 6 -- Analyze interactive elements ```bash openbrowser-ai -c - <<'EOF' state = await browser.get_browser_state_summary() elements_by_tag = {} for idx, el in state.dom_state.selector_map.items(): tag = el.tag_name elements_by_tag.setdefault(tag, []).append({ "index": idx, "text": el.get_all_children_text(max_depth=1)[:50], "type": el.attributes.get("type", ""), "href": el.attributes.get("href", "")[:50] if el.attributes.get("href") else "", }) for tag, elems in sorted(elements_by_tag.items()): print(f"\n{tag} ({len(elems)} elements):") for e in elems[:5]: eidx = e["index"] etxt = e["text"] etype = e["type"] ehref = e["href"] print(f" [{eidx}] text=\"{etxt}\" type={etype} href={ehref}") if len(elems) > 5: print(f" ... and {len(elems) - 5} more") EOF ``` ### Step 7 -- Page dimensions and scroll analysis ```bash openbrowser-ai -c - <<'EOF' dims = await evaluate(""" (function(){ return { viewportWidth: window.innerWidth, viewportHeight: window.innerHeight, scrollHeight: document.body.scrollHeight, scrollWidth: document.body.scrollWidth, scrollable: document.body.scrollHeight > window.innerHeight }; })() """) import json print(json.dumps(dims, indent=2)) if dims["scrollable"]: pages = dims["scrollHeight"] / dims["viewportHeight"] print(f"Page is approximately {pages:.1f} viewport heights long") EOF ``` ### Step 8 -- Search for specific content patterns ```bash openbrowser-ai -c - <<'EOF' import re # Get page text for Python-side analysis text_content = await evaluate("document.body.innerText") # Find emails emails = re.findall(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", text_content) print(f"Emails found: {emails}") # Find phone numbers phones = re.findall(r"\+?\d[\d\s()-]{7,}", text_content) print(f"Phone numbers found: {phones}") # Find dates dates = re.findall(r"\d{4}-\d{2}-\d{2}|\w+ \d{1,2},? \d{4}", text_content) print(f"Dates found: {dates}") EOF ``` ## Tips - Code is piped via stdin using heredoc (`-c - <<'EOF'`), so all Python syntax works without shell escaping issues. - Start with `evaluate()` for metadata and DOM statistics -- gives a fast structured overview. - Use `browser.get_browser_state_summary()` for interactive element analysis. - Use Python regex on extracted text for pattern matching (emails, phones, dates, prices). - For long pages, use `await scroll(down=True)` and re-extract to analyze below-fold content. - Variables persist between `-c` calls while the daemon is running, so you can build a comprehensive analysis incrementally. ## Cleanup This step is **mandatory**. Run it after the analysis finishes, whether extraction succeeded or the page failed to load. Without it, the daemon keeps Chrome running until its 10-minute idle timeout, leaving a stale browser process, a locked profile, and (on macOS/Linux desktop) a visible window. Stop the daemon, then verify it is gone: ```bash openbrowser-ai daemon stop openbrowser-ai daemon status ``` `daemon stop` closes every tab, exits Chrome, flushes saved cookies/login state to the profile, and shuts down the daemon process. `daemon status` should report the daemon is not running. If it still reports running, the daemon is wedged, force-kill it: ```bash pkill -f 'openbrowser.*daemon' || true ``` If your invocation can fail mid-workflow (timeout, navigation error, malformed DOM), guarantee cleanup with a shell trap so the browser is never left orphaned: ```bash trap 'openbrowser-ai daemon stop >/dev/null 2>&1 || true' EXIT # ... openbrowser-ai -c calls here ... ``` Do not rely on the idle timeout. Do not call `done()` as a substitute, `done()` only marks the task complete inside the agent loop, it does not close the browser.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.