mcp-scraper 0.32.0 → 0.32.2
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 +1 -1
- package/dist/bin/api-server.cjs +61 -34
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +3 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +3 -3
- 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 -1
- 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 +4 -2
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -2
- package/dist/{chunk-RDCCU7CQ.js → chunk-AEMABLEL.js} +5 -4
- package/dist/chunk-AEMABLEL.js.map +1 -0
- package/dist/{chunk-55M5CE5L.js → chunk-MKZIL4EA.js} +2 -2
- package/dist/{chunk-PTHY7NYD.js → chunk-V73MPRU6.js} +3 -1
- package/dist/chunk-V73MPRU6.js.map +1 -0
- package/dist/{chunk-YOOEAXWP.js → chunk-XEED325T.js} +3 -3
- package/dist/chunk-XLO5CZ6T.js +7 -0
- package/dist/chunk-XLO5CZ6T.js.map +1 -0
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{server-OBOTNFD6.js → server-LTZDJHFG.js} +63 -38
- package/dist/{server-OBOTNFD6.js.map → server-LTZDJHFG.js.map} +1 -1
- package/dist/{worker-7DJYUACV.js → worker-ZZCZNY3R.js} +3 -3
- package/docs/mcp-tool-manifest.generated.json +2 -2
- package/package.json +1 -1
- package/dist/chunk-CPQF3PYW.js +0 -7
- package/dist/chunk-CPQF3PYW.js.map +0 -1
- package/dist/chunk-PTHY7NYD.js.map +0 -1
- package/dist/chunk-RDCCU7CQ.js.map +0 -1
- /package/dist/{chunk-55M5CE5L.js.map → chunk-MKZIL4EA.js.map} +0 -0
- /package/dist/{chunk-YOOEAXWP.js.map → chunk-XEED325T.js.map} +0 -0
- /package/dist/{worker-7DJYUACV.js.map → worker-ZZCZNY3R.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
harvest
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AEMABLEL.js";
|
|
4
4
|
import {
|
|
5
5
|
recordVendorUsage
|
|
6
6
|
} from "./chunk-MTSBI7ZH.js";
|
|
7
7
|
import "./chunk-K443GQY5.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-V73MPRU6.js";
|
|
9
9
|
import "./chunk-M2S27J6Z.js";
|
|
10
10
|
import "./chunk-ICT7DDHL.js";
|
|
11
11
|
|
|
@@ -15,8 +15,9 @@ import {
|
|
|
15
15
|
encodeUule,
|
|
16
16
|
harvest,
|
|
17
17
|
normalizeLocation,
|
|
18
|
-
resolveKernelProxyId
|
|
19
|
-
|
|
18
|
+
resolveKernelProxyId,
|
|
19
|
+
serpArrivalUrl
|
|
20
|
+
} from "./chunk-AEMABLEL.js";
|
|
20
21
|
import {
|
|
21
22
|
HttpMcpToolExecutor,
|
|
22
23
|
assessTranscriptSignal,
|
|
@@ -36,7 +37,7 @@ import {
|
|
|
36
37
|
sanitizeAttempts,
|
|
37
38
|
sanitizeHarvestResult,
|
|
38
39
|
transcribeMediaUrl
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-XEED325T.js";
|
|
40
41
|
import {
|
|
41
42
|
auditImages,
|
|
42
43
|
buildLinkReport,
|
|
@@ -65,7 +66,7 @@ import {
|
|
|
65
66
|
workflowOutputBaseDir,
|
|
66
67
|
workflowStepCount,
|
|
67
68
|
workflowSupportsSteps
|
|
68
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-MKZIL4EA.js";
|
|
69
70
|
import "./chunk-K443GQY5.js";
|
|
70
71
|
import {
|
|
71
72
|
DEFAULT_MAPS_PROXY_MODE,
|
|
@@ -76,8 +77,8 @@ import {
|
|
|
76
77
|
RawMapsHoursRowSchema,
|
|
77
78
|
RawMapsOverviewSchema,
|
|
78
79
|
RawMapsReviewStatsSchema
|
|
79
|
-
} from "./chunk-
|
|
80
|
-
import "./chunk-
|
|
80
|
+
} from "./chunk-V73MPRU6.js";
|
|
81
|
+
import "./chunk-XLO5CZ6T.js";
|
|
81
82
|
import {
|
|
82
83
|
completeExtractJob,
|
|
83
84
|
countSuccessfulPages,
|
|
@@ -13083,6 +13084,7 @@ import { Hono as Hono11 } from "hono";
|
|
|
13083
13084
|
// src/extractor/MapsSearchExtractor.ts
|
|
13084
13085
|
var LOCAL_RESULTS_PAGE_LIMIT = 8;
|
|
13085
13086
|
var LOCAL_RESULTS_WAIT_MS = 1e3;
|
|
13087
|
+
var MAPS_REDIRECT_BASE = "https://serp-redirector.vercel.app";
|
|
13086
13088
|
function buildGoogleLocalResultsUrl(input) {
|
|
13087
13089
|
const query = [input.query, input.location].filter(Boolean).join(" ");
|
|
13088
13090
|
const params = new URLSearchParams({ q: query, udm: "1", gl: input.gl, hl: input.hl, pws: "0" });
|
|
@@ -13104,21 +13106,23 @@ var MapsSearchExtractor = class {
|
|
|
13104
13106
|
const startMs = Date.now();
|
|
13105
13107
|
const searchQuery = [options.query, options.location].filter(Boolean).join(" ");
|
|
13106
13108
|
const searchUrl = buildGoogleOrganicSearchUrl(options);
|
|
13109
|
+
const keepDefaultProxy = !options.forceDirectEgress && options.proxyMode !== "location" && options.proxyMode !== "configured" && !options.kernelProxyId;
|
|
13107
13110
|
const config = {
|
|
13108
13111
|
headless: options.headless,
|
|
13109
13112
|
kernelApiKey: options.kernelApiKey,
|
|
13110
13113
|
kernelProxyId: options.kernelProxyId,
|
|
13111
13114
|
kernelProxyResolution: options.kernelProxyResolution,
|
|
13112
13115
|
proxyMode: options.proxyMode,
|
|
13113
|
-
keepDefaultProxy
|
|
13116
|
+
keepDefaultProxy,
|
|
13114
13117
|
viewport: { width: 1280, height: 900 },
|
|
13115
13118
|
locale: `${options.hl}-${options.gl.toUpperCase()}`,
|
|
13116
13119
|
debug: options.debug
|
|
13117
13120
|
};
|
|
13121
|
+
const navUrl = options.serpRedirect ? serpArrivalUrl(searchUrl, process.env.SERP_REDIRECT_BASE?.trim() || MAPS_REDIRECT_BASE) : searchUrl;
|
|
13118
13122
|
try {
|
|
13119
13123
|
await this.driver.launch(config);
|
|
13120
13124
|
const page = this.driver.getPage();
|
|
13121
|
-
await page.goto(
|
|
13125
|
+
await page.goto(navUrl, { waitUntil: "domcontentloaded", timeout: 6e4 });
|
|
13122
13126
|
await page.waitForTimeout(LOCAL_RESULTS_WAIT_MS);
|
|
13123
13127
|
if (await this.detectBlock(page)) throw new CaptchaError(RECAPTCHA_INSTRUCTIONS);
|
|
13124
13128
|
const reachedFinder = await this.clickIntoLocalFinder(page);
|
|
@@ -13205,29 +13209,38 @@ var MapsSearchExtractor = class {
|
|
|
13205
13209
|
};
|
|
13206
13210
|
const phonePattern = /(?:\+?1[\s.-]?)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}/;
|
|
13207
13211
|
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;
|
|
13208
|
-
const
|
|
13209
|
-
const
|
|
13210
|
-
return Boolean(
|
|
13212
|
+
const headings = Array.from(document.querySelectorAll('div[role="heading"]')).filter((heading) => {
|
|
13213
|
+
const text2 = normalize(heading.textContent);
|
|
13214
|
+
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 ?? "");
|
|
13211
13215
|
});
|
|
13212
13216
|
const seen = /* @__PURE__ */ new Set();
|
|
13213
13217
|
const out = [];
|
|
13214
|
-
|
|
13215
|
-
|
|
13218
|
+
let cardIndex = -1;
|
|
13219
|
+
for (const heading of headings) {
|
|
13220
|
+
let card = heading;
|
|
13221
|
+
for (let hop = 0; hop < 8; hop++) {
|
|
13222
|
+
const parent = card.parentElement;
|
|
13223
|
+
if (!parent || parent.querySelectorAll('div[role="heading"]').length > 1) break;
|
|
13224
|
+
card = parent;
|
|
13225
|
+
}
|
|
13226
|
+
if (card.closest('[role="dialog"], dialog')) continue;
|
|
13227
|
+
const name = normalize(heading.textContent);
|
|
13216
13228
|
if (!name) continue;
|
|
13217
|
-
const
|
|
13218
|
-
|
|
13229
|
+
const lines = (card.innerText ?? "").split("\n").map((line) => normalize(line)).filter((line) => Boolean(line));
|
|
13230
|
+
if (/^sponsored$/i.test(lines[0] ?? "")) continue;
|
|
13231
|
+
const ratingLine = lines.find((line) => /\d(?:\.\d)?\s*\(/.test(line)) ?? "";
|
|
13232
|
+
const rating = ratingLine.match(/(\d(?:\.\d)?)\s*\(/)?.[1] ?? null;
|
|
13233
|
+
const reviewCount = ratingLine.match(/\((\d[\d,]*)\)/)?.[1]?.replace(/,/g, "") ?? null;
|
|
13234
|
+
const category = normalize(ratingLine.split("\xB7").slice(1).join("\xB7"));
|
|
13235
|
+
const phone = lines.map((line) => line.match(phonePattern)?.[0]).find((value) => Boolean(value)) ?? null;
|
|
13236
|
+
const hoursStatus = normalize((lines.find((line) => /\b(?:open|closed|opens|closes)\b/i.test(line)) ?? "").split("\xB7")[0] ?? "");
|
|
13237
|
+
const address = lines.find((line) => addressPattern.test(line)) ?? null;
|
|
13238
|
+
const websiteUrl = Array.from(card.querySelectorAll("a[href]")).map((anchor) => anchor.href).find((href) => /^https?:/i.test(href) && !/google\.|gstatic|googleusercontent/.test(href)) ?? null;
|
|
13239
|
+
if (!rating && !(phone && websiteUrl)) continue;
|
|
13240
|
+
const key = `${name.toLowerCase()}|${lines.join(" ").toLowerCase()}`;
|
|
13219
13241
|
if (seen.has(key)) continue;
|
|
13220
13242
|
seen.add(key);
|
|
13221
|
-
|
|
13222
|
-
const aria = normalize(card.getAttribute("aria-label"));
|
|
13223
|
-
const rating = (aria ?? text2).match(/(?:rated\s+)?(\d(?:\.\d)?)\s*(?:out of 5)?/i)?.[1] ?? null;
|
|
13224
|
-
const reviewCount = (aria ?? text2).match(/(?:\(|·\s*)([\d,]+)\s+(?:user\s+)?reviews?\b/i)?.[1]?.replace(/,/g, "") ?? null;
|
|
13225
|
-
const phone = lines.find((line) => phonePattern.test(line)) ?? null;
|
|
13226
|
-
const address = lines.find((line) => addressPattern.test(line)) ?? null;
|
|
13227
|
-
const hoursStatus = lines.find((line) => /\b(?:open|closed|opens|closes)\b/i.test(line)) ?? null;
|
|
13228
|
-
const category = lines.find(
|
|
13229
|
-
(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)
|
|
13230
|
-
) ?? null;
|
|
13243
|
+
cardIndex += 1;
|
|
13231
13244
|
const mapsSearchUrl = `https://www.google.com/maps/search/${encodeURIComponent([name, locationHint].filter(Boolean).join(" "))}`;
|
|
13232
13245
|
out.push({
|
|
13233
13246
|
cardIndex,
|
|
@@ -13242,7 +13255,7 @@ var MapsSearchExtractor = class {
|
|
|
13242
13255
|
address,
|
|
13243
13256
|
phone,
|
|
13244
13257
|
hoursStatus,
|
|
13245
|
-
websiteUrl
|
|
13258
|
+
websiteUrl,
|
|
13246
13259
|
directionsUrl: `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent([name, address].filter(Boolean).join(", "))}`,
|
|
13247
13260
|
metadata: lines.slice(0, 20)
|
|
13248
13261
|
});
|
|
@@ -13251,17 +13264,27 @@ var MapsSearchExtractor = class {
|
|
|
13251
13264
|
}, fallbackLocation);
|
|
13252
13265
|
}
|
|
13253
13266
|
async openCardAndExtractDialog(page, card, includeServices) {
|
|
13267
|
+
if (!includeServices) return null;
|
|
13254
13268
|
const clicked = await page.evaluate((input) => {
|
|
13255
13269
|
const normalize = (value) => (value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
13256
|
-
const
|
|
13257
|
-
|
|
13258
|
-
|
|
13259
|
-
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
|
|
13270
|
+
const headings = Array.from(document.querySelectorAll('div[role="heading"]'));
|
|
13271
|
+
for (const heading of headings) {
|
|
13272
|
+
let target = heading;
|
|
13273
|
+
for (let hop = 0; hop < 8; hop++) {
|
|
13274
|
+
const parent = target.parentElement;
|
|
13275
|
+
if (!parent || parent.querySelectorAll('div[role="heading"]').length > 1) break;
|
|
13276
|
+
target = parent;
|
|
13277
|
+
}
|
|
13278
|
+
if (target.closest('[role="dialog"], dialog')) continue;
|
|
13279
|
+
const name = normalize(heading.textContent);
|
|
13280
|
+
if (!name) continue;
|
|
13281
|
+
const lines = (target.innerText ?? "").split("\n").map((line) => normalize(line)).filter(Boolean);
|
|
13282
|
+
if (`${name}|${lines.join(" ")}` !== input.cardKey) continue;
|
|
13283
|
+
target.click();
|
|
13284
|
+
return true;
|
|
13285
|
+
}
|
|
13286
|
+
return false;
|
|
13287
|
+
}, { cardKey: card.cardKey });
|
|
13265
13288
|
if (!clicked) return null;
|
|
13266
13289
|
const dialog = page.locator('dialog, [role="dialog"]').filter({ has: page.locator('h1, h2, [role="heading"]') }).last();
|
|
13267
13290
|
await dialog.waitFor({ state: "visible", timeout: 6e3 }).catch(() => {
|
|
@@ -13959,7 +13982,9 @@ async function runMapsSearchWithRotation(options) {
|
|
|
13959
13982
|
...options,
|
|
13960
13983
|
proxyZip,
|
|
13961
13984
|
kernelProxyId: options.proxyMode === "none" ? void 0 : resolution.kernelProxyId,
|
|
13962
|
-
kernelProxyResolution: resolution.resolution
|
|
13985
|
+
kernelProxyResolution: resolution.resolution,
|
|
13986
|
+
serpRedirect: options.proxyMode === "none" && attemptIndex >= 1 && attemptIndex < maxAttempts - 1,
|
|
13987
|
+
forceDirectEgress: options.proxyMode === "none" && attemptIndex >= 1
|
|
13963
13988
|
};
|
|
13964
13989
|
const isHeadfulEscalation = attemptIndex === maxAttempts - 1;
|
|
13965
13990
|
const baseCtx = currentCostContext();
|
|
@@ -27405,7 +27430,7 @@ app.get("/cron/tick", async (c) => {
|
|
|
27405
27430
|
if (!process.env.CRON_SECRET || secret2 !== `Bearer ${process.env.CRON_SECRET}`) {
|
|
27406
27431
|
return c.json({ error: "Unauthorized" }, 401);
|
|
27407
27432
|
}
|
|
27408
|
-
const { drainQueue } = await import("./worker-
|
|
27433
|
+
const { drainQueue } = await import("./worker-ZZCZNY3R.js");
|
|
27409
27434
|
const budget = { maxJobs: 10, deadlineMs: Date.now() + 28e4 };
|
|
27410
27435
|
const workflowDispatchResult = await dispatchDueWorkflowSchedules(`${new URL(c.req.url).protocol}//${new URL(c.req.url).host}`);
|
|
27411
27436
|
const [results, sweepResult, reapResult, expiredResult, blobCleanup, connectedDataArtifactCleanup] = await Promise.all([
|
|
@@ -27608,4 +27633,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
27608
27633
|
export {
|
|
27609
27634
|
app
|
|
27610
27635
|
};
|
|
27611
|
-
//# sourceMappingURL=server-
|
|
27636
|
+
//# sourceMappingURL=server-LTZDJHFG.js.map
|