mcp-scraper 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/api-server.cjs +296 -198
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +3 -3
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -3
- package/dist/bin/paa-harvest.cjs +166 -74
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +1 -1
- package/dist/chunk-EGWJ74EX.js +7 -0
- package/dist/chunk-EGWJ74EX.js.map +1 -0
- package/dist/{chunk-APJO2XV5.js → chunk-HL33CGJF.js} +3 -3
- package/dist/{chunk-APJO2XV5.js.map → chunk-HL33CGJF.js.map} +1 -1
- package/dist/{chunk-AUCXKRRH.js → chunk-RMPPYKUV.js} +167 -75
- package/dist/chunk-RMPPYKUV.js.map +1 -0
- package/dist/{chunk-BL7BBSYF.js → chunk-RUGJE5EB.js} +2 -2
- package/dist/{chunk-3K5GT6VG.js → chunk-YGRZU7IR.js} +3 -3
- package/dist/index.cjs +166 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{server-WHM446IZ.js → server-JNY4XPZE.js} +81 -76
- package/dist/server-JNY4XPZE.js.map +1 -0
- package/dist/{site-extract-repository-U476J44K.js → site-extract-repository-NVSZH35Y.js} +3 -3
- package/dist/{worker-KJZ3ZN2N.js → worker-JQTS437L.js} +4 -4
- package/dist/worker-JQTS437L.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-AUCXKRRH.js.map +0 -1
- package/dist/chunk-F2BXETDX.js +0 -7
- package/dist/chunk-F2BXETDX.js.map +0 -1
- package/dist/server-WHM446IZ.js.map +0 -1
- package/dist/worker-KJZ3ZN2N.js.map +0 -1
- package/docs/spec-browser-routing.md +0 -108
- package/docs/spec-kernel-computer-controls.md +0 -166
- package/docs/spec-mcp-tools.md +0 -146
- package/docs/spec-server-instructions.md +0 -94
- package/docs/specs/agent-ready-seo-packet-spec.md +0 -237
- package/docs/specs/audit-visual-demo.md +0 -309
- package/docs/specs/cli-agent-wiring-spec.md +0 -206
- package/docs/specs/local-competitive-audit-spec.md +0 -311
- package/docs/specs/scheduled-workflows-api-spec.md +0 -304
- package/docs/specs/seo-cli-growth-roadmap-spec.md +0 -181
- package/docs/specs/seo-workflow-runner-and-reports-spec.md +0 -241
- /package/dist/{chunk-BL7BBSYF.js.map → chunk-RUGJE5EB.js.map} +0 -0
- /package/dist/{chunk-3K5GT6VG.js.map → chunk-YGRZU7IR.js.map} +0 -0
- /package/dist/{site-extract-repository-U476J44K.js.map → site-extract-repository-NVSZH35Y.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LedgerOperation
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HL33CGJF.js";
|
|
4
4
|
import {
|
|
5
5
|
sanitizeVendorName
|
|
6
6
|
} from "./chunk-M2S27J6Z.js";
|
|
@@ -127,4 +127,4 @@ export {
|
|
|
127
127
|
failExtractJob,
|
|
128
128
|
settleExtractJob
|
|
129
129
|
};
|
|
130
|
-
//# sourceMappingURL=chunk-
|
|
130
|
+
//# sourceMappingURL=chunk-RUGJE5EB.js.map
|
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
} from "./chunk-XGIPATLV.js";
|
|
18
18
|
import {
|
|
19
19
|
PACKAGE_VERSION
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-EGWJ74EX.js";
|
|
21
21
|
import {
|
|
22
22
|
MC_PER_CREDIT
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-HL33CGJF.js";
|
|
24
24
|
import {
|
|
25
25
|
sanitizeVendorName
|
|
26
26
|
} from "./chunk-M2S27J6Z.js";
|
|
@@ -8767,4 +8767,4 @@ export {
|
|
|
8767
8767
|
registerMemoryMcpTools,
|
|
8768
8768
|
MemoryMcpToolExecutor
|
|
8769
8769
|
};
|
|
8770
|
-
//# sourceMappingURL=chunk-
|
|
8770
|
+
//# sourceMappingURL=chunk-YGRZU7IR.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -1688,13 +1688,41 @@ var PAAExtractor = class {
|
|
|
1688
1688
|
}
|
|
1689
1689
|
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
1690
1690
|
}
|
|
1691
|
-
return Array.from(document.querySelectorAll(selectors.item)).map((pair) =>
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1691
|
+
return Array.from(document.querySelectorAll(selectors.item)).map((pair) => {
|
|
1692
|
+
let sourceTitle = pair.querySelector(selectors.sourceTitle)?.innerText?.trim() || void 0;
|
|
1693
|
+
let sourceSite = pair.querySelector(selectors.sourceSite)?.innerText?.trim() || void 0;
|
|
1694
|
+
let sourceCite = pair.querySelector(selectors.sourceCite)?.innerText?.trim() || void 0;
|
|
1695
|
+
if (!sourceCite) {
|
|
1696
|
+
const seenHrefs = /* @__PURE__ */ new Set();
|
|
1697
|
+
const anchors = [];
|
|
1698
|
+
for (const a of Array.from(pair.querySelectorAll("a[href]"))) {
|
|
1699
|
+
const href = a.href;
|
|
1700
|
+
if (!/^https?:\/\//.test(href) || seenHrefs.has(href)) continue;
|
|
1701
|
+
let host = "";
|
|
1702
|
+
try {
|
|
1703
|
+
host = new URL(href).hostname;
|
|
1704
|
+
} catch {
|
|
1705
|
+
continue;
|
|
1706
|
+
}
|
|
1707
|
+
if (/(^|\.)google(usercontent)?\.[a-z.]+$/.test(host)) continue;
|
|
1708
|
+
seenHrefs.add(href);
|
|
1709
|
+
anchors.push({ text: (a.textContent || "").trim(), href, host });
|
|
1710
|
+
}
|
|
1711
|
+
const primary = anchors.find((x) => x.text.length > 2) ?? anchors[0];
|
|
1712
|
+
if (primary) {
|
|
1713
|
+
sourceTitle = sourceTitle || primary.text || void 0;
|
|
1714
|
+
sourceSite = sourceSite || primary.host.replace(/^www\./, "");
|
|
1715
|
+
sourceCite = primary.href;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
return {
|
|
1719
|
+
question: pair.getAttribute(selectors.itemDataQ) || pair.getAttribute(selectors.itemDataInitQ) || pair.querySelector(selectors.itemQuestionEl)?.innerText?.trim() || "",
|
|
1720
|
+
answer: cleanText(pair.querySelector(selectors.answerContainer)) || void 0,
|
|
1721
|
+
sourceTitle,
|
|
1722
|
+
sourceSite,
|
|
1723
|
+
sourceCite
|
|
1724
|
+
};
|
|
1725
|
+
});
|
|
1698
1726
|
}, sels);
|
|
1699
1727
|
return raw.flatMap((item) => {
|
|
1700
1728
|
const cleaned = {
|
|
@@ -1736,54 +1764,115 @@ var PAAExtractor = class {
|
|
|
1736
1764
|
const seenQs = /* @__PURE__ */ new Set();
|
|
1737
1765
|
const orderedQs = [];
|
|
1738
1766
|
const results = [];
|
|
1739
|
-
const
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1767
|
+
const abandonedNeverReclick = /* @__PURE__ */ new Set();
|
|
1768
|
+
const clickedOnceEver = /* @__PURE__ */ new Set();
|
|
1769
|
+
const itemArgs = {
|
|
1770
|
+
sel: PAASelectors.item,
|
|
1771
|
+
dataQ: PAASelectors.itemDataQ,
|
|
1772
|
+
dataInitQ: PAASelectors.itemDataInitQ,
|
|
1773
|
+
questionEl: PAASelectors.itemQuestionEl,
|
|
1774
|
+
answerSel: PAASelectors.answerContainer,
|
|
1775
|
+
expandedClass: PAASelectors.expandedClass,
|
|
1776
|
+
clickTarget: PAASelectors.clickTarget
|
|
1777
|
+
};
|
|
1778
|
+
const readItemStates = () => page.evaluate(
|
|
1779
|
+
({ sel, dataQ, dataInitQ, questionEl, expandedClass, answerSel }) => {
|
|
1780
|
+
const answerText = (root) => {
|
|
1781
|
+
if (!root) return "";
|
|
1782
|
+
let out = "";
|
|
1783
|
+
const walk = (n) => {
|
|
1784
|
+
for (const c of Array.from(n.childNodes)) {
|
|
1785
|
+
if (c.nodeType === Node.TEXT_NODE) out += c.textContent || "";
|
|
1786
|
+
else if (c.nodeType === Node.ELEMENT_NODE && c.tagName !== "STYLE" && c.tagName !== "SCRIPT") walk(c);
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
walk(root);
|
|
1790
|
+
return out.replace(/\s+/g, " ").trim();
|
|
1791
|
+
};
|
|
1792
|
+
return Array.from(document.querySelectorAll(sel)).map((el) => ({
|
|
1793
|
+
q: el.getAttribute(dataQ) || el.getAttribute(dataInitQ) || el.querySelector(questionEl)?.innerText?.trim() || "",
|
|
1794
|
+
done: el.classList.contains(expandedClass) || answerText(el.querySelector(answerSel)).length > 30
|
|
1795
|
+
})).filter((s) => s.q);
|
|
1796
|
+
},
|
|
1797
|
+
itemArgs
|
|
1744
1798
|
);
|
|
1799
|
+
const expandOneItemSerially = async (q) => {
|
|
1800
|
+
const clicked = await page.evaluate(
|
|
1801
|
+
({ sel, dataQ, dataInitQ, clickTarget, q: target }) => {
|
|
1802
|
+
const item = Array.from(document.querySelectorAll(sel)).find((el) => (el.getAttribute(dataQ) || el.getAttribute(dataInitQ)) === target);
|
|
1803
|
+
const btn = item?.querySelector(clickTarget);
|
|
1804
|
+
if (!btn) return false;
|
|
1805
|
+
btn.scrollIntoView({ block: "center" });
|
|
1806
|
+
btn.click();
|
|
1807
|
+
return true;
|
|
1808
|
+
},
|
|
1809
|
+
{ ...itemArgs, q }
|
|
1810
|
+
).catch(() => false);
|
|
1811
|
+
if (!clicked) return "failed";
|
|
1812
|
+
try {
|
|
1813
|
+
const outcome = await page.waitForFunction(
|
|
1814
|
+
({ sel, dataQ, dataInitQ, answerSel, q: target }) => {
|
|
1815
|
+
const answerText = (root) => {
|
|
1816
|
+
if (!root) return "";
|
|
1817
|
+
let out = "";
|
|
1818
|
+
const walk = (n) => {
|
|
1819
|
+
for (const c of Array.from(n.childNodes)) {
|
|
1820
|
+
if (c.nodeType === Node.TEXT_NODE) out += c.textContent || "";
|
|
1821
|
+
else if (c.nodeType === Node.ELEMENT_NODE && c.tagName !== "STYLE" && c.tagName !== "SCRIPT") walk(c);
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
walk(root);
|
|
1825
|
+
return out.replace(/\s+/g, " ").trim();
|
|
1826
|
+
};
|
|
1827
|
+
const item = Array.from(document.querySelectorAll(sel)).find((el) => (el.getAttribute(dataQ) || el.getAttribute(dataInitQ)) === target);
|
|
1828
|
+
if (!item) return "gone";
|
|
1829
|
+
const text = answerText(item.querySelector(answerSel));
|
|
1830
|
+
if (/an error has occurred/i.test(text.slice(0, 80))) return "error";
|
|
1831
|
+
if (text.length > 30) return "ok";
|
|
1832
|
+
return false;
|
|
1833
|
+
},
|
|
1834
|
+
{ ...itemArgs, q },
|
|
1835
|
+
{ timeout: 6500, polling: 200 }
|
|
1836
|
+
);
|
|
1837
|
+
return await outcome.jsonValue() === "ok" ? "ok" : "failed";
|
|
1838
|
+
} catch {
|
|
1839
|
+
return "failed";
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1745
1842
|
let round = 0;
|
|
1746
|
-
|
|
1843
|
+
let growthWaits = 0;
|
|
1844
|
+
while (true) {
|
|
1747
1845
|
if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
|
|
1748
1846
|
this.throwIfAborted(signal);
|
|
1749
|
-
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
if (unexpandedCount === 0) break;
|
|
1755
|
-
this.reporter.onDepth(++round);
|
|
1756
|
-
for (let ci = 0; ci < unexpandedCount; ci++) {
|
|
1757
|
-
if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
|
|
1758
|
-
this.throwIfAborted(signal);
|
|
1759
|
-
try {
|
|
1760
|
-
const btn = page.locator(unexpandedSel).first();
|
|
1761
|
-
await btn.scrollIntoViewIfNeeded();
|
|
1762
|
-
await btn.hover({ force: true });
|
|
1763
|
-
await page.waitForTimeout(100);
|
|
1764
|
-
await btn.click({ force: true });
|
|
1765
|
-
await page.waitForTimeout(500);
|
|
1766
|
-
} catch {
|
|
1847
|
+
const states = await readItemStates();
|
|
1848
|
+
for (const s of states) {
|
|
1849
|
+
if (!seenQs.has(s.q)) {
|
|
1850
|
+
seenQs.add(s.q);
|
|
1851
|
+
orderedQs.push(s.q);
|
|
1767
1852
|
}
|
|
1768
1853
|
}
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
}
|
|
1854
|
+
const kept = new Set(orderedQs.slice(0, options.maxQuestions));
|
|
1855
|
+
const clickable = states.filter((s) => kept.has(s.q) && !clickedOnceEver.has(s.q) && !abandonedNeverReclick.has(s.q));
|
|
1856
|
+
const target = clickable.find((s) => !s.done) ?? (seenQs.size < options.maxQuestions ? clickable[0] : void 0);
|
|
1857
|
+
if (!target) {
|
|
1858
|
+
if (seenQs.size >= options.maxQuestions) break;
|
|
1859
|
+
if (++growthWaits > 1) break;
|
|
1860
|
+
const grew = await page.waitForFunction(
|
|
1861
|
+
({ sel, min }) => document.querySelectorAll(sel).length > min,
|
|
1862
|
+
{ sel: PAASelectors.item, min: states.length },
|
|
1863
|
+
{ timeout: 3e3 }
|
|
1864
|
+
).catch(() => null);
|
|
1865
|
+
if (!grew) break;
|
|
1866
|
+
continue;
|
|
1783
1867
|
}
|
|
1868
|
+
growthWaits = 0;
|
|
1869
|
+
this.reporter.onDepth(++round);
|
|
1870
|
+
await this.throwIfCaptcha(page, "Google PAA expansion");
|
|
1871
|
+
clickedOnceEver.add(target.q);
|
|
1872
|
+
if (await expandOneItemSerially(target.q) === "failed") abandonedNeverReclick.add(target.q);
|
|
1784
1873
|
}
|
|
1785
1874
|
const itemMap = new Map((await this.extractVisibleItems(page)).map((i) => [i.question, i]));
|
|
1786
|
-
await this.fillIncompleteItems(page, orderedQs, itemMap, options);
|
|
1875
|
+
await this.fillIncompleteItems(page, orderedQs, itemMap, options, abandonedNeverReclick);
|
|
1787
1876
|
for (const q of orderedQs) {
|
|
1788
1877
|
if (results.length >= options.maxQuestions) break;
|
|
1789
1878
|
const key = this.normalizeQuestion(q);
|
|
@@ -1801,9 +1890,11 @@ var PAAExtractor = class {
|
|
|
1801
1890
|
this.completeness.paaWithoutSource = results.filter((r) => !r.source_title && !r.source_site && !r.source_cite).length;
|
|
1802
1891
|
return results;
|
|
1803
1892
|
}
|
|
1804
|
-
async fillIncompleteItems(page, orderedQs, itemMap, options) {
|
|
1893
|
+
async fillIncompleteItems(page, orderedQs, itemMap, options, abandonedNeverReclick) {
|
|
1805
1894
|
const kept = new Set(orderedQs.slice(0, options.maxQuestions));
|
|
1806
|
-
const missingBefore = new Set(
|
|
1895
|
+
const missingBefore = new Set(
|
|
1896
|
+
[...kept].filter((q) => !itemMap.get(q)?.answer && !abandonedNeverReclick?.has(q))
|
|
1897
|
+
);
|
|
1807
1898
|
if (missingBefore.size === 0) return;
|
|
1808
1899
|
for (let round = 0; round < 2; round++) {
|
|
1809
1900
|
if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
|
|
@@ -2195,40 +2286,41 @@ var PAAExtractor = class {
|
|
|
2195
2286
|
};
|
|
2196
2287
|
}
|
|
2197
2288
|
async captureAIOverviewShareUrl(page, options) {
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
const
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
return true;
|
|
2210
|
-
}
|
|
2211
|
-
sec = sec.parentElement;
|
|
2289
|
+
if (options?.softDeadlineMs && Date.now() >= options.softDeadlineMs) return null;
|
|
2290
|
+
const clicked = await page.evaluate(() => {
|
|
2291
|
+
const heads = Array.from(document.querySelectorAll('h1,h2,h3,[role="heading"],div')).filter((h) => (h.textContent || "").trim() === "AI Overview");
|
|
2292
|
+
for (const head of heads) {
|
|
2293
|
+
let sec = head;
|
|
2294
|
+
for (let up = 0; up < 8 && sec; up++) {
|
|
2295
|
+
const btn = sec.querySelector('[aria-label="Share"]');
|
|
2296
|
+
if (btn) {
|
|
2297
|
+
btn.scrollIntoView({ block: "center" });
|
|
2298
|
+
btn.click();
|
|
2299
|
+
return true;
|
|
2212
2300
|
}
|
|
2301
|
+
sec = sec.parentElement;
|
|
2213
2302
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2303
|
+
}
|
|
2304
|
+
return false;
|
|
2305
|
+
}).catch(() => false);
|
|
2306
|
+
if (!clicked) return null;
|
|
2307
|
+
let url = null;
|
|
2308
|
+
try {
|
|
2309
|
+
const found = await page.waitForFunction(() => {
|
|
2219
2310
|
const w = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
2220
2311
|
while (w.nextNode()) {
|
|
2221
2312
|
const m = (w.currentNode.textContent || "").match(/https:\/\/share\.google\/aimode\/\S+/);
|
|
2222
2313
|
if (m) return m[0];
|
|
2223
2314
|
}
|
|
2224
|
-
return
|
|
2225
|
-
}
|
|
2226
|
-
await
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
await page.waitForTimeout(600);
|
|
2315
|
+
return false;
|
|
2316
|
+
}, void 0, { timeout: 4500, polling: 250 });
|
|
2317
|
+
url = await found.jsonValue();
|
|
2318
|
+
} catch {
|
|
2319
|
+
url = null;
|
|
2230
2320
|
}
|
|
2231
|
-
|
|
2321
|
+
await page.keyboard.press("Escape").catch(() => {
|
|
2322
|
+
});
|
|
2323
|
+
return url;
|
|
2232
2324
|
}
|
|
2233
2325
|
buildTree(flat, _seed) {
|
|
2234
2326
|
const roots = [];
|