@crawlee/utils 4.0.0-beta.15 → 4.0.0-beta.151
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 +14 -14
- package/index.d.ts +6 -15
- package/index.js +5 -14
- package/internal.d.ts +9 -0
- package/internal.js +8 -0
- package/internals/blocked.d.ts +0 -1
- package/internals/blocked.js +0 -1
- package/internals/cheerio.d.ts +1 -3
- package/internals/cheerio.js +4 -5
- package/internals/extract-urls.d.ts +5 -1
- package/internals/extract-urls.js +23 -18
- package/internals/general.d.ts +0 -25
- package/internals/general.js +2 -110
- package/internals/iterables.d.ts +9 -70
- package/internals/iterables.js +40 -111
- package/internals/open_graph_parser.d.ts +2 -3
- package/internals/open_graph_parser.js +8 -9
- package/internals/robots.d.ts +38 -12
- package/internals/robots.js +72 -48
- package/internals/schemas.d.ts +114 -0
- package/internals/schemas.js +114 -0
- package/internals/sitemap.d.ts +69 -8
- package/internals/sitemap.js +239 -75
- package/internals/social.d.ts +1 -2
- package/internals/social.js +7 -5
- package/internals/url.d.ts +70 -2
- package/internals/url.js +120 -2
- package/internals/validation.d.ts +25 -0
- package/internals/validation.js +140 -0
- package/package.json +9 -8
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/internals/blocked.d.ts.map +0 -1
- package/internals/blocked.js.map +0 -1
- package/internals/cheerio.d.ts.map +0 -1
- package/internals/cheerio.js.map +0 -1
- package/internals/chunk.d.ts +0 -2
- package/internals/chunk.d.ts.map +0 -1
- package/internals/chunk.js +0 -40
- package/internals/chunk.js.map +0 -1
- package/internals/debug.d.ts +0 -32
- package/internals/debug.d.ts.map +0 -1
- package/internals/debug.js +0 -41
- package/internals/debug.js.map +0 -1
- package/internals/extract-urls.d.ts.map +0 -1
- package/internals/extract-urls.js.map +0 -1
- package/internals/general.d.ts.map +0 -1
- package/internals/general.js.map +0 -1
- package/internals/iterables.d.ts.map +0 -1
- package/internals/iterables.js.map +0 -1
- package/internals/open_graph_parser.d.ts.map +0 -1
- package/internals/open_graph_parser.js.map +0 -1
- package/internals/robots.d.ts.map +0 -1
- package/internals/robots.js.map +0 -1
- package/internals/sitemap.d.ts.map +0 -1
- package/internals/sitemap.js.map +0 -1
- package/internals/social.d.ts.map +0 -1
- package/internals/social.js.map +0 -1
- package/internals/system-info/cpu-info.d.ts +0 -64
- package/internals/system-info/cpu-info.d.ts.map +0 -1
- package/internals/system-info/cpu-info.js +0 -211
- package/internals/system-info/cpu-info.js.map +0 -1
- package/internals/system-info/memory-info.d.ts +0 -28
- package/internals/system-info/memory-info.d.ts.map +0 -1
- package/internals/system-info/memory-info.js +0 -118
- package/internals/system-info/memory-info.js.map +0 -1
- package/internals/system-info/ps-tree.d.ts +0 -18
- package/internals/system-info/ps-tree.d.ts.map +0 -1
- package/internals/system-info/ps-tree.js +0 -145
- package/internals/system-info/ps-tree.js.map +0 -1
- package/internals/typedefs.d.ts +0 -5
- package/internals/typedefs.d.ts.map +0 -1
- package/internals/typedefs.js +0 -9
- package/internals/typedefs.js.map +0 -1
- package/internals/url.d.ts.map +0 -1
- package/internals/url.js.map +0 -1
package/internals/iterables.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { inspect } from 'node:util';
|
|
2
1
|
/**
|
|
3
2
|
* Type guard that checks if a value is iterable (has Symbol.iterator).
|
|
4
3
|
* @internal
|
|
@@ -40,128 +39,58 @@ export function isAsyncIterable(value) {
|
|
|
40
39
|
}
|
|
41
40
|
return typeof Object(value)[Symbol.asyncIterator] === 'function';
|
|
42
41
|
}
|
|
42
|
+
// Source - https://stackoverflow.com/a/71288323
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
* @internal
|
|
46
|
-
*
|
|
47
|
-
* @yields Each item from the input iterable
|
|
44
|
+
* Merges multiple async iterables into a single async iterable, yielding values concurrently.
|
|
48
45
|
*
|
|
49
46
|
* **Example usage:**
|
|
50
47
|
* ```ts
|
|
51
|
-
* const
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* }
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
export async function* asyncifyIterable(iterable) {
|
|
58
|
-
yield* iterable;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Lazily splits the input async iterable into chunks of specified size.
|
|
62
|
-
* The last chunk may contain fewer items if the total number of items
|
|
63
|
-
* is not evenly divisible by the chunk size.
|
|
64
|
-
* @internal
|
|
65
|
-
*
|
|
66
|
-
* @yields Arrays of items, each containing up to chunkSize items
|
|
48
|
+
* const asyncIterable1 = async function* () {
|
|
49
|
+
* yield 1; yield 3; yield 5;
|
|
50
|
+
* };
|
|
67
51
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* const numbers = async function* () {
|
|
71
|
-
* for (let i = 1; i <= 10; i++) yield i;
|
|
52
|
+
* const asyncIterable2 = async function* () {
|
|
53
|
+
* yield 2; yield 4; yield 6;
|
|
72
54
|
* };
|
|
73
55
|
*
|
|
74
|
-
* for await (const
|
|
75
|
-
* console.log(
|
|
56
|
+
* for await (const value of mergeAsyncIterables(asyncIterable1(), asyncIterable2())) {
|
|
57
|
+
* console.log(value);
|
|
76
58
|
* }
|
|
77
59
|
* ```
|
|
78
60
|
*/
|
|
79
|
-
export async function*
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
61
|
+
export async function* mergeAsyncIterables(...iterables) {
|
|
62
|
+
const asyncIterators = iterables.map((iterable) => iterable[Symbol.asyncIterator]());
|
|
63
|
+
const results = [];
|
|
64
|
+
let count = asyncIterators.length;
|
|
65
|
+
const never = new Promise(() => { });
|
|
66
|
+
async function getNext(asyncIterator, index) {
|
|
67
|
+
const result = await asyncIterator.next();
|
|
68
|
+
return {
|
|
69
|
+
index,
|
|
70
|
+
result,
|
|
71
|
+
};
|
|
90
72
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Wraps an async iterable to provide peek functionality, allowing you to look at
|
|
97
|
-
* the next value without consuming it from the iterator.
|
|
98
|
-
* @internal
|
|
99
|
-
*
|
|
100
|
-
* @param iterable - The async iterable to make peekable
|
|
101
|
-
*
|
|
102
|
-
* **Example usage:**
|
|
103
|
-
* ```ts
|
|
104
|
-
* const numbers = async function* () {
|
|
105
|
-
* yield 1; yield 2; yield 3;
|
|
106
|
-
* };
|
|
107
|
-
*
|
|
108
|
-
* const peekable = peekableAsyncIterable(numbers());
|
|
109
|
-
* const iterator = peekable[Symbol.asyncIterator]();
|
|
110
|
-
*
|
|
111
|
-
* console.log(await iterator.peek()); // 1 (doesn't consume)
|
|
112
|
-
* console.log(await iterator.peek()); // 1 (still doesn't consume)
|
|
113
|
-
* console.log(await iterator.next()); // { value: 1, done: false } (now consumed)
|
|
114
|
-
* console.log(await iterator.peek()); // 2 (next value)
|
|
115
|
-
* ```
|
|
116
|
-
*/
|
|
117
|
-
export function peekableAsyncIterable(iterable) {
|
|
118
|
-
const iterator = asyncifyIterable(iterable)[Symbol.asyncIterator]();
|
|
119
|
-
let peekedValue;
|
|
120
|
-
let isExhausted = false;
|
|
121
|
-
const peekableIterator = {
|
|
122
|
-
async next() {
|
|
123
|
-
// If we have peeked a value, return it and clear the peek
|
|
124
|
-
if (peekedValue !== undefined) {
|
|
125
|
-
const result = peekedValue;
|
|
126
|
-
peekedValue = undefined;
|
|
127
|
-
if (result.done) {
|
|
128
|
-
isExhausted = true;
|
|
129
|
-
return { done: true, value: undefined };
|
|
130
|
-
}
|
|
131
|
-
return { done: false, value: result.value };
|
|
132
|
-
}
|
|
133
|
-
if (isExhausted) {
|
|
134
|
-
return { done: true, value: undefined };
|
|
135
|
-
}
|
|
136
|
-
const result = await iterator.next();
|
|
73
|
+
const nextPromises = asyncIterators.map(getNext);
|
|
74
|
+
try {
|
|
75
|
+
while (count) {
|
|
76
|
+
const { index, result } = await Promise.race(nextPromises);
|
|
137
77
|
if (result.done) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
},
|
|
142
|
-
async peek() {
|
|
143
|
-
if (peekedValue !== undefined) {
|
|
144
|
-
return peekedValue.done ? undefined : peekedValue.value;
|
|
78
|
+
nextPromises[index] = never;
|
|
79
|
+
results[index] = result.value;
|
|
80
|
+
count--;
|
|
145
81
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const result = await iterator.next();
|
|
150
|
-
peekedValue = { done: result.done ?? false, value: result.value };
|
|
151
|
-
if (result.done) {
|
|
152
|
-
isExhausted = true;
|
|
153
|
-
return undefined;
|
|
82
|
+
else {
|
|
83
|
+
nextPromises[index] = getNext(asyncIterators[index], index);
|
|
84
|
+
yield result.value;
|
|
154
85
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
for (const [index, iterator] of asyncIterators.entries()) {
|
|
90
|
+
// no await here - see https://github.com/tc39/proposal-async-iteration/issues/126
|
|
91
|
+
if (nextPromises[index] !== never && iterator.return != null)
|
|
92
|
+
void iterator.return();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
166
96
|
}
|
|
167
|
-
//# sourceMappingURL=iterables.js.map
|
|
@@ -14,7 +14,6 @@ type OpenGraphResult = string | string[] | Dictionary<string | Dictionary>;
|
|
|
14
14
|
* Currently existing properties are kept up to date.
|
|
15
15
|
* @returns Scraped OpenGraph properties as an object.
|
|
16
16
|
*/
|
|
17
|
-
export declare function parseOpenGraph(raw: string, additionalProperties?: OpenGraphProperty[]): Dictionary<OpenGraphResult
|
|
18
|
-
export declare function parseOpenGraph($: CheerioAPI, additionalProperties?: OpenGraphProperty[]): Dictionary<OpenGraphResult
|
|
17
|
+
export declare function parseOpenGraph(raw: string, additionalProperties?: OpenGraphProperty[]): Promise<Dictionary<OpenGraphResult>>;
|
|
18
|
+
export declare function parseOpenGraph($: CheerioAPI, additionalProperties?: OpenGraphProperty[]): Promise<Dictionary<OpenGraphResult>>;
|
|
19
19
|
export {};
|
|
20
|
-
//# sourceMappingURL=open_graph_parser.d.ts.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { load } from 'cheerio';
|
|
2
1
|
/**
|
|
3
2
|
* To be used with the spread operator. Ensures that the item is defined, and is not empty.
|
|
4
3
|
*
|
|
@@ -255,32 +254,32 @@ const OPEN_GRAPH_PROPERTIES = [
|
|
|
255
254
|
outputName: 'articleInfo',
|
|
256
255
|
children: [
|
|
257
256
|
{
|
|
258
|
-
name: '
|
|
257
|
+
name: 'article:published_time',
|
|
259
258
|
outputName: 'publishedTime',
|
|
260
259
|
children: [],
|
|
261
260
|
},
|
|
262
261
|
{
|
|
263
|
-
name: '
|
|
262
|
+
name: 'article:modified_time',
|
|
264
263
|
outputName: 'modifiedTime',
|
|
265
264
|
children: [],
|
|
266
265
|
},
|
|
267
266
|
{
|
|
268
|
-
name: '
|
|
267
|
+
name: 'article:expiration_time',
|
|
269
268
|
outputName: 'expirationTime',
|
|
270
269
|
children: [],
|
|
271
270
|
},
|
|
272
271
|
{
|
|
273
|
-
name: '
|
|
272
|
+
name: 'article:author',
|
|
274
273
|
outputName: 'author',
|
|
275
274
|
children: [],
|
|
276
275
|
},
|
|
277
276
|
{
|
|
278
|
-
name: '
|
|
277
|
+
name: 'article:section',
|
|
279
278
|
outputName: 'section',
|
|
280
279
|
children: [],
|
|
281
280
|
},
|
|
282
281
|
{
|
|
283
|
-
name: '
|
|
282
|
+
name: 'article:tag',
|
|
284
283
|
outputName: 'tag',
|
|
285
284
|
children: [],
|
|
286
285
|
},
|
|
@@ -366,7 +365,8 @@ const parseOpenGraphProperty = (property, $) => {
|
|
|
366
365
|
}, {}),
|
|
367
366
|
};
|
|
368
367
|
};
|
|
369
|
-
export function parseOpenGraph(item, additionalProperties) {
|
|
368
|
+
export async function parseOpenGraph(item, additionalProperties) {
|
|
369
|
+
const { load } = await import('cheerio');
|
|
370
370
|
const $ = typeof item === 'string' ? load(item) : item;
|
|
371
371
|
return [...(additionalProperties || []), ...OPEN_GRAPH_PROPERTIES].reduce((acc, curr) => {
|
|
372
372
|
return {
|
|
@@ -375,4 +375,3 @@ export function parseOpenGraph(item, additionalProperties) {
|
|
|
375
375
|
};
|
|
376
376
|
}, {});
|
|
377
377
|
}
|
|
378
|
-
//# sourceMappingURL=open_graph_parser.js.map
|
package/internals/robots.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
+
import type { BaseHttpClient } from '@crawlee/http-client';
|
|
2
|
+
import type { CrawleeLogger } from '@crawlee/types';
|
|
1
3
|
import { Sitemap } from './sitemap.js';
|
|
4
|
+
import { type EnqueueStrategy } from './url.js';
|
|
5
|
+
export interface RobotsTxtFileSitemapsOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Keep only sitemap URLs matching this strategy relative to the robots.txt host; non-`http(s)` schemes
|
|
8
|
+
* are always dropped. Pass `'all'` to disable host filtering.
|
|
9
|
+
* @default 'same-hostname'
|
|
10
|
+
*/
|
|
11
|
+
enqueueStrategy?: EnqueueStrategy | `${EnqueueStrategy}`;
|
|
12
|
+
}
|
|
2
13
|
/**
|
|
3
14
|
* Loads and queries information from a [robots.txt file](https://en.wikipedia.org/wiki/Robots.txt).
|
|
4
15
|
*
|
|
@@ -18,15 +29,23 @@ import { Sitemap } from './sitemap.js';
|
|
|
18
29
|
* ```
|
|
19
30
|
*/
|
|
20
31
|
export declare class RobotsTxtFile {
|
|
21
|
-
private
|
|
22
|
-
private proxyUrl?;
|
|
32
|
+
#private;
|
|
23
33
|
private constructor();
|
|
24
34
|
/**
|
|
25
35
|
* Determine the location of a robots.txt file for a URL and fetch it.
|
|
26
36
|
* @param url the URL to fetch robots.txt for
|
|
27
|
-
* @param [
|
|
37
|
+
* @param [options] additional options
|
|
38
|
+
* @param [options.signal] an AbortSignal to cancel the request
|
|
39
|
+
* @param [options.timeoutMillis] timeout in milliseconds for the request
|
|
40
|
+
* @param [options.proxyUrl] a proxy to be used for fetching the robots.txt file
|
|
28
41
|
*/
|
|
29
|
-
static find(url: string,
|
|
42
|
+
static find(url: string, options?: {
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
timeoutMillis?: number;
|
|
45
|
+
proxyUrl?: string;
|
|
46
|
+
httpClient?: BaseHttpClient;
|
|
47
|
+
logger?: CrawleeLogger;
|
|
48
|
+
}): Promise<RobotsTxtFile>;
|
|
30
49
|
/**
|
|
31
50
|
* Allows providing the URL and robots.txt content explicitly instead of loading it from the target site.
|
|
32
51
|
* @param url the URL for robots.txt file
|
|
@@ -34,7 +53,12 @@ export declare class RobotsTxtFile {
|
|
|
34
53
|
* @param [proxyUrl] a proxy to be used for fetching the robots.txt file
|
|
35
54
|
*/
|
|
36
55
|
static from(url: string, content: string, proxyUrl?: string): RobotsTxtFile;
|
|
37
|
-
|
|
56
|
+
private static load;
|
|
57
|
+
/**
|
|
58
|
+
* Get crawl delay for a given user agent.
|
|
59
|
+
* @param [userAgent] relevant user agent, default to `*`
|
|
60
|
+
*/
|
|
61
|
+
getCrawlDelay(userAgent?: string): number | undefined;
|
|
38
62
|
/**
|
|
39
63
|
* Check if a URL should be crawled by robots.
|
|
40
64
|
* @param url the URL to check against the rules in robots.txt
|
|
@@ -42,17 +66,19 @@ export declare class RobotsTxtFile {
|
|
|
42
66
|
*/
|
|
43
67
|
isAllowed(url: string, userAgent?: string): boolean;
|
|
44
68
|
/**
|
|
45
|
-
* Get URLs of sitemaps referenced in the robots file.
|
|
69
|
+
* Get URLs of sitemaps referenced in the robots file, filtered by `options.enqueueStrategy` relative to
|
|
70
|
+
* the robots.txt host (default `'same-hostname'`; pass `'all'` to disable). Non-`http(s)` schemes are
|
|
71
|
+
* always dropped.
|
|
46
72
|
*/
|
|
47
|
-
getSitemaps(): string[];
|
|
73
|
+
getSitemaps(options?: RobotsTxtFileSitemapsOptions): string[];
|
|
48
74
|
/**
|
|
49
|
-
* Parse all the sitemaps referenced in the robots file.
|
|
75
|
+
* Parse all the sitemaps referenced in the robots file. `options` are forwarded to `getSitemaps`
|
|
76
|
+
* and the sitemap parser.
|
|
50
77
|
*/
|
|
51
|
-
parseSitemaps(): Promise<Sitemap>;
|
|
78
|
+
parseSitemaps(options?: RobotsTxtFileSitemapsOptions): Promise<Sitemap>;
|
|
52
79
|
/**
|
|
53
80
|
* Get all URLs from all the sitemaps referenced in the robots file. A shorthand for `(await robots.parseSitemaps()).urls`.
|
|
81
|
+
* `options` are forwarded to `parseSitemaps`.
|
|
54
82
|
*/
|
|
55
|
-
parseUrlsFromSitemaps(): Promise<string[]>;
|
|
83
|
+
parseUrlsFromSitemaps(options?: RobotsTxtFileSitemapsOptions): Promise<string[]>;
|
|
56
84
|
}
|
|
57
|
-
export { RobotsTxtFile as RobotsFile };
|
|
58
|
-
//# sourceMappingURL=robots.d.ts.map
|
package/internals/robots.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FetchHttpClient } from '@crawlee/http-client';
|
|
2
2
|
import robotsParser from 'robots-parser';
|
|
3
3
|
import { Sitemap } from './sitemap.js';
|
|
4
|
-
|
|
4
|
+
import { filterUrl } from './url.js';
|
|
5
5
|
/**
|
|
6
6
|
* Loads and queries information from a [robots.txt file](https://en.wikipedia.org/wiki/Robots.txt).
|
|
7
7
|
*
|
|
@@ -21,22 +21,29 @@ let HTTPError;
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
export class RobotsTxtFile {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
#url;
|
|
25
|
+
#robots;
|
|
26
|
+
#proxyUrl;
|
|
27
|
+
#logger;
|
|
28
|
+
constructor(url, robots, proxyUrl, logger) {
|
|
29
|
+
this.#url = url;
|
|
30
|
+
this.#robots = robots;
|
|
31
|
+
this.#proxyUrl = proxyUrl;
|
|
32
|
+
this.#logger = logger;
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
31
35
|
* Determine the location of a robots.txt file for a URL and fetch it.
|
|
32
36
|
* @param url the URL to fetch robots.txt for
|
|
33
|
-
* @param [
|
|
37
|
+
* @param [options] additional options
|
|
38
|
+
* @param [options.signal] an AbortSignal to cancel the request
|
|
39
|
+
* @param [options.timeoutMillis] timeout in milliseconds for the request
|
|
40
|
+
* @param [options.proxyUrl] a proxy to be used for fetching the robots.txt file
|
|
34
41
|
*/
|
|
35
|
-
static async find(url,
|
|
42
|
+
static async find(url, options) {
|
|
36
43
|
const robotsTxtFileUrl = new URL(url);
|
|
37
44
|
robotsTxtFileUrl.pathname = '/robots.txt';
|
|
38
45
|
robotsTxtFileUrl.search = '';
|
|
39
|
-
return RobotsTxtFile.load(robotsTxtFileUrl.toString(),
|
|
46
|
+
return RobotsTxtFile.load(robotsTxtFileUrl.toString(), options);
|
|
40
47
|
}
|
|
41
48
|
/**
|
|
42
49
|
* Allows providing the URL and robots.txt content explicitly instead of loading it from the target site.
|
|
@@ -46,35 +53,40 @@ export class RobotsTxtFile {
|
|
|
46
53
|
*/
|
|
47
54
|
static from(url, content, proxyUrl) {
|
|
48
55
|
// @ts-ignore
|
|
49
|
-
return new RobotsTxtFile(robotsParser(url, content), proxyUrl);
|
|
56
|
+
return new RobotsTxtFile(url, robotsParser(url, content), proxyUrl);
|
|
50
57
|
}
|
|
51
|
-
static async load(url,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
responseType: 'text',
|
|
61
|
-
});
|
|
62
|
-
// @ts-ignore
|
|
63
|
-
return new RobotsTxtFile(robotsParser(url.toString(), response.body), proxyUrl);
|
|
58
|
+
static async load(url, options) {
|
|
59
|
+
const { proxyUrl, logger, httpClient = new FetchHttpClient() } = options || {};
|
|
60
|
+
const response = await httpClient.sendRequest(new Request(url, { method: 'GET' }), {
|
|
61
|
+
proxyUrl,
|
|
62
|
+
timeoutMillis: options?.timeoutMillis,
|
|
63
|
+
signal: options?.signal,
|
|
64
|
+
});
|
|
65
|
+
if (response.status < 200 || response.status >= 300) {
|
|
66
|
+
throw new Error(`Failed to load robots.txt from ${url}: HTTP ${response.status}`);
|
|
64
67
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
if (response.status === 404) {
|
|
69
|
+
return new RobotsTxtFile(url, {
|
|
70
|
+
isAllowed() {
|
|
71
|
+
return true;
|
|
72
|
+
},
|
|
73
|
+
getSitemaps() {
|
|
74
|
+
return [];
|
|
75
|
+
},
|
|
76
|
+
getCrawlDelay() {
|
|
77
|
+
return undefined;
|
|
78
|
+
},
|
|
79
|
+
}, proxyUrl, logger);
|
|
77
80
|
}
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
return new RobotsTxtFile(url, robotsParser(url.toString(), await response.text()), proxyUrl, logger);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get crawl delay for a given user agent.
|
|
86
|
+
* @param [userAgent] relevant user agent, default to `*`
|
|
87
|
+
*/
|
|
88
|
+
getCrawlDelay(userAgent = '*') {
|
|
89
|
+
return this.#robots.getCrawlDelay(userAgent);
|
|
78
90
|
}
|
|
79
91
|
/**
|
|
80
92
|
* Check if a URL should be crawled by robots.
|
|
@@ -82,27 +94,39 @@ export class RobotsTxtFile {
|
|
|
82
94
|
* @param [userAgent] relevant user agent, default to `*`
|
|
83
95
|
*/
|
|
84
96
|
isAllowed(url, userAgent = '*') {
|
|
85
|
-
return this
|
|
97
|
+
return this.#robots.isAllowed(url, userAgent) ?? true; // `undefined` means that there is no explicit rule for the requested URL - assume it's allowed
|
|
86
98
|
}
|
|
87
99
|
/**
|
|
88
|
-
* Get URLs of sitemaps referenced in the robots file.
|
|
100
|
+
* Get URLs of sitemaps referenced in the robots file, filtered by `options.enqueueStrategy` relative to
|
|
101
|
+
* the robots.txt host (default `'same-hostname'`; pass `'all'` to disable). Non-`http(s)` schemes are
|
|
102
|
+
* always dropped.
|
|
89
103
|
*/
|
|
90
|
-
getSitemaps() {
|
|
91
|
-
|
|
104
|
+
getSitemaps(options = {}) {
|
|
105
|
+
const { enqueueStrategy = 'same-hostname' } = options;
|
|
106
|
+
const sitemaps = [];
|
|
107
|
+
for (const sitemapUrl of this.#robots.getSitemaps()) {
|
|
108
|
+
// `filterUrl` tolerates an unparseable origin (returns not-allowed) rather than throwing.
|
|
109
|
+
const { allowed, reason } = filterUrl(sitemapUrl, this.#url, enqueueStrategy);
|
|
110
|
+
if (!allowed) {
|
|
111
|
+
this.#logger?.warning(`Skipping sitemap ${sitemapUrl} listed in robots.txt at ${this.#url}: ${reason}.`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
sitemaps.push(sitemapUrl);
|
|
115
|
+
}
|
|
116
|
+
return sitemaps;
|
|
92
117
|
}
|
|
93
118
|
/**
|
|
94
|
-
* Parse all the sitemaps referenced in the robots file.
|
|
119
|
+
* Parse all the sitemaps referenced in the robots file. `options` are forwarded to `getSitemaps`
|
|
120
|
+
* and the sitemap parser.
|
|
95
121
|
*/
|
|
96
|
-
async parseSitemaps() {
|
|
97
|
-
return Sitemap.load(this.
|
|
122
|
+
async parseSitemaps(options = {}) {
|
|
123
|
+
return Sitemap.load(this.getSitemaps(options), this.#proxyUrl, { ...options, logger: this.#logger });
|
|
98
124
|
}
|
|
99
125
|
/**
|
|
100
126
|
* Get all URLs from all the sitemaps referenced in the robots file. A shorthand for `(await robots.parseSitemaps()).urls`.
|
|
127
|
+
* `options` are forwarded to `parseSitemaps`.
|
|
101
128
|
*/
|
|
102
|
-
async parseUrlsFromSitemaps() {
|
|
103
|
-
return (await this.parseSitemaps()).urls;
|
|
129
|
+
async parseUrlsFromSitemaps(options = {}) {
|
|
130
|
+
return (await this.parseSitemaps(options)).urls;
|
|
104
131
|
}
|
|
105
132
|
}
|
|
106
|
-
// to stay backwards compatible
|
|
107
|
-
export { RobotsTxtFile as RobotsFile };
|
|
108
|
-
//# sourceMappingURL=robots.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { BaseHttpClient } from '@crawlee/http-client';
|
|
2
|
+
import type { Dictionary } from '@crawlee/types';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
/**
|
|
5
|
+
* Accepts any object (including arrays and functions).
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare const anyObject: z.ZodCustom<Dictionary, Dictionary>;
|
|
9
|
+
/**
|
|
10
|
+
* Accepts any array without validating its items (cheap for huge arrays).
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare const anyArray: z.ZodCustom<unknown[], unknown[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Accepts any function.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export declare const anyFunction: z.ZodCustom<(...args: any[]) => unknown, (...args: any[]) => unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Mirrors `ow.number`: `Infinity` is a valid number, `NaN` is not.
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare const anyNumber: z.ZodCustom<number, number>;
|
|
24
|
+
/**
|
|
25
|
+
* Accepts any object (including functions) that has all the given keys, own or inherited.
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export declare function objectWithKeys(keys: string[], message?: string): z.ZodType<Dictionary>;
|
|
29
|
+
/**
|
|
30
|
+
* Accepts only instances of {@link BaseHttpClient} (all Crawlee HTTP clients extend it).
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export declare const httpClient: z.ZodCustom<BaseHttpClient, BaseHttpClient>;
|
|
34
|
+
/**
|
|
35
|
+
* Accepts any object implementing the CrawleeLogger interface.
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export declare const logger: z.ZodType<Dictionary, unknown, z.core.$ZodTypeInternals<Dictionary, unknown>>;
|
|
39
|
+
/**
|
|
40
|
+
* Accepts any typed array (`Uint8Array`, `Float64Array`, ...), but not a `DataView`.
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
export declare const typedArray: z.ZodCustom<NodeJS.TypedArray<ArrayBufferLike>, NodeJS.TypedArray<ArrayBufferLike>>;
|
|
44
|
+
/**
|
|
45
|
+
* Accepts any non-null, non-array object.
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
export declare const plainObject: z.ZodCustom<Record<string, unknown>, Record<string, unknown>>;
|
|
49
|
+
/**
|
|
50
|
+
* Shape of a request stored in a request queue.
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
export declare const storageRequest: z.ZodObject<{
|
|
54
|
+
id: z.ZodString;
|
|
55
|
+
url: z.ZodURL;
|
|
56
|
+
uniqueKey: z.ZodString;
|
|
57
|
+
method: z.ZodOptional<z.ZodString>;
|
|
58
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
59
|
+
handledAt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
60
|
+
}, z.core.$loose>;
|
|
61
|
+
/**
|
|
62
|
+
* {@link storageRequest} before an id is assigned.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export declare const storageRequestWithoutId: z.ZodObject<{
|
|
66
|
+
url: z.ZodURL;
|
|
67
|
+
uniqueKey: z.ZodString;
|
|
68
|
+
method: z.ZodOptional<z.ZodString>;
|
|
69
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
70
|
+
handledAt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
71
|
+
}, z.core.$loose>;
|
|
72
|
+
/**
|
|
73
|
+
* `z.array(item)` whose top-level type error names the element type — ``expected an array of numbers`` —
|
|
74
|
+
* instead of zod's bare `expected array`. Element failures keep zod's per-index messages, and `elements`
|
|
75
|
+
* is a human-readable plural (`'numbers'`, `'URL patterns'`), since element types cannot be introspected.
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
export declare function arrayOf<TItem extends z.ZodType>(item: TItem, elements: string): z.ZodArray<TItem>;
|
|
79
|
+
/**
|
|
80
|
+
* Batch of {@link storageRequestWithoutId}.
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
export declare const storageRequestBatch: z.ZodArray<z.ZodObject<{
|
|
84
|
+
url: z.ZodURL;
|
|
85
|
+
uniqueKey: z.ZodString;
|
|
86
|
+
method: z.ZodOptional<z.ZodString>;
|
|
87
|
+
retryCount: z.ZodOptional<z.ZodNumber>;
|
|
88
|
+
handledAt: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
89
|
+
}, z.core.$loose>>;
|
|
90
|
+
/**
|
|
91
|
+
* Options of request queue add/update operations.
|
|
92
|
+
* @internal
|
|
93
|
+
*/
|
|
94
|
+
export declare const requestQueueOperationOptions: z.ZodObject<{
|
|
95
|
+
forefront: z.ZodOptional<z.ZodBoolean>;
|
|
96
|
+
}, z.core.$strip>;
|
|
97
|
+
/**
|
|
98
|
+
* Options of key-value store `listKeys`.
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
export declare const keyValueStoreListKeysOptions: z.ZodObject<{
|
|
102
|
+
prefix: z.ZodOptional<z.ZodString>;
|
|
103
|
+
exclusiveStartKey: z.ZodOptional<z.ZodString>;
|
|
104
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
/**
|
|
107
|
+
* Options of dataset item listing.
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
export declare const datasetListItemsOptions: z.ZodObject<{
|
|
111
|
+
desc: z.ZodOptional<z.ZodBoolean>;
|
|
112
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
113
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
114
|
+
}, z.core.$strip>;
|