mcp-scraper 0.32.0 → 0.32.1

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/README.md CHANGED
@@ -88,7 +88,7 @@ Build the branded one-click bundle:
88
88
  npm run build:mcpb
89
89
  ```
90
90
 
91
- The generated bundle is written to `build/mcpb/mcp-scraper-<version>.mcpb` and copied to `public/downloads/` for the hosted download. The current public bundle is `https://mcpscraper.dev/downloads/mcp-scraper.mcpb` (`0.32.0`, SHA-256 `f31500fa60ffa96d2bea43feb4f5606a40fdc59b599dd51a11b8a54e5c9bce32`). Install it by opening or dragging it into Claude Desktop. Claude displays the `MCP Scraper` install card, icon, and API-key configuration field from the bundle manifest.
91
+ The generated bundle is written to `build/mcpb/mcp-scraper-<version>.mcpb` and copied to `public/downloads/` for the hosted download. The current public bundle is `https://mcpscraper.dev/downloads/mcp-scraper.mcpb` (`0.32.1`, SHA-256 `a8b3ff927f58382f5e550cbe079a4276e448c6750858ec9712f2f945cd79ffa3`). Install it by opening or dragging it into Claude Desktop. Claude displays the `MCP Scraper` install card, icon, and API-key configuration field from the bundle manifest.
92
92
 
93
93
  The MCPB install exposes every tool — web-intelligence plus all `browser_*` tools — through the one `mcp-scraper` server.
94
94
 
@@ -19378,29 +19378,38 @@ var init_MapsSearchExtractor = __esm({
19378
19378
  };
19379
19379
  const phonePattern = /(?:\+?1[\s.-]?)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}/;
19380
19380
  const addressPattern = /\b[A-Z]{2}\s+\d{5}(?:-\d{4})?\b|\b(?:St|Street|Ave|Avenue|Rd|Road|Blvd|Boulevard|Dr|Drive|Ln|Lane|Way|Pkwy|Parkway)\b/i;
19381
- const cards = Array.from(document.querySelectorAll("button")).filter((button) => {
19382
- const name = normalize4(button.querySelector("h3")?.textContent);
19383
- return Boolean(name) && !button.closest('[role="dialog"], dialog') && !/\bsponsored\b/i.test(button.innerText);
19381
+ const headings = Array.from(document.querySelectorAll('div[role="heading"]')).filter((heading) => {
19382
+ const text2 = normalize4(heading.textContent);
19383
+ return Boolean(text2) && !/^(?:businesses|places|sponsored|people also ask|discussions and forums|more businesses|more places|custom date range|choose what you)/i.test(text2 ?? "");
19384
19384
  });
19385
19385
  const seen = /* @__PURE__ */ new Set();
19386
19386
  const out = [];
19387
- for (const [cardIndex, card] of cards.entries()) {
19388
- const name = normalize4(card.querySelector("h3")?.textContent);
19387
+ let cardIndex = -1;
19388
+ for (const heading of headings) {
19389
+ let card = heading;
19390
+ for (let hop = 0; hop < 8; hop++) {
19391
+ const parent = card.parentElement;
19392
+ if (!parent || parent.querySelectorAll('div[role="heading"]').length > 1) break;
19393
+ card = parent;
19394
+ }
19395
+ if (card.closest('[role="dialog"], dialog')) continue;
19396
+ const name = normalize4(heading.textContent);
19389
19397
  if (!name) continue;
19390
- const text2 = normalize4(card.innerText) ?? name;
19391
- const key = `${name.toLowerCase()}|${text2.toLowerCase()}`;
19398
+ const lines = (card.innerText ?? "").split("\n").map((line) => normalize4(line)).filter((line) => Boolean(line));
19399
+ if (/^sponsored$/i.test(lines[0] ?? "")) continue;
19400
+ const ratingLine = lines.find((line) => /\d(?:\.\d)?\s*\(/.test(line)) ?? "";
19401
+ const rating = ratingLine.match(/(\d(?:\.\d)?)\s*\(/)?.[1] ?? null;
19402
+ const reviewCount = ratingLine.match(/\((\d[\d,]*)\)/)?.[1]?.replace(/,/g, "") ?? null;
19403
+ const category = normalize4(ratingLine.split("\xB7").slice(1).join("\xB7"));
19404
+ const phone = lines.map((line) => line.match(phonePattern)?.[0]).find((value) => Boolean(value)) ?? null;
19405
+ const hoursStatus = normalize4((lines.find((line) => /\b(?:open|closed|opens|closes)\b/i.test(line)) ?? "").split("\xB7")[0] ?? "");
19406
+ const address = lines.find((line) => addressPattern.test(line)) ?? null;
19407
+ const websiteUrl = Array.from(card.querySelectorAll("a[href]")).map((anchor) => anchor.href).find((href) => /^https?:/i.test(href) && !/google\.|gstatic|googleusercontent/.test(href)) ?? null;
19408
+ if (!rating && !(phone && websiteUrl)) continue;
19409
+ const key = `${name.toLowerCase()}|${lines.join(" ").toLowerCase()}`;
19392
19410
  if (seen.has(key)) continue;
19393
19411
  seen.add(key);
19394
- const lines = text2.split(/\n+/).map((line) => normalize4(line)).filter((line) => Boolean(line));
19395
- const aria = normalize4(card.getAttribute("aria-label"));
19396
- const rating = (aria ?? text2).match(/(?:rated\s+)?(\d(?:\.\d)?)\s*(?:out of 5)?/i)?.[1] ?? null;
19397
- const reviewCount = (aria ?? text2).match(/(?:\(|·\s*)([\d,]+)\s+(?:user\s+)?reviews?\b/i)?.[1]?.replace(/,/g, "") ?? null;
19398
- const phone = lines.find((line) => phonePattern.test(line)) ?? null;
19399
- const address = lines.find((line) => addressPattern.test(line)) ?? null;
19400
- const hoursStatus = lines.find((line) => /\b(?:open|closed|opens|closes)\b/i.test(line)) ?? null;
19401
- const category = lines.find(
19402
- (line) => line !== name && line !== phone && line !== address && line !== hoursStatus && !/^(?:rated|\d[\d.,]*\s*(?:out of 5|reviews?)?)\b/i.test(line) && !/sponsored|website|directions|call|save|share|more/i.test(line)
19403
- ) ?? null;
19412
+ cardIndex += 1;
19404
19413
  const mapsSearchUrl = `https://www.google.com/maps/search/${encodeURIComponent([name, locationHint].filter(Boolean).join(" "))}`;
19405
19414
  out.push({
19406
19415
  cardIndex,
@@ -19415,7 +19424,7 @@ var init_MapsSearchExtractor = __esm({
19415
19424
  address,
19416
19425
  phone,
19417
19426
  hoursStatus,
19418
- websiteUrl: null,
19427
+ websiteUrl,
19419
19428
  directionsUrl: `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent([name, address].filter(Boolean).join(", "))}`,
19420
19429
  metadata: lines.slice(0, 20)
19421
19430
  });
@@ -19424,17 +19433,27 @@ var init_MapsSearchExtractor = __esm({
19424
19433
  }, fallbackLocation);
19425
19434
  }
19426
19435
  async openCardAndExtractDialog(page, card, includeServices) {
19436
+ if (!includeServices) return null;
19427
19437
  const clicked = await page.evaluate((input) => {
19428
19438
  const normalize4 = (value) => (value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
19429
- const cards = Array.from(document.querySelectorAll("button")).filter(
19430
- (button) => Boolean(button.querySelector("h3")) && !button.closest('[role="dialog"], dialog') && !/\bsponsored\b/i.test(button.innerText)
19431
- );
19432
- const exact = cards.find((button) => `${normalize4(button.querySelector("h3")?.textContent)}|${normalize4(button.innerText)}` === input.cardKey);
19433
- const target = exact ?? cards[input.cardIndex];
19434
- if (!target) return false;
19435
- target.click();
19436
- return true;
19437
- }, { cardIndex: card.cardIndex, cardKey: card.cardKey });
19439
+ const headings = Array.from(document.querySelectorAll('div[role="heading"]'));
19440
+ for (const heading of headings) {
19441
+ let target = heading;
19442
+ for (let hop = 0; hop < 8; hop++) {
19443
+ const parent = target.parentElement;
19444
+ if (!parent || parent.querySelectorAll('div[role="heading"]').length > 1) break;
19445
+ target = parent;
19446
+ }
19447
+ if (target.closest('[role="dialog"], dialog')) continue;
19448
+ const name = normalize4(heading.textContent);
19449
+ if (!name) continue;
19450
+ const lines = (target.innerText ?? "").split("\n").map((line) => normalize4(line)).filter(Boolean);
19451
+ if (`${name}|${lines.join(" ")}` !== input.cardKey) continue;
19452
+ target.click();
19453
+ return true;
19454
+ }
19455
+ return false;
19456
+ }, { cardKey: card.cardKey });
19438
19457
  if (!clicked) return null;
19439
19458
  const dialog = page.locator('dialog, [role="dialog"]').filter({ has: page.locator('h1, h2, [role="heading"]') }).last();
19440
19459
  await dialog.waitFor({ state: "visible", timeout: 6e3 }).catch(() => {
@@ -29767,7 +29786,7 @@ var PACKAGE_VERSION;
29767
29786
  var init_version = __esm({
29768
29787
  "src/version.ts"() {
29769
29788
  "use strict";
29770
- PACKAGE_VERSION = "0.32.0";
29789
+ PACKAGE_VERSION = "0.32.1";
29771
29790
  }
29772
29791
  });
29773
29792