yggdrasil.loki.web¶
web ¶
Loki on the internet — fetch, browse, tables, images.
Loki reaches the web through yggdrasil's own stack, not a bolted-on client:
every request rides :class:~yggdrasil.http_.HTTPSession (its pooling, retry,
and response cache), and every tabular body is parsed through the io tabular
handlers — :meth:HTTPResponse.to_polars auto-detects CSV / JSON / Parquet /
Arrow / XLSX. So "look it up on the internet" and "parse that table" are the
same two project abstractions the rest of yggdrasil runs on.
from yggdrasil.loki import web
web.read_text("https://example.com") # browse → readable text + links
web.read_table("https://…/data.csv") # → polars DataFrame (any format)
web.read_json("https://api.example.com/x") # → decoded JSON
web.read_image("https://…/chart.png") # → bytes + dims + content-type
Browser ¶
A headless browser session for interacting with a live page.
Drive a page the way a person would — :meth:goto, :meth:fill a field,
:meth:type, :meth:check a box, :meth:select_option, :meth:click a
button, :meth:submit, then read the result (:meth:text, :attr:url,
:meth:title). Backed by Playwright (Chromium by default), imported only
when you open one. Use as a context manager so the browser always closes::
with web.Browser() as b:
b.goto("https://example.com/login")
b.fill("#user", "me").fill("#pass", "secret").submit("button[type=submit]")
print(b.url, b.text())
The action methods return self so calls chain.
fetch ¶
fetch(
url: str,
*,
params: Optional[dict[str, str]] = None,
headers: Optional[dict[str, str]] = None,
timeout: float = 30.0
) -> "HTTPResponse"
GET url through the yggdrasil HTTP session → an :class:HTTPResponse.
Sends a realistic browser header profile (from the http_ user-agent utils) so pages render normally, unless the caller overrides specific headers.
read_table ¶
Fetch url and parse it into a polars DataFrame via the io handlers.
Handles every tabular format the io layer knows (CSV / JSON / Parquet /
Arrow / XLSX). fmt (e.g. "csv") forces the leaf when the URL has no
extension and the server mislabels the body.
read_image ¶
Fetch an image; report its content type, byte size, and pixel dimensions. Optionally save the bytes to save_to.
read_text ¶
Browse url: fetch it and return readable text + links.
HTML is stripped to readable text (scripts/styles dropped) and its anchors collected — a lightweight browser view. Non-HTML bodies come back as text.
scrape ¶
Scrape a page into structured pieces — title, meta, text, links, tables.
A richer :func:read_text: pulls the <title> and meta description, the
readable text, the anchor links, the first HTML <table> as records (if
any), and any embedded JSON-LD structured data. Legitimate extraction of a
public page's content — it does not defeat access controls.
browser_available ¶
True when Playwright (and a browser binary) is installed for automation.
A pure probe — never installs. :func:ensure_browser is the install path.
ensure_browser ¶
Make the headless browser usable, auto-installing on demand.
Installs the playwright package (via Loki's runtime auto-installer) and
then its Chromium binary (python -m playwright install chromium) when
either is missing — unless auto-install is disabled
(YGG_LOKI_AUTO_INSTALL=0 or install=False), in which case it just
probes. Returns whether the browser is ready.
fill_form ¶
fill_form(
url: str,
fields: dict[str, str],
*,
submit: Optional[str] = None,
headless: bool = True,
browser: str = "chromium",
wait_for: Optional[str] = None,
screenshot: Optional[str] = None,
max_chars: int = 4000
) -> dict[str, Any]
Open url, fill fields (CSS selector → value), optionally submit, and return the resulting page state — the high-level "fill in this form".
submit is the selector of the submit button (omit to skip submitting);
wait_for waits for a selector to appear after submit (e.g. a results
panel); screenshot saves a PNG of the final page.
interact ¶
interact(
url: str,
steps: list[dict[str, Any]],
*,
headless: bool = True,
browser: str = "chromium",
screenshot: Optional[str] = None,
max_chars: int = 4000
) -> dict[str, Any]
Drive a page through a sequence of interaction steps, return its final state. Each step is one action dict — the page is a thing you operate::
web.interact("https://shop.example/search", [
{"type": ["#q", "wireless headphones"]},
{"press": ["#q", "Enter"]},
{"wait_for": ".results"},
{"click": ".results a:first-child"},
])
Actions: goto (url), fill/type/select/press ([selector,
value]), click/check/wait_for (selector), submit (button
selector or null for Enter).
discover_apis ¶
Discover the data APIs a page already uses — its underlying endpoints.
Inspects a public page for the data sources it embeds or calls: JSON-LD
structured data, <script type="application/json"> payloads (e.g.
__NEXT_DATA__), and candidate endpoint URLs referenced in markup/scripts
(/api/…, *.json/*.csv, fetch(...) targets). This surfaces the
documented/embedded APIs to pull data from — it does not bypass auth; any
endpoint that needs credentials still needs them.