mcp-scraper 0.3.27 → 0.3.40

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 (45) hide show
  1. package/dist/bin/api-server.cjs +2475 -1154
  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 +346 -7
  11. package/dist/bin/mcp-stdio-server.cjs.map +1 -1
  12. package/dist/bin/mcp-stdio-server.js +7 -4
  13. package/dist/bin/mcp-stdio-server.js.map +1 -1
  14. package/dist/bin/paa-harvest.cjs +109 -5
  15. package/dist/bin/paa-harvest.cjs.map +1 -1
  16. package/dist/bin/paa-harvest.js +2 -2
  17. package/dist/{chunk-TRNSXR46.js → chunk-DVRPXPYH.js} +22 -3
  18. package/dist/chunk-DVRPXPYH.js.map +1 -0
  19. package/dist/{chunk-QSSH4RCX.js → chunk-GMTS35L6.js} +111 -7
  20. package/dist/chunk-GMTS35L6.js.map +1 -0
  21. package/dist/chunk-I5EFBTYE.js +7 -0
  22. package/dist/chunk-I5EFBTYE.js.map +1 -0
  23. package/dist/{chunk-H74L743B.js → chunk-KJWNVQFL.js} +39 -2
  24. package/dist/chunk-KJWNVQFL.js.map +1 -0
  25. package/dist/{chunk-6KHGYJIT.js → chunk-QLQYNE7E.js} +255 -14
  26. package/dist/chunk-QLQYNE7E.js.map +1 -0
  27. package/dist/{db-FP2ABUU4.js → db-LYQENFPW.js} +2 -2
  28. package/dist/index.cjs +109 -5
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +8 -0
  31. package/dist/index.d.ts +8 -0
  32. package/dist/index.js +2 -2
  33. package/dist/{server-KUNOK47B.js → server-HB7WJO3N.js} +1155 -274
  34. package/dist/server-HB7WJO3N.js.map +1 -0
  35. package/dist/{worker-NV4GTIRG.js → worker-TL4J65DE.js} +4 -4
  36. package/package.json +1 -1
  37. package/dist/chunk-6KHGYJIT.js.map +0 -1
  38. package/dist/chunk-DYV72F6A.js +0 -7
  39. package/dist/chunk-DYV72F6A.js.map +0 -1
  40. package/dist/chunk-H74L743B.js.map +0 -1
  41. package/dist/chunk-QSSH4RCX.js.map +0 -1
  42. package/dist/chunk-TRNSXR46.js.map +0 -1
  43. package/dist/server-KUNOK47B.js.map +0 -1
  44. /package/dist/{db-FP2ABUU4.js.map → db-LYQENFPW.js.map} +0 -0
  45. /package/dist/{worker-NV4GTIRG.js.map → worker-TL4J65DE.js.map} +0 -0
@@ -97,7 +97,7 @@ import {
97
97
  stripeEventAlreadyProcessed,
98
98
  updateKpoJobState,
99
99
  verifyPassword
100
- } from "./chunk-TRNSXR46.js";
100
+ } from "./chunk-DVRPXPYH.js";
101
101
  export {
102
102
  CREDIT_LOT_TTL,
103
103
  SiteAuditJobRowSchema,
@@ -198,4 +198,4 @@ export {
198
198
  updateKpoJobState,
199
199
  verifyPassword
200
200
  };
201
- //# sourceMappingURL=db-FP2ABUU4.js.map
201
+ //# sourceMappingURL=db-LYQENFPW.js.map
package/dist/index.cjs CHANGED
@@ -1597,6 +1597,13 @@ var PAAExtractor = class {
1597
1597
  }
1598
1598
  driver;
1599
1599
  reporter;
1600
+ completeness = {
1601
+ paaWithoutAnswer: 0,
1602
+ paaWithoutSource: 0,
1603
+ paaAnswersRecovered: 0,
1604
+ aioShareCaptured: null
1605
+ };
1606
+ aioShareUrlEarly = null;
1600
1607
  normalizeQuestion(q) {
1601
1608
  return q.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
1602
1609
  }
@@ -1725,6 +1732,7 @@ var PAAExtractor = class {
1725
1732
  }
1726
1733
  }
1727
1734
  const itemMap = new Map((await this.extractVisibleItems(page)).map((i) => [i.question, i]));
1735
+ await this.fillIncompleteItems(page, orderedQs, itemMap, options);
1728
1736
  for (const q of orderedQs) {
1729
1737
  if (results.length >= options.maxQuestions) break;
1730
1738
  const key = this.normalizeQuestion(q);
@@ -1738,8 +1746,52 @@ var PAAExtractor = class {
1738
1746
  results.push(this.toFlatRow({ question: q, answer: void 0, sourceTitle: void 0, sourceSite: void 0, sourceCite: void 0 }, 1, null, options.query));
1739
1747
  }
1740
1748
  }
1749
+ this.completeness.paaWithoutAnswer = results.filter((r) => !r.answer).length;
1750
+ this.completeness.paaWithoutSource = results.filter((r) => !r.source_title && !r.source_site && !r.source_cite).length;
1741
1751
  return results;
1742
1752
  }
1753
+ async fillIncompleteItems(page, orderedQs, itemMap, options) {
1754
+ const kept = new Set(orderedQs.slice(0, options.maxQuestions));
1755
+ const missingBefore = new Set([...kept].filter((q) => !itemMap.get(q)?.answer));
1756
+ if (missingBefore.size === 0) return;
1757
+ for (let round = 0; round < 2; round++) {
1758
+ if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
1759
+ const stillMissing = [...missingBefore].filter((q) => !itemMap.get(q)?.answer);
1760
+ if (stillMissing.length === 0) break;
1761
+ const reExpanded = await this.reExpandCollapsedItems(page).catch(() => false);
1762
+ await page.waitForTimeout(reExpanded ? 900 : 500);
1763
+ let improved = false;
1764
+ for (const item of await this.extractVisibleItems(page)) {
1765
+ if (!kept.has(item.question)) continue;
1766
+ const existing = itemMap.get(item.question);
1767
+ if (!item.answer || existing?.answer) continue;
1768
+ itemMap.set(item.question, {
1769
+ ...item,
1770
+ sourceTitle: item.sourceTitle ?? existing?.sourceTitle,
1771
+ sourceSite: item.sourceSite ?? existing?.sourceSite,
1772
+ sourceCite: item.sourceCite ?? existing?.sourceCite
1773
+ });
1774
+ improved = true;
1775
+ }
1776
+ if (!improved) break;
1777
+ }
1778
+ this.completeness.paaAnswersRecovered = [...missingBefore].filter((q) => itemMap.get(q)?.answer).length;
1779
+ }
1780
+ async reExpandCollapsedItems(page) {
1781
+ const unexpandedSel = `${PAASelectors.item}:not(.${PAASelectors.expandedClass}) ${PAASelectors.clickTarget}`;
1782
+ const count = await page.locator(unexpandedSel).count().catch(() => 0);
1783
+ if (count === 0) return false;
1784
+ for (let i = 0; i < count; i++) {
1785
+ try {
1786
+ const btn = page.locator(unexpandedSel).first();
1787
+ await btn.scrollIntoViewIfNeeded();
1788
+ await btn.click({ force: true });
1789
+ await page.waitForTimeout(300);
1790
+ } catch {
1791
+ }
1792
+ }
1793
+ return true;
1794
+ }
1743
1795
  async extractVideos(page) {
1744
1796
  const vsels = VideoSelectors;
1745
1797
  return page.evaluate((sels) => {
@@ -2074,12 +2126,58 @@ var PAAExtractor = class {
2074
2126
  }
2075
2127
  return { ...entityIds, entities: records, cids: [...cidSet] };
2076
2128
  }
2077
- async extractAISurfaces(page) {
2078
- return page.evaluate(extractAISurfacesFromDocument, {
2129
+ async extractAISurfaces(page, options) {
2130
+ const surfaces = await page.evaluate(extractAISurfacesFromDocument, {
2079
2131
  aio: AIOverviewSelectors,
2080
2132
  aim: AIModeSelectors,
2081
2133
  expandWaitMs: 1500
2082
2134
  });
2135
+ if (!surfaces.aiOverview.detected) {
2136
+ this.completeness.aioShareCaptured = null;
2137
+ return surfaces;
2138
+ }
2139
+ const shareUrl = this.aioShareUrlEarly ?? await this.captureAIOverviewShareUrl(page, options).catch(() => null);
2140
+ this.completeness.aioShareCaptured = shareUrl !== null;
2141
+ return {
2142
+ ...surfaces,
2143
+ aiOverview: { ...surfaces.aiOverview, shareUrl }
2144
+ };
2145
+ }
2146
+ async captureAIOverviewShareUrl(page, options) {
2147
+ for (let attempt = 0; attempt < 3; attempt++) {
2148
+ if (options?.softDeadlineMs && Date.now() >= options.softDeadlineMs) return null;
2149
+ const clicked = await page.evaluate(() => {
2150
+ const heads = Array.from(document.querySelectorAll('h1,h2,h3,[role="heading"],div')).filter((h) => (h.textContent || "").trim() === "AI Overview");
2151
+ for (const head of heads) {
2152
+ let sec = head;
2153
+ for (let up = 0; up < 8 && sec; up++) {
2154
+ const btn = sec.querySelector('[aria-label="Share"]');
2155
+ if (btn) {
2156
+ btn.scrollIntoView({ block: "center" });
2157
+ btn.click();
2158
+ return true;
2159
+ }
2160
+ sec = sec.parentElement;
2161
+ }
2162
+ }
2163
+ return false;
2164
+ }).catch(() => false);
2165
+ if (!clicked) return null;
2166
+ await page.waitForTimeout(4500);
2167
+ const url = await page.evaluate(() => {
2168
+ const w = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
2169
+ while (w.nextNode()) {
2170
+ const m = (w.currentNode.textContent || "").match(/https:\/\/share\.google\/aimode\/\S+/);
2171
+ if (m) return m[0];
2172
+ }
2173
+ return null;
2174
+ }).catch(() => null);
2175
+ await page.keyboard.press("Escape").catch(() => {
2176
+ });
2177
+ if (url) return url;
2178
+ await page.waitForTimeout(600);
2179
+ }
2180
+ return null;
2083
2181
  }
2084
2182
  buildTree(flat, _seed) {
2085
2183
  const roots = [];
@@ -2132,6 +2230,8 @@ var PAAExtractor = class {
2132
2230
  }
2133
2231
  async extract(options, signal) {
2134
2232
  const startMs = Date.now();
2233
+ this.completeness = { paaWithoutAnswer: 0, paaWithoutSource: 0, paaAnswersRecovered: 0, aioShareCaptured: null };
2234
+ this.aioShareUrlEarly = null;
2135
2235
  const isMobile = options.device === "mobile";
2136
2236
  const config = {
2137
2237
  headless: options.headless,
@@ -2170,6 +2270,7 @@ var PAAExtractor = class {
2170
2270
  this.throwIfAborted(signal);
2171
2271
  const page = this.driver.getPage();
2172
2272
  await this.throwIfCaptcha(page, "Google SERP");
2273
+ this.aioShareUrlEarly = await this.captureAIOverviewShareUrl(page, options).catch(() => null);
2173
2274
  if (options.serpOnly) {
2174
2275
  const [organicResults2, localPack2, rawEntityIds2] = await Promise.all([
2175
2276
  this.extractOrganicResults(page),
@@ -2177,7 +2278,7 @@ var PAAExtractor = class {
2177
2278
  this.extractEntityIds(page)
2178
2279
  ]);
2179
2280
  const entityIds2 = this.mergeLocalPackIntoEntities(rawEntityIds2, localPack2);
2180
- const aiSurfaces2 = await this.extractAISurfaces(page);
2281
+ const aiSurfaces2 = await this.extractAISurfaces(page, options);
2181
2282
  let locationEvidence2 = options.debug ? inferSerpLocationEvidence(canonicalLocation, organicResults2, localPack2) : void 0;
2182
2283
  let allOrganic2 = organicResults2;
2183
2284
  if ((options.pages ?? 1) >= 2) {
@@ -2206,6 +2307,7 @@ var PAAExtractor = class {
2206
2307
  diagnostics: {
2207
2308
  completionStatus: "serp_only",
2208
2309
  problem: null,
2310
+ completeness: { ...this.completeness },
2209
2311
  ...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence2) } : {}
2210
2312
  },
2211
2313
  totalQuestions: 0,
@@ -2249,7 +2351,7 @@ var PAAExtractor = class {
2249
2351
  locationEvidence2 = inferSerpLocationEvidence(canonicalLocation, noPaaOrganic, localPack);
2250
2352
  }
2251
2353
  }
2252
- const aiSurfaces2 = await this.extractAISurfaces(page);
2354
+ const aiSurfaces2 = await this.extractAISurfaces(page, options);
2253
2355
  const stats2 = {
2254
2356
  seed: options.query,
2255
2357
  totalQuestions: 0,
@@ -2265,6 +2367,7 @@ var PAAExtractor = class {
2265
2367
  diagnostics: {
2266
2368
  completionStatus: "no_paa",
2267
2369
  problem: null,
2370
+ completeness: { ...this.completeness },
2268
2371
  ...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence2) } : {}
2269
2372
  },
2270
2373
  totalQuestions: 0,
@@ -2284,7 +2387,7 @@ var PAAExtractor = class {
2284
2387
  }
2285
2388
  const flat = await this.runBFS(page, options, signal);
2286
2389
  this.throwIfAborted(signal);
2287
- const aiSurfaces = await this.extractAISurfaces(page);
2390
+ const aiSurfaces = await this.extractAISurfaces(page, options);
2288
2391
  const shortVidsParams = new URLSearchParams({ q: options.query, gl: options.gl, hl: options.hl, pws: "0", udm: ShortVideoSelectors.udm });
2289
2392
  if (uule) shortVidsParams.set("uule", uule);
2290
2393
  let shortVideos = [];
@@ -2331,6 +2434,7 @@ var PAAExtractor = class {
2331
2434
  diagnostics: {
2332
2435
  completionStatus: "paa_found",
2333
2436
  problem: null,
2437
+ completeness: { ...this.completeness },
2334
2438
  ...diagnosticWarnings.length > 0 ? { warnings: diagnosticWarnings } : {},
2335
2439
  ...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence) } : {}
2336
2440
  },