Purpose
Provide a fast, reusable scraper for Infrastructure Capital Advisors ETF pages. The site exposes individual funds at direct ticker-based paths and uses table.table-small for compact fund data tables, including fee metadata such as Management Fee.
When to Use
Use when the caller supplies an ICA fund ticker or ticker-like page slug and needs the fund heading, page metadata, links, tabular holdings, performance, factsheet data, or the exact text/value of a named table row.
Workflow
- Build the fund URL directly as
https://infracapfund.com/{ticker}. Preserve the supplied ticker spelling; observed routes include/icap,/scap,/qvol, and/BNDS. - Navigate to that URL with
waitUntil: "domcontentloaded". - If the target table is not populated immediately, wait for
table.table-small tdfor up to 6 seconds. - Run this evaluator on the loaded page. It returns normalized tables, raw HTML for compact tables, and exact matching rows for a requested label such as
Management Fee.
((label = "Management Fee") => {
const clean = (value) => (value || "").replace(/\\s+/g, " ").trim();
const wanted = clean(label).toLowerCase();
const tables = [...document.querySelectorAll("table")]
.map((table) => ({
selector: table.matches("table.table-small") ? "table.table-small" : "table",
caption: clean(table.querySelector("caption")?.innerText),
html: table.matches("table.table-small") ? table.outerHTML : undefined,
rows: [...table.querySelectorAll("tr")]
.map((row) => {
const cells = [...row.querySelectorAll("th, td")].map((cell) =>
clean(cell.innerText),
);
return cells.length ? cells : [clean(row.innerText)];
})
.filter((row) => row.some(Boolean)),
}))
.filter((table) => table.rows.length);
const matchingRows = tables.flatMap((table) =>
table.rows
.filter((row) =>
row.some(
(cell) =>
cell.toLowerCase() === wanted || cell.toLowerCase().includes(wanted),
),
)
.map((row) => ({ label, row })),
);
return {
url: location.href,
title: clean(document.title),
h1: clean(document.querySelector("h1")?.innerText),
headings: [...document.querySelectorAll("h2, h3")]
.map((node) => clean(node.innerText))
.filter(Boolean),
managementFeeRows: matchingRows,
tables,
links: [...document.querySelectorAll("a[href]")]
.map((a) => ({ text: clean(a.innerText), href: a.href }))
.filter((link) => link.text || link.href),
};
})();Use managementFeeRows to determine whether the requested row exists and report its exact normalized cell values. Use tables[*].html when the caller specifically requests the raw HTML representation.
Site-Specific Gotchas
- The canonical direct fund host is
infracapfund.com; related navigation also exposesicapfunds.comandwww.infracapfunds.com, including a/fundscatalog, but visiting the catalog is unnecessary when the ticker is known. - Fund pages use ticker-based paths rather than opaque IDs. Do not invent a different slug or use the issuer homepage as an intermediate step.
- Ticker paths may use mixed case, as shown by
/BNDS; retain the caller's path casing when possible. - Compact fund information is commonly rendered in
table.table-small; table contents may appear shortly afterdomcontentloaded, so conditionally wait fortable.table-small tdbefore extraction. - Fee labels may be rendered as table cells with the value in an adjacent cell; match the label cell and preserve the complete row rather than assuming a fixed column count.
- The extractor intentionally includes all tables and page links because different fund pages can expose different table categories and document links.
Expected Output
Return an object containing the resolved URL, page title, primary heading, secondary headings, managementFeeRows for the requested label, every populated table as normalized row arrays, raw HTML for compact tables, and all page links with their visible text and absolute URLs.