Browser Harness: Query a Dashboard Using a Signed-in Chrome Session

Reuse an independent signed-in Chrome profile, extract a bounded order table, save status summaries, and diagnose session, tab, and field changes.

16Yun Engineering TeamOct 28, 20264 min read

A dashboard may already have a human-established login and filter selection. Starting a fresh browser just to read one table can introduce unnecessary authentication work.

Browser Harness can connect to an existing Chrome session. This tutorial reads one currently filtered order page in a separate test profile and saves records plus status summaries. It does not export every historical order or modify the application.

Prepare an explicit browser environment

Use Python 3.12, uv, and a supported Chrome installation. Follow the uv installation guide if necessary:

uv tool install --python 3.12 'browser-harness==0.1.13'
browser-harness recordings disable

Local Chrome does not require a Browser Use cloud key. Recording stays disabled because this task only needs a read-only query.

Open an independent test profile and allow remote debugging for that instance through Chrome's inspect settings. Log into your own test dashboard manually in that profile. Login in another browser window is not automatically the same connected session.

See the Browser Harness setup instructions.

Confirm the selected tab

browser-harness <<'PY'
print(page_info())
print(list_tabs())
PY

Compare title and URL with the intended dashboard. If needed, use switch_tab(target) with the actual target returned by the tab list, not an identifier copied from another machine.

An available CDP connection does not establish a valid dashboard login or filter state. Avoid treating Chrome settings or an empty tab as a business source.

Map the dashboard's data contract

The following HTML defines the example selectors:

<div data-testid="filter-summary">2026-10-01 至 2026-10-28,全部状态,当前页</div>
<table data-testid="orders">
  <tbody>
    <tr data-order-id="o-1001" data-total-minor="1290">
      <td data-field="status">paid</td>
    </tr>
    <tr data-order-id="o-1002" data-total-minor="2500">
      <td data-field="status">pending</td>
    </tr>
  </tbody>
</table>

Map them to your actual page. This is an illustrative DOM contract, not a hosted backend with an automatic login flow.

The order ID is stable identity. The amount attribute contains integer minor units, not formatted display money. The filter summary identifies the current page's scope.

Set the date and status filters manually and wait for the table update. For an application you maintain, stable attributes can be more maintainable than positional cell selectors. Otherwise map current headings and DOM deliberately.

Extract and summarize without changing session state

With the correct dashboard tab selected, run:

browser-harness <<'PY'
import json
from collections import Counter
from pathlib import Path
 
if not wait_for_element('table[data-testid="orders"]', timeout=15):
    raise RuntimeError("订单表未就绪;请检查当前标签、登录和加载状态")
 
result = js("""
(() => ({
  source_url: location.href,
  scope: document.querySelector('[data-testid="filter-summary"]')?.textContent?.trim(),
  rows: [...document.querySelectorAll('table[data-testid="orders"] tbody tr')].map(row => ({
    order_id: row.getAttribute('data-order-id'),
    total_minor: row.getAttribute('data-total-minor'),
    status: row.querySelector('[data-field="status"]')?.textContent?.trim()
  }))
}))()
""")
if not result.get("scope"):
    raise RuntimeError("缺少筛选范围说明")
seen = set()
totals = Counter()
for row in result["rows"]:
    if not row["order_id"] or row["order_id"] in seen:
        raise RuntimeError("订单 ID 缺失或重复")
    if row["status"] not in {"paid", "pending", "cancelled"}:
        raise RuntimeError("订单状态不在本次约定内")
    if not isinstance(row["total_minor"], str) or not row["total_minor"].isdigit():
        raise RuntimeError("金额必须是非负整数分")
    row["total_minor"] = int(row["total_minor"])
    seen.add(row["order_id"])
    totals[row["status"]] += row["total_minor"]
result["counts"] = dict(Counter(row["status"] for row in result["rows"]))
result["totals_minor_by_status"] = dict(totals)
Path("orders.json").write_text(
    json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
)
print({"scope": result["scope"], "rows": len(result["rows"]),
       "counts": result["counts"]})
PY

The script reads DOM data and writes orders.json in the CLI working directory. It neither navigates to an unknown destination nor clears cookies.

An empty table is not proof that an entire order history is empty. Inspect the site's explicit empty state and selected scope. Pagination requires additional traversal and coverage accounting; this page's total is not automatically the full date range total.

Verify the saved result

For the supplied HTML, expect one paid and one pending order, with 1290 and 2500 minor units respectively. Actual results depend on the current DOM.

Check the source URL and scope, then compare representative order IDs and amounts. Multi-currency dashboards need currency fields and separate totals.

Do not close an entire user browser after reading a reused tab. A newly created task tab can be closed independently. Client disconnection, daemon lifetime, and Chrome lifetime are different concerns.

Recover from changes

Login page appears: stop, manually authenticate again in the same test profile, and recheck URL and table identity. Do not export a login page as zero orders.

Wrong tab: list targets again, select the actual dashboard, and verify its URL. Duplicate titles are insufficient.

Table never appears: inspect loading, frame ownership, and layout changes. A frame needs its appropriate target, not a broader root-page selector.

Status values change: the example rejects unknown values. Determine whether the business added a state or only changed its display label before updating the mapping.

Tool errors have different shapes: CLI and MCP layers may surface exceptions or tool errors. Do not assume every failure is an object with an error property, and do not save success output without a complete result.

Old filtered data remains: wait for the actual update marker, preserve the displayed scope, and verify source rows rather than adding a fixed sleep.

Session reuse removes repeated login work; correct tab selection, scope, and field meaning still determine the query's reliability.

Need an enterprise proxy plan?

We can tailor architecture to your target domains, concurrency, and reliability goals.