@crawlee/core 3.17.1-beta.57 → 3.17.1-beta.59
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.
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
|
|
2
|
-
import { type RobotsTxtFile } from '@crawlee/utils';
|
|
2
|
+
import { EnqueueStrategy, type RobotsTxtFile } from '@crawlee/utils';
|
|
3
3
|
import type { SetRequired } from 'type-fest';
|
|
4
4
|
import type { Request } from '../request';
|
|
5
5
|
import type { AddRequestsBatchedOptions, AddRequestsBatchedResult, RequestProvider, RequestQueueOperationOptions } from '../storages';
|
|
6
6
|
import type { GlobInput, PseudoUrlInput, RegExpInput, RequestTransform, SkippedRequestCallback } from './shared';
|
|
7
|
+
export { EnqueueStrategy };
|
|
7
8
|
export interface EnqueueLinksOptions extends RequestQueueOperationOptions {
|
|
8
9
|
/** Limit the amount of actually enqueued URLs to this number. Useful for testing across the entire crawling scope. */
|
|
9
10
|
limit?: number;
|
|
@@ -156,57 +157,6 @@ export interface EnqueueLinksOptions extends RequestQueueOperationOptions {
|
|
|
156
157
|
*/
|
|
157
158
|
onSkippedRequest?: SkippedRequestCallback;
|
|
158
159
|
}
|
|
159
|
-
/**
|
|
160
|
-
* The different enqueueing strategies available.
|
|
161
|
-
*
|
|
162
|
-
* Depending on the strategy you select, we will only check certain parts of the URLs found. Here is a diagram of each URL part and their name:
|
|
163
|
-
*
|
|
164
|
-
* ```md
|
|
165
|
-
* Protocol Domain
|
|
166
|
-
* ┌────┐ ┌─────────┐
|
|
167
|
-
* https://example.crawlee.dev/...
|
|
168
|
-
* │ └─────────────────┤
|
|
169
|
-
* │ Hostname │
|
|
170
|
-
* │ │
|
|
171
|
-
* └─────────────────────────┘
|
|
172
|
-
* Origin
|
|
173
|
-
*```
|
|
174
|
-
*
|
|
175
|
-
* - The `Protocol` is usually `http` or `https`
|
|
176
|
-
* - The `Domain` represents the path without any possible subdomains to a website. For example, `crawlee.dev` is the domain of `https://example.crawlee.dev/`
|
|
177
|
-
* - The `Hostname` is the full path to a website, including any subdomains. For example, `example.crawlee.dev` is the hostname of `https://example.crawlee.dev/`
|
|
178
|
-
* - The `Origin` is the combination of the `Protocol` and `Hostname`. For example, `https://example.crawlee.dev` is the origin of `https://example.crawlee.dev/`
|
|
179
|
-
*/
|
|
180
|
-
export declare enum EnqueueStrategy {
|
|
181
|
-
/**
|
|
182
|
-
* Matches any URLs found
|
|
183
|
-
*/
|
|
184
|
-
All = "all",
|
|
185
|
-
/**
|
|
186
|
-
* Matches any URLs that have the same hostname.
|
|
187
|
-
* For example, `https://wow.example.com/hello` will be matched for a base url of `https://wow.example.com/`, but
|
|
188
|
-
* `https://example.com/hello` will not be matched.
|
|
189
|
-
*
|
|
190
|
-
* > This strategy will match both `http` and `https` protocols regardless of the base URL protocol.
|
|
191
|
-
*/
|
|
192
|
-
SameHostname = "same-hostname",
|
|
193
|
-
/**
|
|
194
|
-
* Matches any URLs that have the same domain as the base URL.
|
|
195
|
-
* For example, `https://wow.an.example.com` and `https://example.com` will both be matched for a base url of
|
|
196
|
-
* `https://example.com`.
|
|
197
|
-
*
|
|
198
|
-
* > This strategy will match both `http` and `https` protocols regardless of the base URL protocol.
|
|
199
|
-
*/
|
|
200
|
-
SameDomain = "same-domain",
|
|
201
|
-
/**
|
|
202
|
-
* Matches any URLs that have the same hostname and protocol.
|
|
203
|
-
* For example, `https://wow.example.com/hello` will be matched for a base url of `https://wow.example.com/`, but
|
|
204
|
-
* `http://wow.example.com/hello` will not be matched.
|
|
205
|
-
*
|
|
206
|
-
* > This strategy will ensure the protocol of the base URL is the same as the protocol of the URL to be enqueued.
|
|
207
|
-
*/
|
|
208
|
-
SameOrigin = "same-origin"
|
|
209
|
-
}
|
|
210
160
|
/**
|
|
211
161
|
* This function enqueues the urls provided to the {@link RequestQueue} provided. If you want to automatically find and enqueue links,
|
|
212
162
|
* you should use the context-aware `enqueueLinks` function provided on the crawler contexts.
|
|
@@ -4,62 +4,12 @@ exports.EnqueueStrategy = void 0;
|
|
|
4
4
|
exports.enqueueLinks = enqueueLinks;
|
|
5
5
|
exports.resolveBaseUrlForEnqueueLinksFiltering = resolveBaseUrlForEnqueueLinksFiltering;
|
|
6
6
|
const tslib_1 = require("tslib");
|
|
7
|
+
const utils_1 = require("@crawlee/utils");
|
|
8
|
+
Object.defineProperty(exports, "EnqueueStrategy", { enumerable: true, get: function () { return utils_1.EnqueueStrategy; } });
|
|
7
9
|
const ow_1 = tslib_1.__importDefault(require("ow"));
|
|
8
10
|
const tldts_1 = require("tldts");
|
|
9
11
|
const log_1 = tslib_1.__importDefault(require("@apify/log"));
|
|
10
12
|
const shared_1 = require("./shared");
|
|
11
|
-
/**
|
|
12
|
-
* The different enqueueing strategies available.
|
|
13
|
-
*
|
|
14
|
-
* Depending on the strategy you select, we will only check certain parts of the URLs found. Here is a diagram of each URL part and their name:
|
|
15
|
-
*
|
|
16
|
-
* ```md
|
|
17
|
-
* Protocol Domain
|
|
18
|
-
* ┌────┐ ┌─────────┐
|
|
19
|
-
* https://example.crawlee.dev/...
|
|
20
|
-
* │ └─────────────────┤
|
|
21
|
-
* │ Hostname │
|
|
22
|
-
* │ │
|
|
23
|
-
* └─────────────────────────┘
|
|
24
|
-
* Origin
|
|
25
|
-
*```
|
|
26
|
-
*
|
|
27
|
-
* - The `Protocol` is usually `http` or `https`
|
|
28
|
-
* - The `Domain` represents the path without any possible subdomains to a website. For example, `crawlee.dev` is the domain of `https://example.crawlee.dev/`
|
|
29
|
-
* - The `Hostname` is the full path to a website, including any subdomains. For example, `example.crawlee.dev` is the hostname of `https://example.crawlee.dev/`
|
|
30
|
-
* - The `Origin` is the combination of the `Protocol` and `Hostname`. For example, `https://example.crawlee.dev` is the origin of `https://example.crawlee.dev/`
|
|
31
|
-
*/
|
|
32
|
-
var EnqueueStrategy;
|
|
33
|
-
(function (EnqueueStrategy) {
|
|
34
|
-
/**
|
|
35
|
-
* Matches any URLs found
|
|
36
|
-
*/
|
|
37
|
-
EnqueueStrategy["All"] = "all";
|
|
38
|
-
/**
|
|
39
|
-
* Matches any URLs that have the same hostname.
|
|
40
|
-
* For example, `https://wow.example.com/hello` will be matched for a base url of `https://wow.example.com/`, but
|
|
41
|
-
* `https://example.com/hello` will not be matched.
|
|
42
|
-
*
|
|
43
|
-
* > This strategy will match both `http` and `https` protocols regardless of the base URL protocol.
|
|
44
|
-
*/
|
|
45
|
-
EnqueueStrategy["SameHostname"] = "same-hostname";
|
|
46
|
-
/**
|
|
47
|
-
* Matches any URLs that have the same domain as the base URL.
|
|
48
|
-
* For example, `https://wow.an.example.com` and `https://example.com` will both be matched for a base url of
|
|
49
|
-
* `https://example.com`.
|
|
50
|
-
*
|
|
51
|
-
* > This strategy will match both `http` and `https` protocols regardless of the base URL protocol.
|
|
52
|
-
*/
|
|
53
|
-
EnqueueStrategy["SameDomain"] = "same-domain";
|
|
54
|
-
/**
|
|
55
|
-
* Matches any URLs that have the same hostname and protocol.
|
|
56
|
-
* For example, `https://wow.example.com/hello` will be matched for a base url of `https://wow.example.com/`, but
|
|
57
|
-
* `http://wow.example.com/hello` will not be matched.
|
|
58
|
-
*
|
|
59
|
-
* > This strategy will ensure the protocol of the base URL is the same as the protocol of the URL to be enqueued.
|
|
60
|
-
*/
|
|
61
|
-
EnqueueStrategy["SameOrigin"] = "same-origin";
|
|
62
|
-
})(EnqueueStrategy || (exports.EnqueueStrategy = EnqueueStrategy = {}));
|
|
63
13
|
/**
|
|
64
14
|
* This function enqueues the urls provided to the {@link RequestQueue} provided. If you want to automatically find and enqueue links,
|
|
65
15
|
* you should use the context-aware `enqueueLinks` function provided on the crawler contexts.
|
|
@@ -109,7 +59,7 @@ async function enqueueLinks(options) {
|
|
|
109
59
|
exclude: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.string, ow_1.default.regExp, ow_1.default.object.hasKeys('glob'), ow_1.default.object.hasKeys('regexp'))),
|
|
110
60
|
regexps: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.regExp, ow_1.default.object.hasKeys('regexp'))),
|
|
111
61
|
transformRequestFunction: ow_1.default.optional.function,
|
|
112
|
-
strategy: ow_1.default.optional.string.oneOf(Object.values(EnqueueStrategy)),
|
|
62
|
+
strategy: ow_1.default.optional.string.oneOf(Object.values(utils_1.EnqueueStrategy)),
|
|
113
63
|
waitForAllRequestsToBeAdded: ow_1.default.optional.boolean,
|
|
114
64
|
}));
|
|
115
65
|
const { requestQueue, limit, urls, pseudoUrls, exclude, globs, regexps, transformRequestFunction, forefront, waitForAllRequestsToBeAdded, robotsTxtFile, onSkippedRequest, } = options;
|
|
@@ -136,19 +86,19 @@ async function enqueueLinks(options) {
|
|
|
136
86
|
urlPatternObjects.push(...(0, shared_1.constructRegExpObjectsFromRegExps)(regexps));
|
|
137
87
|
}
|
|
138
88
|
if (!urlPatternObjects.length) {
|
|
139
|
-
options.strategy ?? (options.strategy = EnqueueStrategy.SameHostname);
|
|
89
|
+
options.strategy ?? (options.strategy = utils_1.EnqueueStrategy.SameHostname);
|
|
140
90
|
}
|
|
141
91
|
const enqueueStrategyPatterns = [];
|
|
142
92
|
if (options.baseUrl) {
|
|
143
93
|
const url = new URL(options.baseUrl);
|
|
144
94
|
switch (options.strategy) {
|
|
145
|
-
case EnqueueStrategy.SameHostname:
|
|
95
|
+
case utils_1.EnqueueStrategy.SameHostname:
|
|
146
96
|
// We need to get the origin of the passed in domain in the event someone sets baseUrl
|
|
147
97
|
// to an url like https://example.com/deep/default/path and one of the found urls is an
|
|
148
98
|
// absolute relative path (/path/to/page)
|
|
149
99
|
enqueueStrategyPatterns.push({ glob: ignoreHttpSchema(`${url.origin}/**`) });
|
|
150
100
|
break;
|
|
151
|
-
case EnqueueStrategy.SameDomain: {
|
|
101
|
+
case utils_1.EnqueueStrategy.SameDomain: {
|
|
152
102
|
// Get the actual hostname from the base url
|
|
153
103
|
const baseUrlHostname = (0, tldts_1.getDomain)(url.hostname, { mixedInputs: false });
|
|
154
104
|
if (baseUrlHostname) {
|
|
@@ -163,12 +113,12 @@ async function enqueueLinks(options) {
|
|
|
163
113
|
}
|
|
164
114
|
break;
|
|
165
115
|
}
|
|
166
|
-
case EnqueueStrategy.SameOrigin: {
|
|
116
|
+
case utils_1.EnqueueStrategy.SameOrigin: {
|
|
167
117
|
// The same behavior as SameHostname, but respecting the protocol of the URL
|
|
168
118
|
enqueueStrategyPatterns.push({ glob: `${url.origin}/**` });
|
|
169
119
|
break;
|
|
170
120
|
}
|
|
171
|
-
case EnqueueStrategy.All:
|
|
121
|
+
case utils_1.EnqueueStrategy.All:
|
|
172
122
|
default:
|
|
173
123
|
enqueueStrategyPatterns.push({ glob: `http{s,}://**` });
|
|
174
124
|
break;
|
|
@@ -249,13 +199,13 @@ function resolveBaseUrlForEnqueueLinksFiltering({ enqueueStrategy, finalRequestU
|
|
|
249
199
|
const originalUrlOrigin = new URL(originalRequestUrl).origin;
|
|
250
200
|
const finalUrlOrigin = new URL(finalRequestUrl ?? originalRequestUrl).origin;
|
|
251
201
|
// We can assume users want to go off the domain in this case
|
|
252
|
-
if (enqueueStrategy === EnqueueStrategy.All) {
|
|
202
|
+
if (enqueueStrategy === utils_1.EnqueueStrategy.All) {
|
|
253
203
|
return finalUrlOrigin;
|
|
254
204
|
}
|
|
255
205
|
// If the user wants to ensure the same domain is accessed, regardless of subdomains, we check to ensure the domains match
|
|
256
206
|
// Returning undefined here is intentional! If the domains don't match, having no baseUrl in enqueueLinks will cause it to not enqueue anything
|
|
257
207
|
// which is the intended behavior (since we went off domain)
|
|
258
|
-
if (enqueueStrategy === EnqueueStrategy.SameDomain) {
|
|
208
|
+
if (enqueueStrategy === utils_1.EnqueueStrategy.SameDomain) {
|
|
259
209
|
const originalHostname = (0, tldts_1.getDomain)(originalUrlOrigin, { mixedInputs: false });
|
|
260
210
|
const finalHostname = (0, tldts_1.getDomain)(finalUrlOrigin, { mixedInputs: false });
|
|
261
211
|
if (originalHostname === finalHostname) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/core",
|
|
3
|
-
"version": "3.17.1-beta.
|
|
3
|
+
"version": "3.17.1-beta.59",
|
|
4
4
|
"description": "The scalable web crawling and scraping library for JavaScript/Node.js. Enables development of data extraction and web automation jobs (not only) with headless Chrome and Puppeteer.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=16.0.0"
|
|
@@ -57,11 +57,11 @@
|
|
|
57
57
|
"@apify/datastructures": "^2.0.0",
|
|
58
58
|
"@apify/log": "^2.4.0",
|
|
59
59
|
"@apify/pseudo_url": "^2.0.30",
|
|
60
|
-
"@apify/timeout": "^0.
|
|
60
|
+
"@apify/timeout": "^0.4.0",
|
|
61
61
|
"@apify/utilities": "^2.7.10",
|
|
62
|
-
"@crawlee/memory-storage": "3.17.1-beta.
|
|
63
|
-
"@crawlee/types": "3.17.1-beta.
|
|
64
|
-
"@crawlee/utils": "3.17.1-beta.
|
|
62
|
+
"@crawlee/memory-storage": "3.17.1-beta.59",
|
|
63
|
+
"@crawlee/types": "3.17.1-beta.59",
|
|
64
|
+
"@crawlee/utils": "3.17.1-beta.59",
|
|
65
65
|
"@sapphire/async-queue": "^1.5.1",
|
|
66
66
|
"@standard-schema/spec": "^1.0.0",
|
|
67
67
|
"@vladfrangu/async_event_emitter": "^2.2.2",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "8980103b700213806843d4fe1355b75519474ea4"
|
|
88
88
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ParseSitemapOptions } from '@crawlee/utils';
|
|
2
2
|
import { Configuration } from '../configuration';
|
|
3
3
|
import type { GlobInput, RegExpInput } from '../enqueue_links';
|
|
4
|
+
import { EnqueueStrategy } from '../enqueue_links';
|
|
4
5
|
import { Request } from '../request';
|
|
5
6
|
import type { IRequestList } from './request_list';
|
|
6
7
|
interface UrlConstraints {
|
|
@@ -76,6 +77,13 @@ export interface SitemapRequestListOptions extends UrlConstraints {
|
|
|
76
77
|
* @default 200
|
|
77
78
|
*/
|
|
78
79
|
maxBufferSize?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Keep only sitemap-derived URLs matching this strategy relative to the parent sitemap URL; non-`http(s)`
|
|
82
|
+
* schemes are always dropped. The filtering stays enforced after navigation (e.g. across redirects).
|
|
83
|
+
* Pass `'all'` to disable host filtering.
|
|
84
|
+
* @default EnqueueStrategy.SameHostname
|
|
85
|
+
*/
|
|
86
|
+
enqueueStrategy?: EnqueueStrategy | `${EnqueueStrategy}`;
|
|
79
87
|
/**
|
|
80
88
|
* Advanced options for the underlying `parseSitemap` call.
|
|
81
89
|
*/
|
|
@@ -133,6 +141,10 @@ export declare class SitemapRequestList implements IRequestList {
|
|
|
133
141
|
* Proxy URL to be used for sitemap loading.
|
|
134
142
|
*/
|
|
135
143
|
private proxyUrl;
|
|
144
|
+
/**
|
|
145
|
+
* Enqueue strategy applied to sitemap-derived URLs and stamped onto the emitted `Request` objects.
|
|
146
|
+
*/
|
|
147
|
+
private enqueueStrategy;
|
|
136
148
|
/**
|
|
137
149
|
* Logger instance.
|
|
138
150
|
*/
|
|
@@ -132,6 +132,15 @@ class SitemapRequestList {
|
|
|
132
132
|
writable: true,
|
|
133
133
|
value: void 0
|
|
134
134
|
});
|
|
135
|
+
/**
|
|
136
|
+
* Enqueue strategy applied to sitemap-derived URLs and stamped onto the emitted `Request` objects.
|
|
137
|
+
*/
|
|
138
|
+
Object.defineProperty(this, "enqueueStrategy", {
|
|
139
|
+
enumerable: true,
|
|
140
|
+
configurable: true,
|
|
141
|
+
writable: true,
|
|
142
|
+
value: void 0
|
|
143
|
+
});
|
|
135
144
|
/**
|
|
136
145
|
* Logger instance.
|
|
137
146
|
*/
|
|
@@ -173,6 +182,7 @@ class SitemapRequestList {
|
|
|
173
182
|
signal: ow_1.default.optional.any(),
|
|
174
183
|
timeoutMillis: ow_1.default.optional.number,
|
|
175
184
|
maxBufferSize: ow_1.default.optional.number,
|
|
185
|
+
enqueueStrategy: ow_1.default.optional.string.oneOf(Object.values(enqueue_links_1.EnqueueStrategy)),
|
|
176
186
|
parseSitemapOptions: ow_1.default.optional.object,
|
|
177
187
|
globs: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.string, ow_1.default.object.hasKeys('glob'))),
|
|
178
188
|
exclude: ow_1.default.optional.array.ofType(ow_1.default.any(ow_1.default.string, ow_1.default.regExp, ow_1.default.object.hasKeys('glob'), ow_1.default.object.hasKeys('regexp'))),
|
|
@@ -200,6 +210,7 @@ class SitemapRequestList {
|
|
|
200
210
|
this.persistStateKey = options.persistStateKey;
|
|
201
211
|
this.persistenceOptions = { enable: true, ...options.persistenceOptions };
|
|
202
212
|
this.proxyUrl = options.proxyUrl;
|
|
213
|
+
this.enqueueStrategy = options.enqueueStrategy ?? enqueue_links_1.EnqueueStrategy.SameHostname;
|
|
203
214
|
this.urlQueueStream = this.createNewStream(options.maxBufferSize ?? 200);
|
|
204
215
|
this.sitemapParsingProgress.pendingSitemapUrls = new Set(options.sitemapUrls);
|
|
205
216
|
this.events = config.getEventManager();
|
|
@@ -308,6 +319,7 @@ class SitemapRequestList {
|
|
|
308
319
|
...parseSitemapOptions,
|
|
309
320
|
maxDepth: 0,
|
|
310
321
|
emitNestedSitemaps: true,
|
|
322
|
+
enqueueStrategy: this.enqueueStrategy,
|
|
311
323
|
})) {
|
|
312
324
|
if (!item.originSitemapUrl) {
|
|
313
325
|
// This is a nested sitemap
|
|
@@ -457,7 +469,7 @@ class SitemapRequestList {
|
|
|
457
469
|
if (!nextUrl) {
|
|
458
470
|
return null;
|
|
459
471
|
}
|
|
460
|
-
this.requestData.set(nextUrl, new request_1.Request({ url: nextUrl }));
|
|
472
|
+
this.requestData.set(nextUrl, new request_1.Request({ url: nextUrl, enqueueStrategy: this.enqueueStrategy }));
|
|
461
473
|
}
|
|
462
474
|
this.inProgress.add(nextUrl);
|
|
463
475
|
return this.requestData.get(nextUrl);
|