@crawlee/core 4.0.0-beta.80 → 4.0.0-beta.82
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/autoscaling/autoscaled_pool.d.ts +9 -9
- package/autoscaling/autoscaled_pool.js +25 -25
- package/autoscaling/snapshotter.d.ts +3 -28
- package/autoscaling/snapshotter.js +0 -36
- package/autoscaling/system_status.d.ts +1 -1
- package/autoscaling/system_status.js +3 -3
- package/crawlers/statistics.d.ts +6 -6
- package/crawlers/statistics.js +8 -8
- package/package.json +5 -5
- package/proxy_configuration.d.ts +26 -14
- package/proxy_configuration.js +8 -11
- package/router.d.ts +1 -1
- package/session_pool/session.d.ts +2 -2
- package/session_pool/session.js +3 -3
- package/session_pool/session_pool.d.ts +23 -27
- package/session_pool/session_pool.js +29 -29
- package/storages/request_list.d.ts +16 -16
- package/storages/request_list.js +37 -37
- package/storages/request_queue.d.ts +20 -34
- package/storages/request_queue.js +13 -15
- package/storages/utils.d.ts +2 -2
- package/validators.js +2 -2
package/storages/request_list.js
CHANGED
|
@@ -162,19 +162,19 @@ export class RequestList {
|
|
|
162
162
|
}
|
|
163
163
|
this.isLoading = true;
|
|
164
164
|
await purgeDefaultStorages({ onlyPurgeOnce: true });
|
|
165
|
-
const [state, persistedRequests] = await this.
|
|
165
|
+
const [state, persistedRequests] = await this.loadStateAndPersistedRequests();
|
|
166
166
|
// Add persisted requests / new sources in a memory efficient way because with very
|
|
167
167
|
// large lists, we were running out of memory.
|
|
168
168
|
if (persistedRequests) {
|
|
169
|
-
await this.
|
|
169
|
+
await this.addPersistedRequests(persistedRequests);
|
|
170
170
|
}
|
|
171
171
|
else {
|
|
172
|
-
await this.
|
|
172
|
+
await this.addRequestsFromSources();
|
|
173
173
|
}
|
|
174
|
-
this.
|
|
174
|
+
this.restoreState(state);
|
|
175
175
|
this.isInitialized = true;
|
|
176
176
|
if (this.persistRequestsKey && !this.areRequestsPersisted)
|
|
177
|
-
await this.
|
|
177
|
+
await this.persistRequests();
|
|
178
178
|
if (this.persistStateKey) {
|
|
179
179
|
serviceLocator.getEventManager().on("persistState" /* EventType.PERSIST_STATE */, this.persistState);
|
|
180
180
|
}
|
|
@@ -185,7 +185,7 @@ export class RequestList {
|
|
|
185
185
|
* This needs to be done in a memory efficient way. We should update the input
|
|
186
186
|
* to a Stream once apify-client supports streams.
|
|
187
187
|
*/
|
|
188
|
-
async
|
|
188
|
+
async addPersistedRequests(persistedRequests) {
|
|
189
189
|
// We don't need the sources so we purge them to
|
|
190
190
|
// prevent them from hanging in memory.
|
|
191
191
|
for (let i = 0; i < this.sources.length; i++) {
|
|
@@ -196,7 +196,7 @@ export class RequestList {
|
|
|
196
196
|
this.areRequestsPersisted = true;
|
|
197
197
|
const requestStream = createDeserialize(persistedRequests);
|
|
198
198
|
for await (const request of requestStream) {
|
|
199
|
-
this.
|
|
199
|
+
this.addRequest(request);
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
/**
|
|
@@ -205,7 +205,7 @@ export class RequestList {
|
|
|
205
205
|
* We need to avoid keeping both sources and requests in memory
|
|
206
206
|
* to reduce memory footprint with very large sources.
|
|
207
207
|
*/
|
|
208
|
-
async
|
|
208
|
+
async addRequestsFromSources() {
|
|
209
209
|
// We'll load all sources in sequence to ensure that they get loaded in the right order.
|
|
210
210
|
const sourcesCount = this.sources.length;
|
|
211
211
|
for (let i = 0; i < sourcesCount; i++) {
|
|
@@ -215,11 +215,11 @@ export class RequestList {
|
|
|
215
215
|
// oxlint-disable-next-line typescript/no-array-delete
|
|
216
216
|
delete this.sources[i];
|
|
217
217
|
if (typeof source === 'object' && source.requestsFromUrl) {
|
|
218
|
-
const fetchedRequests = await this.
|
|
219
|
-
await this.
|
|
218
|
+
const fetchedRequests = await this.fetchRequestsFromUrl(source);
|
|
219
|
+
await this.addFetchedRequests(source, fetchedRequests);
|
|
220
220
|
}
|
|
221
221
|
else {
|
|
222
|
-
this.
|
|
222
|
+
this.addRequest(source);
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
// Drop the original array full of empty indexes.
|
|
@@ -232,7 +232,7 @@ export class RequestList {
|
|
|
232
232
|
const source = sourcesFromFunction[i];
|
|
233
233
|
// oxlint-disable-next-line typescript/no-array-delete -- intentional, drop the slot so V8 can collect the object
|
|
234
234
|
delete sourcesFromFunction[i];
|
|
235
|
-
this.
|
|
235
|
+
this.addRequest(source);
|
|
236
236
|
}
|
|
237
237
|
sourcesFromFunction.length = 0;
|
|
238
238
|
}
|
|
@@ -277,7 +277,7 @@ export class RequestList {
|
|
|
277
277
|
* are automatically persisted at RequestList initialization (if the persistRequestsKey is set),
|
|
278
278
|
* but there's no reason to persist it again afterwards, because RequestList is immutable.
|
|
279
279
|
*/
|
|
280
|
-
async
|
|
280
|
+
async persistRequests() {
|
|
281
281
|
const serializedRequests = await serializeArray(this.requests);
|
|
282
282
|
this.store ??= await KeyValueStore.open();
|
|
283
283
|
await this.store.setValue(this.persistRequestsKey, serializedRequests, { contentType: CONTENT_TYPE_BINARY });
|
|
@@ -286,7 +286,7 @@ export class RequestList {
|
|
|
286
286
|
/**
|
|
287
287
|
* Restores RequestList state from a state object.
|
|
288
288
|
*/
|
|
289
|
-
|
|
289
|
+
restoreState(state) {
|
|
290
290
|
// If there's no state it means we've not persisted any (yet).
|
|
291
291
|
if (!state)
|
|
292
292
|
return;
|
|
@@ -341,7 +341,7 @@ export class RequestList {
|
|
|
341
341
|
* Attempts to load state and requests using the `RequestList` configuration
|
|
342
342
|
* and returns a tuple of [state, requests] where each may be null if not loaded.
|
|
343
343
|
*/
|
|
344
|
-
async
|
|
344
|
+
async loadStateAndPersistedRequests() {
|
|
345
345
|
let state;
|
|
346
346
|
let persistedRequests;
|
|
347
347
|
if (this.initialState) {
|
|
@@ -349,12 +349,12 @@ export class RequestList {
|
|
|
349
349
|
this.log.debug('Loaded state from options.state argument.');
|
|
350
350
|
}
|
|
351
351
|
else if (this.persistStateKey) {
|
|
352
|
-
state = await this.
|
|
352
|
+
state = await this.getPersistedState(this.persistStateKey);
|
|
353
353
|
if (state)
|
|
354
354
|
this.log.debug('Loaded state from key value store using the persistStateKey.');
|
|
355
355
|
}
|
|
356
356
|
if (this.persistRequestsKey) {
|
|
357
|
-
persistedRequests = await this.
|
|
357
|
+
persistedRequests = await this.getPersistedState(this.persistRequestsKey);
|
|
358
358
|
if (persistedRequests)
|
|
359
359
|
this.log.debug('Loaded requests from key value store using the persistRequestsKey.');
|
|
360
360
|
}
|
|
@@ -365,7 +365,7 @@ export class RequestList {
|
|
|
365
365
|
* Note that the object's fields can change in future releases.
|
|
366
366
|
*/
|
|
367
367
|
getState() {
|
|
368
|
-
this.
|
|
368
|
+
this.ensureIsInitialized();
|
|
369
369
|
return {
|
|
370
370
|
nextIndex: this.nextIndex,
|
|
371
371
|
nextUniqueKey: this.nextIndex < this.requests.length ? this.requests[this.nextIndex].uniqueKey : null,
|
|
@@ -376,21 +376,21 @@ export class RequestList {
|
|
|
376
376
|
* @inheritDoc
|
|
377
377
|
*/
|
|
378
378
|
async isEmpty() {
|
|
379
|
-
this.
|
|
379
|
+
this.ensureIsInitialized();
|
|
380
380
|
return this.requestsToRetry.length === 0 && this.nextIndex >= this.requests.length;
|
|
381
381
|
}
|
|
382
382
|
/**
|
|
383
383
|
* @inheritDoc
|
|
384
384
|
*/
|
|
385
385
|
async isFinished() {
|
|
386
|
-
this.
|
|
386
|
+
this.ensureIsInitialized();
|
|
387
387
|
return this.inProgress.size === 0 && this.nextIndex >= this.requests.length;
|
|
388
388
|
}
|
|
389
389
|
/**
|
|
390
390
|
* @inheritDoc
|
|
391
391
|
*/
|
|
392
392
|
async fetchNextRequest() {
|
|
393
|
-
this.
|
|
393
|
+
this.ensureIsInitialized();
|
|
394
394
|
// First re-serve any requests that were interrupted before the last state persist.
|
|
395
395
|
const uniqueKey = this.requestsToRetry.shift();
|
|
396
396
|
if (uniqueKey) {
|
|
@@ -431,19 +431,19 @@ export class RequestList {
|
|
|
431
431
|
*/
|
|
432
432
|
async markRequestAsHandled(request) {
|
|
433
433
|
const { uniqueKey } = request;
|
|
434
|
-
this.
|
|
435
|
-
this.
|
|
436
|
-
this.
|
|
434
|
+
this.ensureUniqueKeyValid(uniqueKey);
|
|
435
|
+
this.ensureInProgress(uniqueKey);
|
|
436
|
+
this.ensureIsInitialized();
|
|
437
437
|
this.inProgress.delete(uniqueKey);
|
|
438
438
|
this.isStatePersisted = false;
|
|
439
439
|
}
|
|
440
440
|
/**
|
|
441
441
|
* Adds all fetched requests from a URL from a remote resource.
|
|
442
442
|
*/
|
|
443
|
-
async
|
|
443
|
+
async addFetchedRequests(source, fetchedRequests) {
|
|
444
444
|
const { requestsFromUrl, regex } = source;
|
|
445
445
|
const originalLength = this.requests.length;
|
|
446
|
-
fetchedRequests.forEach((request) => this.
|
|
446
|
+
fetchedRequests.forEach((request) => this.addRequest(request));
|
|
447
447
|
const fetchedCount = fetchedRequests.length;
|
|
448
448
|
const importedCount = this.requests.length - originalLength;
|
|
449
449
|
this.log.info('Fetched and loaded Requests from a remote resource.', {
|
|
@@ -455,7 +455,7 @@ export class RequestList {
|
|
|
455
455
|
sample: JSON.stringify(fetchedRequests.slice(0, 5)),
|
|
456
456
|
});
|
|
457
457
|
}
|
|
458
|
-
async
|
|
458
|
+
async getPersistedState(key) {
|
|
459
459
|
this.store ??= await KeyValueStore.open();
|
|
460
460
|
const state = await this.store.getValue(key);
|
|
461
461
|
return state;
|
|
@@ -463,7 +463,7 @@ export class RequestList {
|
|
|
463
463
|
/**
|
|
464
464
|
* Fetches URLs from requestsFromUrl and returns them in format of list of requests
|
|
465
465
|
*/
|
|
466
|
-
async
|
|
466
|
+
async fetchRequestsFromUrl(source) {
|
|
467
467
|
const { requestsFromUrl, regex, ...sharedOpts } = source;
|
|
468
468
|
// Download remote resource and parse URLs.
|
|
469
469
|
let urlsArr;
|
|
@@ -471,7 +471,7 @@ export class RequestList {
|
|
|
471
471
|
urlsArr = await this._downloadListOfUrls({
|
|
472
472
|
url: requestsFromUrl,
|
|
473
473
|
urlRegExp: regex,
|
|
474
|
-
proxyUrl: await this.proxyConfiguration?.
|
|
474
|
+
proxyUrl: (await this.proxyConfiguration?.newProxyInfo())?.url,
|
|
475
475
|
});
|
|
476
476
|
}
|
|
477
477
|
catch (err) {
|
|
@@ -489,7 +489,7 @@ export class RequestList {
|
|
|
489
489
|
* If the `source` parameter is a string or plain object and not an instance
|
|
490
490
|
* of a `Request`, then the function creates a `Request` instance.
|
|
491
491
|
*/
|
|
492
|
-
|
|
492
|
+
addRequest(source) {
|
|
493
493
|
let request;
|
|
494
494
|
const type = typeof source;
|
|
495
495
|
if (type === 'string') {
|
|
@@ -511,7 +511,7 @@ export class RequestList {
|
|
|
511
511
|
request.uniqueKey += `-${this.requests.length}`;
|
|
512
512
|
}
|
|
513
513
|
const { uniqueKey } = request;
|
|
514
|
-
this.
|
|
514
|
+
this.ensureUniqueKeyValid(uniqueKey);
|
|
515
515
|
// Skip requests with duplicate uniqueKey
|
|
516
516
|
if (!Object.hasOwn(this.uniqueKeyToIndex, uniqueKey)) {
|
|
517
517
|
this.uniqueKeyToIndex[uniqueKey] = this.requests.length;
|
|
@@ -525,7 +525,7 @@ export class RequestList {
|
|
|
525
525
|
* Helper function that validates unique key.
|
|
526
526
|
* Throws an error if uniqueKey is not a non-empty string.
|
|
527
527
|
*/
|
|
528
|
-
|
|
528
|
+
ensureUniqueKeyValid(uniqueKey) {
|
|
529
529
|
if (typeof uniqueKey !== 'string' || !uniqueKey) {
|
|
530
530
|
throw new Error("Request object's uniqueKey must be a non-empty string");
|
|
531
531
|
}
|
|
@@ -533,7 +533,7 @@ export class RequestList {
|
|
|
533
533
|
/**
|
|
534
534
|
* Checks that a request is currently being processed and throws an error if not.
|
|
535
535
|
*/
|
|
536
|
-
|
|
536
|
+
ensureInProgress(uniqueKey) {
|
|
537
537
|
if (!this.inProgress.has(uniqueKey)) {
|
|
538
538
|
throw new Error(`The request is not being processed (uniqueKey: ${uniqueKey})`);
|
|
539
539
|
}
|
|
@@ -541,7 +541,7 @@ export class RequestList {
|
|
|
541
541
|
/**
|
|
542
542
|
* Throws an error if request list wasn't initialized.
|
|
543
543
|
*/
|
|
544
|
-
|
|
544
|
+
ensureIsInitialized() {
|
|
545
545
|
if (!this.isInitialized) {
|
|
546
546
|
throw new Error('RequestList is not initialized; you must call "await requestList.initialize()" before using it!');
|
|
547
547
|
}
|
|
@@ -550,14 +550,14 @@ export class RequestList {
|
|
|
550
550
|
* Returns the total number of unique requests present in the `RequestList`.
|
|
551
551
|
*/
|
|
552
552
|
async getTotalCount() {
|
|
553
|
-
this.
|
|
553
|
+
this.ensureIsInitialized();
|
|
554
554
|
return this.requests.length;
|
|
555
555
|
}
|
|
556
556
|
/**
|
|
557
557
|
* Returns the number of pending requests in the `RequestList`.
|
|
558
558
|
*/
|
|
559
559
|
async getPendingCount() {
|
|
560
|
-
this.
|
|
560
|
+
this.ensureIsInitialized();
|
|
561
561
|
return this.requests.length - (this.nextIndex - this.inProgress.size);
|
|
562
562
|
}
|
|
563
563
|
/**
|
|
@@ -575,7 +575,7 @@ export class RequestList {
|
|
|
575
575
|
* @inheritDoc
|
|
576
576
|
*/
|
|
577
577
|
async getHandledCount() {
|
|
578
|
-
this.
|
|
578
|
+
this.ensureIsInitialized();
|
|
579
579
|
return this.nextIndex - this.inProgress.size;
|
|
580
580
|
}
|
|
581
581
|
/**
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BatchAddRequestsResult, Dictionary, ProcessedRequest, QueueOperationInfo, RequestQueueBackend, RequestQueueInfo } from '@crawlee/types';
|
|
2
2
|
import type { ReadonlyDeep } from 'type-fest';
|
|
3
|
-
import { LruCache } from '@apify/datastructures';
|
|
4
|
-
import { Configuration } from '../configuration.js';
|
|
5
|
-
import type { EventManager } from '../events/event_manager.js';
|
|
6
3
|
import type { CrawleeLogger } from '../log.js';
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
4
|
+
import type { IProxyConfiguration } from '../proxy_configuration.js';
|
|
5
|
+
import type { Source } from '../request.js';
|
|
9
6
|
import { Request } from '../request.js';
|
|
10
7
|
import type { IRequestManager, RequestsLike } from './request_manager.js';
|
|
11
8
|
import type { RequestQueueStats } from './storage_stats.js';
|
|
12
9
|
import type { IStorage, StorageIdentifier } from './storage_instance_manager.js';
|
|
13
10
|
import type { StorageOpenOptions } from './utils.js';
|
|
14
|
-
import { RequestDeduplicationCache } from './request_dedup_cache.js';
|
|
15
11
|
/**
|
|
16
12
|
* Represents a queue of URLs to crawl, which is used for deep crawling of websites
|
|
17
13
|
* where you start with several URLs and then recursively
|
|
@@ -47,29 +43,28 @@ import { RequestDeduplicationCache } from './request_dedup_cache.js';
|
|
|
47
43
|
* @category Sources
|
|
48
44
|
*/
|
|
49
45
|
export declare class RequestQueue implements IStorage, IRequestManager {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
protected requestCache: LruCache<RequestLruItem>;
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly name?: string;
|
|
48
|
+
readonly backend: RequestQueueBackend;
|
|
49
|
+
private proxyConfiguration?;
|
|
50
|
+
readonly log: CrawleeLogger;
|
|
51
|
+
private requestCache;
|
|
57
52
|
/**
|
|
58
53
|
* Remembers the `requestId` of every request already submitted to the client — including background
|
|
59
54
|
* batches that `requestCache` skips — so overlapping URL sets aren't re-submitted.
|
|
60
55
|
* See {@link RequestDeduplicationCache} for why this is a separate, cheaper cache.
|
|
61
56
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
private requestSeenCache;
|
|
58
|
+
private queuePausedForMigration;
|
|
59
|
+
private inProgressRequestBatchCount;
|
|
65
60
|
/**
|
|
66
61
|
* The largest expected request-processing time (in seconds) seen so far via
|
|
67
62
|
* {@link setExpectedRequestProcessingTimeSecs}. Used to ensure that value is only ever raised, never
|
|
68
63
|
* lowered, before being forwarded to the storage backend.
|
|
69
64
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
private expectedRequestProcessingSecs;
|
|
66
|
+
private httpClient?;
|
|
67
|
+
private readonly events;
|
|
73
68
|
private readonly statsTracker;
|
|
74
69
|
/**
|
|
75
70
|
* Backend-independent usage counters tracked for this request queue (write operations and
|
|
@@ -79,7 +74,7 @@ export declare class RequestQueue implements IStorage, IRequestManager {
|
|
|
79
74
|
/**
|
|
80
75
|
* @internal
|
|
81
76
|
*/
|
|
82
|
-
constructor(options: RequestQueueOptions
|
|
77
|
+
constructor(options: RequestQueueOptions);
|
|
83
78
|
/**
|
|
84
79
|
* Returns the total number of requests in the queue (i.e. pending + handled).
|
|
85
80
|
*
|
|
@@ -205,7 +200,7 @@ export declare class RequestQueue implements IStorage, IRequestManager {
|
|
|
205
200
|
/**
|
|
206
201
|
* Caches information about request to beware of unneeded addRequest() calls.
|
|
207
202
|
*/
|
|
208
|
-
|
|
203
|
+
private cacheRequest;
|
|
209
204
|
/**
|
|
210
205
|
* Removes the queue either from the Apify Cloud storage or from the local database,
|
|
211
206
|
* depending on the mode of operation.
|
|
@@ -254,11 +249,11 @@ export declare class RequestQueue implements IStorage, IRequestManager {
|
|
|
254
249
|
/**
|
|
255
250
|
* Fetches URLs from requestsFromUrl and returns them in format of list of requests
|
|
256
251
|
*/
|
|
257
|
-
|
|
252
|
+
private fetchRequestsFromUrl;
|
|
258
253
|
/**
|
|
259
254
|
* Adds all fetched requests from a URL from a remote resource.
|
|
260
255
|
*/
|
|
261
|
-
|
|
256
|
+
private addFetchedRequests;
|
|
262
257
|
/**
|
|
263
258
|
* @internal wraps public utility for mocking purposes
|
|
264
259
|
*/
|
|
@@ -282,14 +277,6 @@ export declare class RequestQueue implements IStorage, IRequestManager {
|
|
|
282
277
|
*/
|
|
283
278
|
static open(identifier?: string | StorageIdentifier | null, options?: StorageOpenOptions): Promise<RequestQueue>;
|
|
284
279
|
}
|
|
285
|
-
interface RequestLruItem {
|
|
286
|
-
uniqueKey: string;
|
|
287
|
-
isHandled: boolean;
|
|
288
|
-
id: string;
|
|
289
|
-
hydrated: Request | null;
|
|
290
|
-
lockExpiresAt: number | null;
|
|
291
|
-
forefront: boolean;
|
|
292
|
-
}
|
|
293
280
|
export interface RequestQueueOptions {
|
|
294
281
|
/** Resolved metadata for the request queue, as returned by the backend's `getMetadata()`. */
|
|
295
282
|
metadata: RequestQueueInfo;
|
|
@@ -299,7 +286,7 @@ export interface RequestQueueOptions {
|
|
|
299
286
|
* Takes advantage of the internal address rotation and authentication process.
|
|
300
287
|
* If undefined, the `requestsFromUrl` requests will be made without proxy.
|
|
301
288
|
*/
|
|
302
|
-
proxyConfiguration?:
|
|
289
|
+
proxyConfiguration?: IProxyConfiguration;
|
|
303
290
|
}
|
|
304
291
|
export interface RequestQueueOperationOptions {
|
|
305
292
|
/**
|
|
@@ -382,4 +369,3 @@ export interface AddRequestsBatchedResult {
|
|
|
382
369
|
*/
|
|
383
370
|
requestsOverLimit?: Source[];
|
|
384
371
|
}
|
|
385
|
-
export {};
|
|
@@ -55,7 +55,6 @@ const MAX_UNPROCESSED_REQUESTS_RETRIES = 3;
|
|
|
55
55
|
* @category Sources
|
|
56
56
|
*/
|
|
57
57
|
export class RequestQueue {
|
|
58
|
-
config;
|
|
59
58
|
id;
|
|
60
59
|
name;
|
|
61
60
|
backend;
|
|
@@ -92,8 +91,7 @@ export class RequestQueue {
|
|
|
92
91
|
/**
|
|
93
92
|
* @internal
|
|
94
93
|
*/
|
|
95
|
-
constructor(options
|
|
96
|
-
this.config = config;
|
|
94
|
+
constructor(options) {
|
|
97
95
|
this.id = options.metadata.id;
|
|
98
96
|
this.name = options.metadata.name;
|
|
99
97
|
this.events = serviceLocator.getEventManager();
|
|
@@ -146,8 +144,8 @@ export class RequestQueue {
|
|
|
146
144
|
}));
|
|
147
145
|
const { forefront = false } = options;
|
|
148
146
|
if ('requestsFromUrl' in requestLike) {
|
|
149
|
-
const requests = await this.
|
|
150
|
-
const processedRequests = await this.
|
|
147
|
+
const requests = await this.fetchRequestsFromUrl(requestLike);
|
|
148
|
+
const processedRequests = await this.addFetchedRequests(requestLike, requests, options);
|
|
151
149
|
return { ...processedRequests[0], forefront };
|
|
152
150
|
}
|
|
153
151
|
ow(requestLike, ow.object.partialShape({
|
|
@@ -176,7 +174,7 @@ export class RequestQueue {
|
|
|
176
174
|
uniqueKey: request.uniqueKey,
|
|
177
175
|
forefront,
|
|
178
176
|
};
|
|
179
|
-
this.
|
|
177
|
+
this.cacheRequest(cacheKey, queueOperationInfo);
|
|
180
178
|
this.requestSeenCache.add(cacheKey, request.id);
|
|
181
179
|
return queueOperationInfo;
|
|
182
180
|
}
|
|
@@ -223,8 +221,8 @@ export class RequestQueue {
|
|
|
223
221
|
requests.push(new Request({ url: requestLike }));
|
|
224
222
|
}
|
|
225
223
|
else if ('requestsFromUrl' in requestLike) {
|
|
226
|
-
const fetchedRequests = await this.
|
|
227
|
-
await this.
|
|
224
|
+
const fetchedRequests = await this.fetchRequestsFromUrl(requestLike);
|
|
225
|
+
await this.addFetchedRequests(requestLike, fetchedRequests, options);
|
|
228
226
|
}
|
|
229
227
|
else {
|
|
230
228
|
requests.push(requestLike instanceof Request ? requestLike : new Request(requestLike));
|
|
@@ -264,7 +262,7 @@ export class RequestQueue {
|
|
|
264
262
|
results.processedRequests.push(newRequest);
|
|
265
263
|
const cacheKey = getCachedRequestId(newRequest.uniqueKey);
|
|
266
264
|
if (cache) {
|
|
267
|
-
this.
|
|
265
|
+
this.cacheRequest(cacheKey, { ...newRequest, forefront });
|
|
268
266
|
}
|
|
269
267
|
// Unlike `requestCache`, populate this on every batch (including background ones).
|
|
270
268
|
this.requestSeenCache.add(cacheKey, newRequest.requestId);
|
|
@@ -474,7 +472,7 @@ export class RequestQueue {
|
|
|
474
472
|
uniqueKey: request.uniqueKey,
|
|
475
473
|
forefront,
|
|
476
474
|
};
|
|
477
|
-
this.
|
|
475
|
+
this.cacheRequest(getRequestId(request.uniqueKey), queueOperationInfo);
|
|
478
476
|
return queueOperationInfo;
|
|
479
477
|
}
|
|
480
478
|
/**
|
|
@@ -504,7 +502,7 @@ export class RequestQueue {
|
|
|
504
502
|
uniqueKey: request.uniqueKey,
|
|
505
503
|
forefront,
|
|
506
504
|
};
|
|
507
|
-
this.
|
|
505
|
+
this.cacheRequest(getRequestId(request.uniqueKey), queueOperationInfo);
|
|
508
506
|
return queueOperationInfo;
|
|
509
507
|
}
|
|
510
508
|
/**
|
|
@@ -557,7 +555,7 @@ export class RequestQueue {
|
|
|
557
555
|
/**
|
|
558
556
|
* Caches information about request to beware of unneeded addRequest() calls.
|
|
559
557
|
*/
|
|
560
|
-
|
|
558
|
+
cacheRequest(cacheKey, queueOperationInfo) {
|
|
561
559
|
// Remove the previous entry, as otherwise our cache will never update 👀
|
|
562
560
|
this.requestCache.remove(cacheKey);
|
|
563
561
|
this.requestCache.add(cacheKey, {
|
|
@@ -646,7 +644,7 @@ export class RequestQueue {
|
|
|
646
644
|
/**
|
|
647
645
|
* Fetches URLs from requestsFromUrl and returns them in format of list of requests
|
|
648
646
|
*/
|
|
649
|
-
async
|
|
647
|
+
async fetchRequestsFromUrl(source) {
|
|
650
648
|
const { requestsFromUrl, regex, ...sharedOpts } = source;
|
|
651
649
|
// Download remote resource and parse URLs.
|
|
652
650
|
let urlsArr;
|
|
@@ -654,7 +652,7 @@ export class RequestQueue {
|
|
|
654
652
|
urlsArr = await this._downloadListOfUrls({
|
|
655
653
|
url: requestsFromUrl,
|
|
656
654
|
urlRegExp: regex,
|
|
657
|
-
proxyUrl: await this.proxyConfiguration?.
|
|
655
|
+
proxyUrl: (await this.proxyConfiguration?.newProxyInfo())?.url,
|
|
658
656
|
});
|
|
659
657
|
}
|
|
660
658
|
catch (err) {
|
|
@@ -670,7 +668,7 @@ export class RequestQueue {
|
|
|
670
668
|
/**
|
|
671
669
|
* Adds all fetched requests from a URL from a remote resource.
|
|
672
670
|
*/
|
|
673
|
-
async
|
|
671
|
+
async addFetchedRequests(source, fetchedRequests, options) {
|
|
674
672
|
const { requestsFromUrl, regex } = source;
|
|
675
673
|
const { addedRequests } = await this.addRequestsBatched(fetchedRequests, options);
|
|
676
674
|
this.log.info('Fetched and loaded Requests from a remote resource.', {
|
package/storages/utils.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BaseHttpClient, Dictionary, StorageBackend } from '@crawlee/types';
|
|
2
2
|
import { Configuration } from '../configuration.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { IProxyConfiguration } from '../proxy_configuration.js';
|
|
4
4
|
/**
|
|
5
5
|
* Options for purging default storage.
|
|
6
6
|
*/
|
|
@@ -129,7 +129,7 @@ export interface StorageOpenOptions {
|
|
|
129
129
|
* Takes advantage of the internal address rotation and authentication process.
|
|
130
130
|
* If undefined, the `requestsFromUrl` requests will be made without proxy.
|
|
131
131
|
*/
|
|
132
|
-
proxyConfiguration?:
|
|
132
|
+
proxyConfiguration?: IProxyConfiguration;
|
|
133
133
|
/**
|
|
134
134
|
* HTTP client to be used to download the list of URLs in `RequestQueue`.
|
|
135
135
|
*/
|
package/validators.js
CHANGED
|
@@ -7,8 +7,8 @@ export const validators = {
|
|
|
7
7
|
message: (label) => `Expected argument '${label}' to be a Puppeteer Page, got something else.`,
|
|
8
8
|
}),
|
|
9
9
|
proxyConfiguration: (value) => ({
|
|
10
|
-
validator: ow.isValid(value, ow.object.hasKeys('
|
|
11
|
-
message: (label) => `Expected argument '${label}' to
|
|
10
|
+
validator: ow.isValid(value, ow.object.hasKeys('newProxyInfo')),
|
|
11
|
+
message: (label) => `Expected argument '${label}' to implement the IProxyConfiguration interface (missing 'newProxyInfo'), got something else.`,
|
|
12
12
|
}),
|
|
13
13
|
requestList: (value) => ({
|
|
14
14
|
validator: ow.isValid(value, ow.object.hasKeys('fetchNextRequest', 'persistState')),
|