@graphext/cuery 0.9.0 → 0.9.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/esm/mod.d.ts +4 -0
- package/esm/mod.d.ts.map +1 -1
- package/esm/mod.js +4 -0
- package/esm/src/apis/brightdata/index.d.ts +2 -0
- package/esm/src/apis/brightdata/index.d.ts.map +1 -0
- package/esm/src/apis/brightdata/index.js +1 -0
- package/esm/src/apis/brightdata/scrape.d.ts +30 -0
- package/esm/src/apis/brightdata/scrape.d.ts.map +1 -0
- package/esm/src/apis/brightdata/scrape.js +93 -0
- package/esm/src/apis/hasdata/aim.d.ts +4 -0
- package/esm/src/apis/hasdata/aim.d.ts.map +1 -0
- package/esm/src/apis/hasdata/aim.js +32 -0
- package/esm/src/apis/hasdata/aio.d.ts +4 -0
- package/esm/src/apis/hasdata/aio.d.ts.map +1 -0
- package/esm/src/apis/hasdata/aio.js +42 -0
- package/esm/src/apis/hasdata/helpers.d.ts +55 -0
- package/esm/src/apis/hasdata/helpers.d.ts.map +1 -0
- package/esm/src/apis/hasdata/helpers.js +182 -0
- package/esm/src/apis/hasdata/index.d.ts +6 -0
- package/esm/src/apis/hasdata/index.d.ts.map +1 -0
- package/esm/src/apis/hasdata/index.js +5 -0
- package/esm/src/apis/hasdata/scrape.d.ts +73 -0
- package/esm/src/apis/hasdata/scrape.d.ts.map +1 -0
- package/esm/src/apis/hasdata/scrape.js +310 -0
- package/esm/src/apis/hasdata/serp.d.ts +152 -0
- package/esm/src/apis/hasdata/serp.d.ts.map +1 -0
- package/esm/src/apis/hasdata/serp.js +133 -0
- package/esm/src/schemas/index.d.ts +6 -2
- package/esm/src/schemas/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/script/mod.d.ts +4 -0
- package/script/mod.d.ts.map +1 -1
- package/script/mod.js +4 -0
- package/script/src/apis/brightdata/index.d.ts +2 -0
- package/script/src/apis/brightdata/index.d.ts.map +1 -0
- package/script/src/apis/brightdata/index.js +17 -0
- package/script/src/apis/brightdata/scrape.d.ts +30 -0
- package/script/src/apis/brightdata/scrape.d.ts.map +1 -0
- package/script/src/apis/brightdata/scrape.js +130 -0
- package/script/src/apis/hasdata/aim.d.ts +4 -0
- package/script/src/apis/hasdata/aim.d.ts.map +1 -0
- package/script/src/apis/hasdata/aim.js +36 -0
- package/script/src/apis/hasdata/aio.d.ts +4 -0
- package/script/src/apis/hasdata/aio.d.ts.map +1 -0
- package/script/src/apis/hasdata/aio.js +46 -0
- package/script/src/apis/hasdata/helpers.d.ts +55 -0
- package/script/src/apis/hasdata/helpers.d.ts.map +1 -0
- package/script/src/apis/hasdata/helpers.js +222 -0
- package/script/src/apis/hasdata/index.d.ts +6 -0
- package/script/src/apis/hasdata/index.d.ts.map +1 -0
- package/script/src/apis/hasdata/index.js +21 -0
- package/script/src/apis/hasdata/scrape.d.ts +73 -0
- package/script/src/apis/hasdata/scrape.d.ts.map +1 -0
- package/script/src/apis/hasdata/scrape.js +352 -0
- package/script/src/apis/hasdata/serp.d.ts +152 -0
- package/script/src/apis/hasdata/serp.d.ts.map +1 -0
- package/script/src/apis/hasdata/serp.js +137 -0
- package/script/src/schemas/index.d.ts +6 -2
- package/script/src/schemas/index.d.ts.map +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
type ProxyType = 'datacenter' | 'residential';
|
|
2
|
+
type OutputFormat = 'markdown' | 'text' | 'html';
|
|
3
|
+
interface JSScenarioAction {
|
|
4
|
+
click?: string;
|
|
5
|
+
fill?: [string, string];
|
|
6
|
+
wait?: number;
|
|
7
|
+
waitFor?: string;
|
|
8
|
+
scroll?: string;
|
|
9
|
+
evaluate?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ScrapeOptions {
|
|
12
|
+
formats: Array<OutputFormat>;
|
|
13
|
+
proxyType?: ProxyType;
|
|
14
|
+
proxyCountry?: string;
|
|
15
|
+
extractLinks?: boolean;
|
|
16
|
+
wait?: number;
|
|
17
|
+
waitFor?: string;
|
|
18
|
+
blockResources?: boolean;
|
|
19
|
+
blockAds?: boolean;
|
|
20
|
+
blockUrls?: Array<string>;
|
|
21
|
+
jsRendering?: boolean;
|
|
22
|
+
jsScenario?: Array<JSScenarioAction>;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
export interface ScrapeResponse {
|
|
26
|
+
url?: string;
|
|
27
|
+
markdown?: string;
|
|
28
|
+
text?: string;
|
|
29
|
+
html?: string;
|
|
30
|
+
links?: Array<string>;
|
|
31
|
+
}
|
|
32
|
+
export interface BatchJobResponse {
|
|
33
|
+
jobId: string;
|
|
34
|
+
status: string;
|
|
35
|
+
}
|
|
36
|
+
export interface BatchJobStatus {
|
|
37
|
+
jobId: string;
|
|
38
|
+
status: string;
|
|
39
|
+
data: {
|
|
40
|
+
status: string;
|
|
41
|
+
requestsCount: number;
|
|
42
|
+
responsesCount: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* In batch jobs, results are only links to json files containing the actual scrape results.
|
|
47
|
+
*/
|
|
48
|
+
export interface BatchResultItem {
|
|
49
|
+
query: Record<string, unknown>;
|
|
50
|
+
result: {
|
|
51
|
+
id: string;
|
|
52
|
+
status: string;
|
|
53
|
+
json?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export interface BatchResults {
|
|
57
|
+
page: number;
|
|
58
|
+
limit: number;
|
|
59
|
+
total: number;
|
|
60
|
+
results: Array<BatchResultItem>;
|
|
61
|
+
}
|
|
62
|
+
export declare function scrapeWeb(url: string, options: ScrapeOptions): Promise<ScrapeResponse>;
|
|
63
|
+
export declare function scrapeWebBatch(urls: Array<string>, options: ScrapeOptions, maxConcurrency?: number): Promise<Array<ScrapeResponse>>;
|
|
64
|
+
/** Submit a batch scrape job to HasData API.
|
|
65
|
+
* IMPORTANT: results are not returned in original order! You need to match them by jobId and query.url.
|
|
66
|
+
*/
|
|
67
|
+
export declare function submitBatchScrapeJob(urls: Array<string>, options: ScrapeOptions): Promise<BatchJobResponse>;
|
|
68
|
+
export declare function getBatchJobStatus(jobId: string): Promise<BatchJobStatus>;
|
|
69
|
+
export declare function waitForBatchCompletion(jobId: string, pollInterval?: number, maxWaitTime?: number): Promise<BatchJobStatus>;
|
|
70
|
+
export declare function getBatchJobPage(jobId: string, page?: number, limit?: number): Promise<BatchResults>;
|
|
71
|
+
export declare function runBatchScrape(urls: Array<string>, options: ScrapeOptions, pageSize?: number, pollInterval?: number, maxWaitTime?: number): Promise<Array<ScrapeResponse>>;
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=scrape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrape.d.ts","sourceRoot":"","sources":["../../../../src/src/apis/hasdata/scrape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scrapeWeb = scrapeWeb;
|
|
37
|
+
exports.scrapeWebBatch = scrapeWebBatch;
|
|
38
|
+
exports.submitBatchScrapeJob = submitBatchScrapeJob;
|
|
39
|
+
exports.getBatchJobStatus = getBatchJobStatus;
|
|
40
|
+
exports.waitForBatchCompletion = waitForBatchCompletion;
|
|
41
|
+
exports.getBatchJobPage = getBatchJobPage;
|
|
42
|
+
exports.runBatchScrape = runBatchScrape;
|
|
43
|
+
/* eslint no-console: ["warn", { allow: ["log", "warn", "error"] }] */
|
|
44
|
+
const dntShim = __importStar(require("../../../_dnt.shims.js"));
|
|
45
|
+
const async_js_1 = require("../../helpers/async.js");
|
|
46
|
+
const HASDATA_CONCURRENCY = 29;
|
|
47
|
+
const HASDATA_RETRY_CONFIG = {
|
|
48
|
+
maxRetries: 3,
|
|
49
|
+
initialDelay: 1000,
|
|
50
|
+
maxDelay: 8000,
|
|
51
|
+
backoffMultiplier: 2,
|
|
52
|
+
statusCodes: [429, 500]
|
|
53
|
+
};
|
|
54
|
+
function cleanMarkdown(markdown, excludeImages = true) {
|
|
55
|
+
if (!markdown) {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
if (excludeImages) {
|
|
59
|
+
// Remove markdown images: 
|
|
60
|
+
markdown = markdown.replace(/!\[([^\]]*)\]\([^)]+\)/g, '');
|
|
61
|
+
// Remove standalone "Image" text between line breaks (from plain text format)
|
|
62
|
+
markdown = markdown.replace(/\n\s*Image\s*\n/g, '\n');
|
|
63
|
+
// Clean up multiple consecutive newlines
|
|
64
|
+
markdown = markdown.replace(/\n{3,}/g, '\n\n').trim();
|
|
65
|
+
}
|
|
66
|
+
markdown = markdown.replace(/\u00a0/g, ' ');
|
|
67
|
+
markdown = markdown.replace(/[ \t]+/g, ' ');
|
|
68
|
+
const lines = markdown.split('\n').map(line => line.trim());
|
|
69
|
+
const cleaned = [];
|
|
70
|
+
for (const line of lines) {
|
|
71
|
+
if (line || (cleaned.length > 0 && cleaned[cleaned.length - 1])) {
|
|
72
|
+
cleaned.push(line);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return cleaned.join('\n').trim();
|
|
76
|
+
}
|
|
77
|
+
async function fetchWithRetry(url, options, retryConfig = HASDATA_RETRY_CONFIG) {
|
|
78
|
+
const response = await (0, async_js_1.withRetries)(async () => fetch(url, {
|
|
79
|
+
...options,
|
|
80
|
+
signal: dntShim.dntGlobalThis.abortSignal
|
|
81
|
+
}), retryConfig);
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
const status = response.status;
|
|
84
|
+
let errorMessage;
|
|
85
|
+
if (status === 400) {
|
|
86
|
+
let details = '';
|
|
87
|
+
try {
|
|
88
|
+
const body = await response.text();
|
|
89
|
+
details = ` - ${body}`;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
}
|
|
93
|
+
errorMessage = `HasData API error (400): Bad Request${details}`;
|
|
94
|
+
}
|
|
95
|
+
else if (status === 401) {
|
|
96
|
+
errorMessage = 'HasData API error (401): Invalid API key';
|
|
97
|
+
}
|
|
98
|
+
else if (status === 403) {
|
|
99
|
+
errorMessage = 'HasData API error (403): API credits exhausted';
|
|
100
|
+
}
|
|
101
|
+
else if (status === 404) {
|
|
102
|
+
errorMessage = 'HasData API error (404): Resource not found';
|
|
103
|
+
}
|
|
104
|
+
else if (status === 422) {
|
|
105
|
+
let details = '';
|
|
106
|
+
try {
|
|
107
|
+
const body = await response.text();
|
|
108
|
+
details = ` - ${body}`;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
}
|
|
112
|
+
errorMessage = `HasData API error (422): Unprocessable Entity${details}`;
|
|
113
|
+
}
|
|
114
|
+
else if (status === 429) {
|
|
115
|
+
errorMessage = 'HasData API error (429): Rate limit exceeded';
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
errorMessage = `HasData API error: ${status} ${response.statusText}`;
|
|
119
|
+
}
|
|
120
|
+
console.error(errorMessage);
|
|
121
|
+
throw new Error(errorMessage);
|
|
122
|
+
}
|
|
123
|
+
return response;
|
|
124
|
+
}
|
|
125
|
+
function configureRequestBody(body, options) {
|
|
126
|
+
const formats = [...options.formats];
|
|
127
|
+
if (!formats.includes('json')) {
|
|
128
|
+
formats.push('json');
|
|
129
|
+
}
|
|
130
|
+
body.outputFormat = formats;
|
|
131
|
+
if (options.proxyType) {
|
|
132
|
+
body.proxyType = options.proxyType;
|
|
133
|
+
}
|
|
134
|
+
if (options.proxyCountry) {
|
|
135
|
+
body.proxyCountry = options.proxyCountry;
|
|
136
|
+
}
|
|
137
|
+
if (options.extractLinks != null) {
|
|
138
|
+
body.extractLinks = options.extractLinks;
|
|
139
|
+
}
|
|
140
|
+
if (options.wait != null) {
|
|
141
|
+
body.wait = options.wait;
|
|
142
|
+
}
|
|
143
|
+
if (options.waitFor) {
|
|
144
|
+
body.waitFor = options.waitFor;
|
|
145
|
+
}
|
|
146
|
+
if (options.blockResources != null) {
|
|
147
|
+
body.blockResources = options.blockResources;
|
|
148
|
+
}
|
|
149
|
+
if (options.blockAds != null) {
|
|
150
|
+
body.blockAds = options.blockAds;
|
|
151
|
+
}
|
|
152
|
+
if (options.blockUrls) {
|
|
153
|
+
body.blockUrls = options.blockUrls;
|
|
154
|
+
}
|
|
155
|
+
if (options.jsRendering != null) {
|
|
156
|
+
body.jsRendering = options.jsRendering;
|
|
157
|
+
}
|
|
158
|
+
if (options.jsScenario) {
|
|
159
|
+
body.jsScenario = options.jsScenario;
|
|
160
|
+
}
|
|
161
|
+
if (options.headers) {
|
|
162
|
+
body.headers = options.headers;
|
|
163
|
+
}
|
|
164
|
+
return body;
|
|
165
|
+
}
|
|
166
|
+
function getApiKey() {
|
|
167
|
+
const apiKey = dntShim.Deno.env.get('HASDATA_API_KEY');
|
|
168
|
+
if (!apiKey) {
|
|
169
|
+
throw new Error('HASDATA_API_KEY environment variable is required');
|
|
170
|
+
}
|
|
171
|
+
return apiKey;
|
|
172
|
+
}
|
|
173
|
+
async function scrapeWeb(url, options) {
|
|
174
|
+
const apiKey = getApiKey();
|
|
175
|
+
const endpoint = 'https://api.hasdata.com/scrape/web';
|
|
176
|
+
let requestBody = { url: url };
|
|
177
|
+
requestBody = configureRequestBody(requestBody, options);
|
|
178
|
+
try {
|
|
179
|
+
const response = await fetchWithRetry(endpoint, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: {
|
|
182
|
+
'Content-Type': 'application/json',
|
|
183
|
+
'x-api-key': apiKey
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify(requestBody)
|
|
186
|
+
});
|
|
187
|
+
const responseJson = await response.json();
|
|
188
|
+
const result = { url: url };
|
|
189
|
+
if (responseJson.markdown) {
|
|
190
|
+
result.markdown = cleanMarkdown(responseJson.markdown);
|
|
191
|
+
}
|
|
192
|
+
if (responseJson.text) {
|
|
193
|
+
result.text = responseJson.text;
|
|
194
|
+
}
|
|
195
|
+
if (responseJson.content) {
|
|
196
|
+
result.html = responseJson.content;
|
|
197
|
+
}
|
|
198
|
+
if (options.extractLinks && responseJson.links) {
|
|
199
|
+
result.links = responseJson.links;
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
console.error('HasData Web Scraping API error:', error);
|
|
205
|
+
return {}; // Return an empty object on error
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function scrapeWebBatch(urls, options, maxConcurrency = HASDATA_CONCURRENCY) {
|
|
209
|
+
return (0, async_js_1.mapParallel)(urls, maxConcurrency, async (url) => {
|
|
210
|
+
return await scrapeWeb(url, options);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
/** Submit a batch scrape job to HasData API.
|
|
214
|
+
* IMPORTANT: results are not returned in original order! You need to match them by jobId and query.url.
|
|
215
|
+
*/
|
|
216
|
+
async function submitBatchScrapeJob(urls, options) {
|
|
217
|
+
const apiKey = getApiKey();
|
|
218
|
+
const endpoint = 'https://api.hasdata.com/scrape/batch/web/';
|
|
219
|
+
const requestPayloads = urls.map((url) => {
|
|
220
|
+
let payload = { url: url };
|
|
221
|
+
payload = configureRequestBody(payload, options);
|
|
222
|
+
return payload;
|
|
223
|
+
});
|
|
224
|
+
const requestBody = { requests: requestPayloads };
|
|
225
|
+
try {
|
|
226
|
+
const response = await fetchWithRetry(endpoint, {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: {
|
|
229
|
+
'Content-Type': 'application/json',
|
|
230
|
+
'x-api-key': apiKey
|
|
231
|
+
},
|
|
232
|
+
body: JSON.stringify(requestBody)
|
|
233
|
+
});
|
|
234
|
+
return await response.json();
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console.error('HasData Batch Scrape submission error:', error);
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function getBatchJobStatus(jobId) {
|
|
242
|
+
const apiKey = getApiKey();
|
|
243
|
+
const endpoint = `https://api.hasdata.com/scrape/batch/web/${jobId}`;
|
|
244
|
+
try {
|
|
245
|
+
const response = await fetchWithRetry(endpoint, {
|
|
246
|
+
method: 'GET',
|
|
247
|
+
headers: {
|
|
248
|
+
'x-api-key': apiKey
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
const status = await response.json();
|
|
252
|
+
return status;
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
console.error('HasData Batch Job status error:', error);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function waitForBatchCompletion(jobId, pollInterval = 5000, maxWaitTime = 300000) {
|
|
260
|
+
const startTime = Date.now();
|
|
261
|
+
while (true) {
|
|
262
|
+
const status = await getBatchJobStatus(jobId);
|
|
263
|
+
const endStates = ['done', 'stopped', 'finished', 'failed'];
|
|
264
|
+
if (endStates.includes(status.data.status)) {
|
|
265
|
+
return status;
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
const total = status.data.requestsCount;
|
|
269
|
+
const completed = status.data.responsesCount;
|
|
270
|
+
console.log(`Batch job ${jobId} in progress: ${completed}/${total} completed.`);
|
|
271
|
+
}
|
|
272
|
+
const elapsed = Date.now() - startTime;
|
|
273
|
+
if (elapsed >= maxWaitTime) {
|
|
274
|
+
throw new Error(`Batch job ${jobId} did not complete within ${maxWaitTime}ms`);
|
|
275
|
+
}
|
|
276
|
+
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async function getBatchJobPage(jobId, page = 0, limit = 100) {
|
|
280
|
+
const apiKey = getApiKey();
|
|
281
|
+
const url = new URL(`https://api.hasdata.com/scrape/batch/web/${jobId}/results`);
|
|
282
|
+
url.searchParams.set('page', page.toString());
|
|
283
|
+
url.searchParams.set('limit', limit.toString());
|
|
284
|
+
console.log(`Fetching batch job results from: ${url.toString()}`);
|
|
285
|
+
try {
|
|
286
|
+
const response = await fetchWithRetry(url.toString(), {
|
|
287
|
+
method: 'GET',
|
|
288
|
+
headers: {
|
|
289
|
+
'x-api-key': apiKey
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
return await response.json();
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
console.error('HasData Batch Job results error:', error);
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function runBatchScrape(urls, options, pageSize = 100, pollInterval = 5000, maxWaitTime = 300000) {
|
|
300
|
+
const { jobId } = await submitBatchScrapeJob(urls, options);
|
|
301
|
+
const status = await waitForBatchCompletion(jobId, pollInterval, maxWaitTime);
|
|
302
|
+
if (status.data.status === 'done') {
|
|
303
|
+
console.log(`Batch job ${jobId} finished successfully.`);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
throw new Error(`Batch job failed with status:\n${JSON.stringify(status, null, 2)}`);
|
|
307
|
+
}
|
|
308
|
+
const aggregatedResults = [];
|
|
309
|
+
let currentPage = 0;
|
|
310
|
+
let hasMore = true;
|
|
311
|
+
while (hasMore) {
|
|
312
|
+
const pageResults = await getBatchJobPage(jobId, currentPage, pageSize);
|
|
313
|
+
console.log(`Fetched page ${pageResults.page} with ${pageResults.results.length} results.`);
|
|
314
|
+
const scrapeResponses = await (0, async_js_1.mapParallel)(pageResults.results, HASDATA_CONCURRENCY, async (item) => {
|
|
315
|
+
if (item.result.status === 'ok' && item.result.json) {
|
|
316
|
+
try {
|
|
317
|
+
const response = await fetchWithRetry(item.result.json, { method: 'GET' });
|
|
318
|
+
const fullResponse = await response.json();
|
|
319
|
+
const scrapeResponse = {
|
|
320
|
+
url: item.query.url
|
|
321
|
+
};
|
|
322
|
+
if (options.formats.includes('markdown') && fullResponse.markdown) {
|
|
323
|
+
scrapeResponse.markdown = cleanMarkdown(fullResponse.markdown);
|
|
324
|
+
}
|
|
325
|
+
if (options.formats.includes('text') && fullResponse.text) {
|
|
326
|
+
scrapeResponse.text = fullResponse.text;
|
|
327
|
+
}
|
|
328
|
+
if (options.formats.includes('html') && fullResponse.content) {
|
|
329
|
+
scrapeResponse.html = fullResponse.content;
|
|
330
|
+
}
|
|
331
|
+
if (options.extractLinks && fullResponse.links) {
|
|
332
|
+
scrapeResponse.links = fullResponse.links;
|
|
333
|
+
}
|
|
334
|
+
return scrapeResponse;
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
console.error(`Failed to fetch result for ${item.query.url}:`, error);
|
|
338
|
+
return {};
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return {};
|
|
342
|
+
});
|
|
343
|
+
aggregatedResults.push(...scrapeResponses);
|
|
344
|
+
if (pageResults.results.length < pageSize || (pageResults.page + 1) * pageResults.limit >= pageResults.total) {
|
|
345
|
+
hasMore = false;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
currentPage += 1;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return aggregatedResults;
|
|
352
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { type AIOverview, type AIOParsed } from './helpers.js';
|
|
2
|
+
type SerpSearchType = 'all' | 'images' | 'videos' | 'news' | 'shopping' | 'local';
|
|
3
|
+
export interface SerpRequestOptions {
|
|
4
|
+
location: string;
|
|
5
|
+
country: string;
|
|
6
|
+
language: string;
|
|
7
|
+
contentLanguage?: string;
|
|
8
|
+
domain?: string;
|
|
9
|
+
filters?: string | Array<string>;
|
|
10
|
+
safeSearch?: 'active' | 'off' | boolean;
|
|
11
|
+
filterResults?: boolean;
|
|
12
|
+
preventAutoCorrect?: boolean;
|
|
13
|
+
offset?: number;
|
|
14
|
+
resultsPerPage?: number;
|
|
15
|
+
type?: SerpSearchType;
|
|
16
|
+
device?: 'desktop' | 'mobile' | 'tablet';
|
|
17
|
+
placeId?: string;
|
|
18
|
+
lsig?: string;
|
|
19
|
+
entityId?: string;
|
|
20
|
+
encodedLocation?: string;
|
|
21
|
+
searchId?: string;
|
|
22
|
+
}
|
|
23
|
+
interface SerpInlineSiteLink {
|
|
24
|
+
title?: string;
|
|
25
|
+
link?: string;
|
|
26
|
+
}
|
|
27
|
+
interface SerpListSiteLink {
|
|
28
|
+
title?: string;
|
|
29
|
+
link?: string;
|
|
30
|
+
snippet?: string;
|
|
31
|
+
}
|
|
32
|
+
interface SerpRichSnippetTop {
|
|
33
|
+
extensions?: Array<string>;
|
|
34
|
+
detectedExtensions?: Record<string, string | number>;
|
|
35
|
+
}
|
|
36
|
+
interface SerpRichSnippet {
|
|
37
|
+
top?: SerpRichSnippetTop;
|
|
38
|
+
}
|
|
39
|
+
interface SerpSiteLinks {
|
|
40
|
+
inline?: Array<SerpInlineSiteLink>;
|
|
41
|
+
list?: Array<SerpListSiteLink>;
|
|
42
|
+
}
|
|
43
|
+
export interface SerpOrganicResult {
|
|
44
|
+
position?: number;
|
|
45
|
+
title?: string;
|
|
46
|
+
link?: string;
|
|
47
|
+
url?: string;
|
|
48
|
+
displayedLink?: string;
|
|
49
|
+
source?: string;
|
|
50
|
+
snippet?: string;
|
|
51
|
+
snippetHighlitedWords?: Array<string>;
|
|
52
|
+
images?: Array<string>;
|
|
53
|
+
richSnippet?: SerpRichSnippet;
|
|
54
|
+
sitelinks?: SerpSiteLinks;
|
|
55
|
+
}
|
|
56
|
+
export interface SerpRequestMetadata {
|
|
57
|
+
id?: string;
|
|
58
|
+
status?: string;
|
|
59
|
+
html?: string;
|
|
60
|
+
url?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface SerpSearchInformation {
|
|
63
|
+
totalResults?: string;
|
|
64
|
+
formattedTotalResults?: string;
|
|
65
|
+
timeTaken?: number;
|
|
66
|
+
searchTime?: number;
|
|
67
|
+
}
|
|
68
|
+
export interface SerpLocalPlace {
|
|
69
|
+
position?: number;
|
|
70
|
+
title?: string;
|
|
71
|
+
rating?: number;
|
|
72
|
+
reviews?: number;
|
|
73
|
+
reviewsOriginal?: string;
|
|
74
|
+
address?: string;
|
|
75
|
+
hours?: string;
|
|
76
|
+
placeId?: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SerpLocalResults {
|
|
80
|
+
places?: Array<SerpLocalPlace>;
|
|
81
|
+
moreLocationsLink?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface SerpRelatedSearch {
|
|
84
|
+
query?: string;
|
|
85
|
+
link?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface SerpRelatedQuestion {
|
|
88
|
+
question?: string;
|
|
89
|
+
snippet?: string;
|
|
90
|
+
link?: string;
|
|
91
|
+
title?: string;
|
|
92
|
+
displayedLink?: string;
|
|
93
|
+
date?: string;
|
|
94
|
+
list?: Array<string>;
|
|
95
|
+
table?: Array<Array<string>>;
|
|
96
|
+
aiOverview?: AIOverview;
|
|
97
|
+
}
|
|
98
|
+
export interface SerpPerspective {
|
|
99
|
+
index?: number;
|
|
100
|
+
author?: string;
|
|
101
|
+
source?: string;
|
|
102
|
+
duration?: string;
|
|
103
|
+
extensions?: Array<string>;
|
|
104
|
+
thumbnail?: string;
|
|
105
|
+
title?: string;
|
|
106
|
+
link?: string;
|
|
107
|
+
date?: string;
|
|
108
|
+
snippet?: string;
|
|
109
|
+
}
|
|
110
|
+
export interface SerpImmersiveProduct {
|
|
111
|
+
position?: number;
|
|
112
|
+
category?: string;
|
|
113
|
+
title?: string;
|
|
114
|
+
productId?: string;
|
|
115
|
+
productLink?: string;
|
|
116
|
+
price?: string;
|
|
117
|
+
extractedPrice?: number;
|
|
118
|
+
source?: string;
|
|
119
|
+
reviews?: number;
|
|
120
|
+
rating?: number;
|
|
121
|
+
delivery?: string;
|
|
122
|
+
extensions?: Array<string>;
|
|
123
|
+
thumbnail?: string;
|
|
124
|
+
}
|
|
125
|
+
export interface SerpPagination {
|
|
126
|
+
next?: string;
|
|
127
|
+
pages?: Array<Record<string, string>>;
|
|
128
|
+
}
|
|
129
|
+
export interface SerpResponse {
|
|
130
|
+
requestMetadata?: SerpRequestMetadata;
|
|
131
|
+
searchMetadata?: Record<string, unknown>;
|
|
132
|
+
searchParameters?: Record<string, unknown>;
|
|
133
|
+
searchInformation?: SerpSearchInformation;
|
|
134
|
+
organicResults?: Array<SerpOrganicResult>;
|
|
135
|
+
adsResults?: Array<Record<string, unknown>>;
|
|
136
|
+
localResults?: SerpLocalResults;
|
|
137
|
+
knowledgeGraph?: Record<string, unknown>;
|
|
138
|
+
relatedSearches?: Array<SerpRelatedSearch>;
|
|
139
|
+
topStories?: Array<Record<string, unknown>>;
|
|
140
|
+
peopleAlsoAsk?: Array<Record<string, unknown>>;
|
|
141
|
+
relatedQuestions?: Array<SerpRelatedQuestion>;
|
|
142
|
+
imagesResults?: Array<Record<string, unknown>>;
|
|
143
|
+
videosResults?: Array<Record<string, unknown>>;
|
|
144
|
+
perspectives?: Array<SerpPerspective>;
|
|
145
|
+
immersiveProducts?: Array<SerpImmersiveProduct>;
|
|
146
|
+
pagination?: SerpPagination;
|
|
147
|
+
aiOverview?: AIOParsed;
|
|
148
|
+
}
|
|
149
|
+
export declare function fetchSerp(query: string, options: SerpRequestOptions): Promise<SerpResponse>;
|
|
150
|
+
export declare function fetchSerpBatch(queries: Array<string>, options: SerpRequestOptions, maxConcurrency?: number): Promise<Array<SerpResponse>>;
|
|
151
|
+
export {};
|
|
152
|
+
//# sourceMappingURL=serp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serp.d.ts","sourceRoot":"","sources":["../../../../src/src/apis/hasdata/serp.ts"],"names":[],"mappings":"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"}
|