mcp-scraper 0.3.28 → 0.3.42

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.
Files changed (44) hide show
  1. package/dist/bin/api-server.cjs +2464 -1134
  2. package/dist/bin/api-server.cjs.map +1 -1
  3. package/dist/bin/api-server.js +3 -3
  4. package/dist/bin/mcp-scraper-cli.cjs +1 -1
  5. package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
  6. package/dist/bin/mcp-scraper-cli.js +1 -1
  7. package/dist/bin/mcp-scraper-install.cjs +1 -1
  8. package/dist/bin/mcp-scraper-install.cjs.map +1 -1
  9. package/dist/bin/mcp-scraper-install.js +1 -1
  10. package/dist/bin/mcp-stdio-server.cjs +237 -6
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +2 -2
  13. package/dist/bin/paa-harvest.cjs +109 -5
  14. package/dist/bin/paa-harvest.cjs.map +1 -1
  15. package/dist/bin/paa-harvest.js +2 -2
  16. package/dist/{chunk-TRNSXR46.js → chunk-DVRPXPYH.js} +22 -3
  17. package/dist/chunk-DVRPXPYH.js.map +1 -0
  18. package/dist/{chunk-QSSH4RCX.js → chunk-GMTS35L6.js} +111 -7
  19. package/dist/chunk-GMTS35L6.js.map +1 -0
  20. package/dist/{chunk-NGD4ITAO.js → chunk-KPF64WST.js} +238 -7
  21. package/dist/chunk-KPF64WST.js.map +1 -0
  22. package/dist/chunk-OUZZNW3I.js +7 -0
  23. package/dist/chunk-OUZZNW3I.js.map +1 -0
  24. package/dist/{chunk-H74L743B.js → chunk-RXJHXBGG.js} +40 -3
  25. package/dist/chunk-RXJHXBGG.js.map +1 -0
  26. package/dist/{db-FP2ABUU4.js → db-LYQENFPW.js} +2 -2
  27. package/dist/index.cjs +109 -5
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +8 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +2 -2
  32. package/dist/{server-IDDX7KGT.js → server-CZT7EULY.js} +1157 -246
  33. package/dist/server-CZT7EULY.js.map +1 -0
  34. package/dist/{worker-NV4GTIRG.js → worker-FNSBPP7Y.js} +4 -4
  35. package/package.json +1 -1
  36. package/dist/chunk-7MY2NSQX.js +0 -7
  37. package/dist/chunk-7MY2NSQX.js.map +0 -1
  38. package/dist/chunk-H74L743B.js.map +0 -1
  39. package/dist/chunk-NGD4ITAO.js.map +0 -1
  40. package/dist/chunk-QSSH4RCX.js.map +0 -1
  41. package/dist/chunk-TRNSXR46.js.map +0 -1
  42. package/dist/server-IDDX7KGT.js.map +0 -1
  43. /package/dist/{db-FP2ABUU4.js.map → db-LYQENFPW.js.map} +0 -0
  44. /package/dist/{worker-NV4GTIRG.js.map → worker-FNSBPP7Y.js.map} +0 -0
@@ -5,13 +5,13 @@ import {
5
5
  registerBrowserAgentMcpTools,
6
6
  registerPaaExtractorMcpTools,
7
7
  registerSerpIntelligenceCaptureTools
8
- } from "../chunk-NGD4ITAO.js";
8
+ } from "../chunk-KPF64WST.js";
9
9
  import {
10
10
  renderInstallTerminal
11
11
  } from "../chunk-T3VZD2I4.js";
12
12
  import {
13
13
  PACKAGE_VERSION
14
- } from "../chunk-7MY2NSQX.js";
14
+ } from "../chunk-OUZZNW3I.js";
15
15
  import "../chunk-3PRO376E.js";
16
16
  import "../chunk-BP27CZ5Q.js";
17
17
 
@@ -1587,6 +1587,13 @@ var PAAExtractor = class {
1587
1587
  }
1588
1588
  driver;
1589
1589
  reporter;
1590
+ completeness = {
1591
+ paaWithoutAnswer: 0,
1592
+ paaWithoutSource: 0,
1593
+ paaAnswersRecovered: 0,
1594
+ aioShareCaptured: null
1595
+ };
1596
+ aioShareUrlEarly = null;
1590
1597
  normalizeQuestion(q) {
1591
1598
  return q.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
1592
1599
  }
@@ -1715,6 +1722,7 @@ var PAAExtractor = class {
1715
1722
  }
1716
1723
  }
1717
1724
  const itemMap = new Map((await this.extractVisibleItems(page)).map((i) => [i.question, i]));
1725
+ await this.fillIncompleteItems(page, orderedQs, itemMap, options);
1718
1726
  for (const q of orderedQs) {
1719
1727
  if (results.length >= options.maxQuestions) break;
1720
1728
  const key = this.normalizeQuestion(q);
@@ -1728,8 +1736,52 @@ var PAAExtractor = class {
1728
1736
  results.push(this.toFlatRow({ question: q, answer: void 0, sourceTitle: void 0, sourceSite: void 0, sourceCite: void 0 }, 1, null, options.query));
1729
1737
  }
1730
1738
  }
1739
+ this.completeness.paaWithoutAnswer = results.filter((r) => !r.answer).length;
1740
+ this.completeness.paaWithoutSource = results.filter((r) => !r.source_title && !r.source_site && !r.source_cite).length;
1731
1741
  return results;
1732
1742
  }
1743
+ async fillIncompleteItems(page, orderedQs, itemMap, options) {
1744
+ const kept = new Set(orderedQs.slice(0, options.maxQuestions));
1745
+ const missingBefore = new Set([...kept].filter((q) => !itemMap.get(q)?.answer));
1746
+ if (missingBefore.size === 0) return;
1747
+ for (let round = 0; round < 2; round++) {
1748
+ if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
1749
+ const stillMissing = [...missingBefore].filter((q) => !itemMap.get(q)?.answer);
1750
+ if (stillMissing.length === 0) break;
1751
+ const reExpanded = await this.reExpandCollapsedItems(page).catch(() => false);
1752
+ await page.waitForTimeout(reExpanded ? 900 : 500);
1753
+ let improved = false;
1754
+ for (const item of await this.extractVisibleItems(page)) {
1755
+ if (!kept.has(item.question)) continue;
1756
+ const existing = itemMap.get(item.question);
1757
+ if (!item.answer || existing?.answer) continue;
1758
+ itemMap.set(item.question, {
1759
+ ...item,
1760
+ sourceTitle: item.sourceTitle ?? existing?.sourceTitle,
1761
+ sourceSite: item.sourceSite ?? existing?.sourceSite,
1762
+ sourceCite: item.sourceCite ?? existing?.sourceCite
1763
+ });
1764
+ improved = true;
1765
+ }
1766
+ if (!improved) break;
1767
+ }
1768
+ this.completeness.paaAnswersRecovered = [...missingBefore].filter((q) => itemMap.get(q)?.answer).length;
1769
+ }
1770
+ async reExpandCollapsedItems(page) {
1771
+ const unexpandedSel = `${PAASelectors.item}:not(.${PAASelectors.expandedClass}) ${PAASelectors.clickTarget}`;
1772
+ const count = await page.locator(unexpandedSel).count().catch(() => 0);
1773
+ if (count === 0) return false;
1774
+ for (let i = 0; i < count; i++) {
1775
+ try {
1776
+ const btn = page.locator(unexpandedSel).first();
1777
+ await btn.scrollIntoViewIfNeeded();
1778
+ await btn.click({ force: true });
1779
+ await page.waitForTimeout(300);
1780
+ } catch {
1781
+ }
1782
+ }
1783
+ return true;
1784
+ }
1733
1785
  async extractVideos(page) {
1734
1786
  const vsels = VideoSelectors;
1735
1787
  return page.evaluate((sels) => {
@@ -2064,12 +2116,58 @@ var PAAExtractor = class {
2064
2116
  }
2065
2117
  return { ...entityIds, entities: records, cids: [...cidSet] };
2066
2118
  }
2067
- async extractAISurfaces(page) {
2068
- return page.evaluate(extractAISurfacesFromDocument, {
2119
+ async extractAISurfaces(page, options) {
2120
+ const surfaces = await page.evaluate(extractAISurfacesFromDocument, {
2069
2121
  aio: AIOverviewSelectors,
2070
2122
  aim: AIModeSelectors,
2071
2123
  expandWaitMs: 1500
2072
2124
  });
2125
+ if (!surfaces.aiOverview.detected) {
2126
+ this.completeness.aioShareCaptured = null;
2127
+ return surfaces;
2128
+ }
2129
+ const shareUrl = this.aioShareUrlEarly ?? await this.captureAIOverviewShareUrl(page, options).catch(() => null);
2130
+ this.completeness.aioShareCaptured = shareUrl !== null;
2131
+ return {
2132
+ ...surfaces,
2133
+ aiOverview: { ...surfaces.aiOverview, shareUrl }
2134
+ };
2135
+ }
2136
+ async captureAIOverviewShareUrl(page, options) {
2137
+ for (let attempt = 0; attempt < 3; attempt++) {
2138
+ if (options?.softDeadlineMs && Date.now() >= options.softDeadlineMs) return null;
2139
+ const clicked = await page.evaluate(() => {
2140
+ const heads = Array.from(document.querySelectorAll('h1,h2,h3,[role="heading"],div')).filter((h) => (h.textContent || "").trim() === "AI Overview");
2141
+ for (const head of heads) {
2142
+ let sec = head;
2143
+ for (let up = 0; up < 8 && sec; up++) {
2144
+ const btn = sec.querySelector('[aria-label="Share"]');
2145
+ if (btn) {
2146
+ btn.scrollIntoView({ block: "center" });
2147
+ btn.click();
2148
+ return true;
2149
+ }
2150
+ sec = sec.parentElement;
2151
+ }
2152
+ }
2153
+ return false;
2154
+ }).catch(() => false);
2155
+ if (!clicked) return null;
2156
+ await page.waitForTimeout(4500);
2157
+ const url = await page.evaluate(() => {
2158
+ const w = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
2159
+ while (w.nextNode()) {
2160
+ const m = (w.currentNode.textContent || "").match(/https:\/\/share\.google\/aimode\/\S+/);
2161
+ if (m) return m[0];
2162
+ }
2163
+ return null;
2164
+ }).catch(() => null);
2165
+ await page.keyboard.press("Escape").catch(() => {
2166
+ });
2167
+ if (url) return url;
2168
+ await page.waitForTimeout(600);
2169
+ }
2170
+ return null;
2073
2171
  }
2074
2172
  buildTree(flat, _seed) {
2075
2173
  const roots = [];
@@ -2122,6 +2220,8 @@ var PAAExtractor = class {
2122
2220
  }
2123
2221
  async extract(options, signal) {
2124
2222
  const startMs = Date.now();
2223
+ this.completeness = { paaWithoutAnswer: 0, paaWithoutSource: 0, paaAnswersRecovered: 0, aioShareCaptured: null };
2224
+ this.aioShareUrlEarly = null;
2125
2225
  const isMobile = options.device === "mobile";
2126
2226
  const config = {
2127
2227
  headless: options.headless,
@@ -2160,6 +2260,7 @@ var PAAExtractor = class {
2160
2260
  this.throwIfAborted(signal);
2161
2261
  const page = this.driver.getPage();
2162
2262
  await this.throwIfCaptcha(page, "Google SERP");
2263
+ this.aioShareUrlEarly = await this.captureAIOverviewShareUrl(page, options).catch(() => null);
2163
2264
  if (options.serpOnly) {
2164
2265
  const [organicResults2, localPack2, rawEntityIds2] = await Promise.all([
2165
2266
  this.extractOrganicResults(page),
@@ -2167,7 +2268,7 @@ var PAAExtractor = class {
2167
2268
  this.extractEntityIds(page)
2168
2269
  ]);
2169
2270
  const entityIds2 = this.mergeLocalPackIntoEntities(rawEntityIds2, localPack2);
2170
- const aiSurfaces2 = await this.extractAISurfaces(page);
2271
+ const aiSurfaces2 = await this.extractAISurfaces(page, options);
2171
2272
  let locationEvidence2 = options.debug ? inferSerpLocationEvidence(canonicalLocation, organicResults2, localPack2) : void 0;
2172
2273
  let allOrganic2 = organicResults2;
2173
2274
  if ((options.pages ?? 1) >= 2) {
@@ -2196,6 +2297,7 @@ var PAAExtractor = class {
2196
2297
  diagnostics: {
2197
2298
  completionStatus: "serp_only",
2198
2299
  problem: null,
2300
+ completeness: { ...this.completeness },
2199
2301
  ...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence2) } : {}
2200
2302
  },
2201
2303
  totalQuestions: 0,
@@ -2239,7 +2341,7 @@ var PAAExtractor = class {
2239
2341
  locationEvidence2 = inferSerpLocationEvidence(canonicalLocation, noPaaOrganic, localPack);
2240
2342
  }
2241
2343
  }
2242
- const aiSurfaces2 = await this.extractAISurfaces(page);
2344
+ const aiSurfaces2 = await this.extractAISurfaces(page, options);
2243
2345
  const stats2 = {
2244
2346
  seed: options.query,
2245
2347
  totalQuestions: 0,
@@ -2255,6 +2357,7 @@ var PAAExtractor = class {
2255
2357
  diagnostics: {
2256
2358
  completionStatus: "no_paa",
2257
2359
  problem: null,
2360
+ completeness: { ...this.completeness },
2258
2361
  ...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence2) } : {}
2259
2362
  },
2260
2363
  totalQuestions: 0,
@@ -2274,7 +2377,7 @@ var PAAExtractor = class {
2274
2377
  }
2275
2378
  const flat = await this.runBFS(page, options, signal);
2276
2379
  this.throwIfAborted(signal);
2277
- const aiSurfaces = await this.extractAISurfaces(page);
2380
+ const aiSurfaces = await this.extractAISurfaces(page, options);
2278
2381
  const shortVidsParams = new URLSearchParams({ q: options.query, gl: options.gl, hl: options.hl, pws: "0", udm: ShortVideoSelectors.udm });
2279
2382
  if (uule) shortVidsParams.set("uule", uule);
2280
2383
  let shortVideos = [];
@@ -2321,6 +2424,7 @@ var PAAExtractor = class {
2321
2424
  diagnostics: {
2322
2425
  completionStatus: "paa_found",
2323
2426
  problem: null,
2427
+ completeness: { ...this.completeness },
2324
2428
  ...diagnosticWarnings.length > 0 ? { warnings: diagnosticWarnings } : {},
2325
2429
  ...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence) } : {}
2326
2430
  },