Purpose
Load any Llano Commercial Realty property listing and return a deterministic structured snapshot of the listing content.
When to Use
Use when the caller provides a Llano Commercial Realty property identifier, such as {property-id} or the full propertyId token from a listing URL.
Workflow
- Build the embedded Buildout listing URL directly:
https://buildout.com/plugins/4ac67f3d21ba1955bfb17190aaa7bf314cd8892a/llanocommercialrealty.com/inventory/{property-id}?pluginId=0&iframe=true&embedded=true&cacheSearch=true&propertyId={property-id} - Navigate to that URL and wait for
.pdt-header1 h1or#overviewto appear. - In the same browser call, run this
evaluate()extractor:
(() => {
const text = (el) => el?.innerText?.trim() || null;
const section = (slug) => document.querySelector(`section[slug="${slug}"]`);
const customText = (slug) =>
text(section(slug)?.querySelector('[slug$="_custom_text"]'));
const tableRows = (table) =>
[...(table?.querySelectorAll("tr") || [])]
.map((row) =>
[...row.querySelectorAll("th,td")].map((cell) => text(cell)).filter(Boolean),
)
.filter((row) => row.length);
const details = section("property_details_section");
const spaces = [
...document.querySelectorAll(
"#spaces table tbody tr.js-lease-space-row-toggle, #spaces table tbody tr",
),
]
.map((row) =>
[...row.querySelectorAll("th,td")].map((cell) => text(cell)).filter(Boolean),
)
.filter((row) => row.length);
const broker = document.querySelector(".pdt-broker");
const images =
document.querySelector(".pdt-image-gallery")?.getAttribute("images") || null;
return {
url: location.href,
title: text(document.querySelector(".pdt-header1 h1")),
address: text(document.querySelector(".pdt-header2 h2")),
price: text(document.querySelector('[slug="price-header"] h1')),
propertyDetails: tableRows(details?.querySelector("table")),
description: customText("description_section"),
locationDescription: customText("location_description_section"),
highlights: customText("highlights_section"),
spaces,
broker: broker
? {
name: text(broker.querySelector(".pdt-broker-name strong")),
title: text(broker.querySelector(".pdt-broker-title")),
license: text(broker.querySelector(".pdt-broker-license")),
phone: text(broker.querySelector(".pdt-broker-cell-phone a")),
email: text(broker.querySelector('a[href^="mailto:"]')),
}
: null,
imageMetadata: images,
};
})();Site-Specific Gotchas
- The public
/properties?propertyId={property-id}page embeds the actual listing in a cross-origin Buildout iframe; navigating directly to the Buildout URL avoids the wrapper page and iframe discovery. - The property identifier is an opaque listing token and must be supplied or extracted from the caller's property URL; do not invent it.
- Listing content is organized under
section[slug]elements rather than a conventional page-specific URL structure. - Lease spaces are represented by table rows with the
js-lease-space-row-toggleclass; the fallback selector also captures ordinary rows if that class changes. - The image gallery's
imagesattribute may contain serialized metadata and is returned without attempting to interpret it.
Expected Output
Return one object containing the listing URL, title, address, price, property-detail table rows, description, location description, highlights, lease-space rows, broker contact fields, and optional image metadata. Missing fields are returned as null, and absent table collections as empty arrays.