@crawlee/linkedom 4.0.0-beta.121 → 4.0.0-beta.123
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/internals/linkedom-crawler.d.ts +10 -27
- package/internals/linkedom-crawler.js +21 -46
- package/package.json +5 -6
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AddRequestsBatchedResult, CrawlingContext, EnqueueLinksOptions, ErrorHandler, ExtractLinksOptions, GetUserDataFromRequest, HttpCrawlerOptions, InternalHttpCrawlingContext, InternalHttpHook, RequestHandler, RouterHandler, RouterRoutes, RouteSchemas, RoutesFromSchemas } from '@crawlee/http';
|
|
2
2
|
import { HttpCrawler } from '@crawlee/http';
|
|
3
|
-
import type {
|
|
3
|
+
import type { Dictionary } from '@crawlee/types';
|
|
4
4
|
import { type CheerioRoot } from '@crawlee/utils/internal';
|
|
5
|
-
import { type RobotsTxtFile } from '@crawlee/utils';
|
|
6
5
|
import * as cheerio from 'cheerio';
|
|
7
6
|
export type LinkeDOMErrorHandler<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
8
7
|
JSONData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
@@ -11,8 +10,6 @@ export interface LinkeDOMCrawlerOptions<ContextExtension = Dictionary<never>, Ex
|
|
|
11
10
|
JSONData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
12
11
|
Routes extends Record<keyof Routes, Dictionary> = Record<string, UserData>> extends HttpCrawlerOptions<LinkeDOMCrawlingContext<UserData, JSONData>, ContextExtension, ExtendedContext, Routes> {
|
|
13
12
|
}
|
|
14
|
-
export interface LinkeDOMCrawlerEnqueueLinksOptions extends Omit<EnqueueLinksOptions, 'urls' | 'requestManager'> {
|
|
15
|
-
}
|
|
16
13
|
export type LinkeDOMHook<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
17
14
|
JSONData extends Dictionary = any> = InternalHttpHook<LinkeDOMCrawlingContext<UserData, JSONData>>;
|
|
18
15
|
export interface LinkeDOMCrawlingContext<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
@@ -47,9 +44,13 @@ JSONData extends Dictionary = any> extends InternalHttpCrawlingContext<UserData,
|
|
|
47
44
|
*/
|
|
48
45
|
parseWithCheerio(selector?: string, timeoutMs?: number): Promise<CheerioRoot>;
|
|
49
46
|
/**
|
|
50
|
-
*
|
|
47
|
+
* Extracts URLs from the parsed DOM, without adding them to the request queue.
|
|
48
|
+
*/
|
|
49
|
+
extractLinks(options?: ExtractLinksOptions): Promise<string[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Helper function for extracting URLs from the parsed DOM and adding them to the request queue.
|
|
51
52
|
*/
|
|
52
|
-
enqueueLinks(options?:
|
|
53
|
+
enqueueLinks(options?: EnqueueLinksOptions): Promise<AddRequestsBatchedResult>;
|
|
53
54
|
}
|
|
54
55
|
export type LinkeDOMRequestHandler<UserData extends Dictionary = any, // with default to Dictionary we cant use a typed router in untyped crawler
|
|
55
56
|
JSONData extends Dictionary = any> = RequestHandler<LinkeDOMCrawlingContext<UserData, JSONData>>;
|
|
@@ -134,31 +135,14 @@ export declare class LinkeDOMCrawler<ContextExtension = Dictionary<never>, Exten
|
|
|
134
135
|
readonly body: string;
|
|
135
136
|
readonly document: Document;
|
|
136
137
|
} & {
|
|
137
|
-
|
|
138
|
+
extractLinks: (options?: ExtractLinksOptions) => Promise<string[]>;
|
|
139
|
+
enqueueLinks: (options?: EnqueueLinksOptions) => Promise<AddRequestsBatchedResult>;
|
|
138
140
|
waitForSelector(selector: string, timeoutMs?: number): Promise<void>;
|
|
139
141
|
parseWithCheerio(selector?: string, _timeoutMs?: number): Promise<cheerio.CheerioAPI>;
|
|
140
142
|
}>;
|
|
141
143
|
private parseContent;
|
|
142
144
|
private addHelpers;
|
|
143
145
|
}
|
|
144
|
-
interface EnqueueLinksInternalOptions {
|
|
145
|
-
options?: EnqueueLinksOptions;
|
|
146
|
-
window: Window | null;
|
|
147
|
-
requestManager: IRequestManager;
|
|
148
|
-
robotsTxtFile?: RobotsTxtFile;
|
|
149
|
-
onSkippedRequest?: SkippedRequestCallback;
|
|
150
|
-
originalRequestUrl: string;
|
|
151
|
-
finalRequestUrl?: string;
|
|
152
|
-
}
|
|
153
|
-
interface BoundEnqueueLinksInternalOptions {
|
|
154
|
-
enqueueLinks: BasicCrawlingContext['enqueueLinks'];
|
|
155
|
-
options?: EnqueueLinksOptions;
|
|
156
|
-
window: Window | null;
|
|
157
|
-
originalRequestUrl: string;
|
|
158
|
-
finalRequestUrl?: string;
|
|
159
|
-
}
|
|
160
|
-
/** @internal */
|
|
161
|
-
export declare function linkedomCrawlerEnqueueLinks(options: EnqueueLinksInternalOptions | BoundEnqueueLinksInternalOptions): Promise<unknown>;
|
|
162
146
|
/**
|
|
163
147
|
* Creates new {@link Router} instance that works based on request labels.
|
|
164
148
|
* This instance can then serve as a `requestHandler` of your {@link LinkeDOMCrawler}.
|
|
@@ -186,4 +170,3 @@ export declare function linkedomCrawlerEnqueueLinks(options: EnqueueLinksInterna
|
|
|
186
170
|
export declare function createLinkeDOMRouter<Context extends LinkeDOMCrawlingContext = LinkeDOMCrawlingContext, Routes extends Record<keyof Routes, Dictionary> = Record<string, GetUserDataFromRequest<Context['request']>>>(routes?: RouterRoutes<Context, Routes>): RouterHandler<Context, Routes>;
|
|
187
171
|
export declare function createLinkeDOMRouter<Context extends LinkeDOMCrawlingContext = LinkeDOMCrawlingContext, UserData extends Dictionary = GetUserDataFromRequest<Context['request']>>(routes?: RouterRoutes<Context, Record<string, UserData>>): RouterHandler<Context, Record<string, UserData>>;
|
|
188
172
|
export declare function createLinkeDOMRouter<Context extends LinkeDOMCrawlingContext = LinkeDOMCrawlingContext, const Schemas extends RouteSchemas = RouteSchemas>(schemas: Schemas): RouterHandler<Context, RoutesFromSchemas<Schemas>>;
|
|
189
|
-
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnqueueStrategy, HttpCrawler, NavigationSkippedError, resolveBaseUrlForEnqueueLinksFiltering, Router, tryAbsoluteURL, } from '@crawlee/http';
|
|
2
2
|
import { sleep } from '@crawlee/utils';
|
|
3
3
|
import * as cheerio from 'cheerio';
|
|
4
4
|
import { DOMParser } from 'linkedom/cached';
|
|
@@ -124,20 +124,28 @@ export class LinkeDOMCrawler extends HttpCrawler {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
async addHelpers(crawlingContext) {
|
|
127
|
+
const addRequests = crawlingContext.addRequests;
|
|
128
|
+
const extractLinks = async (options) => {
|
|
129
|
+
if (!crawlingContext.window) {
|
|
130
|
+
throw new Error('Cannot extract links because the DOM is not available.');
|
|
131
|
+
}
|
|
132
|
+
return extractUrlsFromWindow(crawlingContext.window, options?.selector ?? 'a', options?.baseUrl ?? crawlingContext.request.loadedUrl ?? crawlingContext.request.url);
|
|
133
|
+
};
|
|
127
134
|
return {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
limit: await this.calculateEnqueuedRequestLimit(enqueueOptions?.limit),
|
|
133
|
-
},
|
|
134
|
-
window: crawlingContext.window,
|
|
135
|
-
requestManager: await this.getRequestManager(),
|
|
136
|
-
robotsTxtFile: await this.getRobotsTxtFileForUrl(crawlingContext.request.url),
|
|
137
|
-
onSkippedRequest: this.handleSkippedRequest,
|
|
138
|
-
originalRequestUrl: crawlingContext.request.url,
|
|
135
|
+
extractLinks,
|
|
136
|
+
enqueueLinks: async (options = {}) => {
|
|
137
|
+
const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
|
|
138
|
+
enqueueStrategy: options.strategy,
|
|
139
139
|
finalRequestUrl: crawlingContext.request.loadedUrl,
|
|
140
|
-
|
|
140
|
+
originalRequestUrl: crawlingContext.request.url,
|
|
141
|
+
userProvidedBaseUrl: options.baseUrl,
|
|
142
|
+
});
|
|
143
|
+
const urls = await extractLinks(options);
|
|
144
|
+
return addRequests(urls, {
|
|
145
|
+
...options,
|
|
146
|
+
baseUrl,
|
|
147
|
+
strategy: options.strategy ?? EnqueueStrategy.SameHostname,
|
|
148
|
+
});
|
|
141
149
|
},
|
|
142
150
|
async waitForSelector(selector, timeoutMs = 5_000) {
|
|
143
151
|
const $ = cheerio.load(crawlingContext.body);
|
|
@@ -160,39 +168,6 @@ export class LinkeDOMCrawler extends HttpCrawler {
|
|
|
160
168
|
};
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
|
-
/** @internal */
|
|
164
|
-
function containsEnqueueLinks(options) {
|
|
165
|
-
return !!options.enqueueLinks;
|
|
166
|
-
}
|
|
167
|
-
/** @internal */
|
|
168
|
-
export async function linkedomCrawlerEnqueueLinks(options) {
|
|
169
|
-
const { options: enqueueLinksOptions, window, originalRequestUrl, finalRequestUrl } = options;
|
|
170
|
-
if (!window) {
|
|
171
|
-
throw new Error('Cannot enqueue links because the DOM is not available.');
|
|
172
|
-
}
|
|
173
|
-
const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
|
|
174
|
-
enqueueStrategy: enqueueLinksOptions?.strategy,
|
|
175
|
-
finalRequestUrl,
|
|
176
|
-
originalRequestUrl,
|
|
177
|
-
userProvidedBaseUrl: enqueueLinksOptions?.baseUrl,
|
|
178
|
-
});
|
|
179
|
-
const urls = extractUrlsFromWindow(window, enqueueLinksOptions?.selector ?? 'a', enqueueLinksOptions?.baseUrl ?? finalRequestUrl ?? originalRequestUrl);
|
|
180
|
-
if (containsEnqueueLinks(options)) {
|
|
181
|
-
return options.enqueueLinks({
|
|
182
|
-
urls,
|
|
183
|
-
baseUrl,
|
|
184
|
-
...enqueueLinksOptions,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
return enqueueLinks({
|
|
188
|
-
requestManager: options.requestManager,
|
|
189
|
-
robotsTxtFile: options.robotsTxtFile,
|
|
190
|
-
onSkippedRequest: options.onSkippedRequest,
|
|
191
|
-
urls,
|
|
192
|
-
baseUrl,
|
|
193
|
-
...enqueueLinksOptions,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
171
|
/**
|
|
197
172
|
* Extracts URLs from a given Window object.
|
|
198
173
|
* @ignore
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawlee/linkedom",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.123",
|
|
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": ">=22.0.0"
|
|
@@ -49,12 +49,11 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@apify/timeout": "^0.4.4",
|
|
51
51
|
"@apify/utilities": "^2.15.5",
|
|
52
|
-
"@crawlee/http": "4.0.0-beta.
|
|
53
|
-
"@crawlee/types": "4.0.0-beta.
|
|
54
|
-
"@crawlee/utils": "4.0.0-beta.
|
|
52
|
+
"@crawlee/http": "4.0.0-beta.123",
|
|
53
|
+
"@crawlee/types": "4.0.0-beta.123",
|
|
54
|
+
"@crawlee/utils": "4.0.0-beta.123",
|
|
55
55
|
"cheerio": "^1.0.0",
|
|
56
56
|
"linkedom": "^0.18.10",
|
|
57
|
-
"ow": "^2.0.0",
|
|
58
57
|
"tslib": "^2.8.1"
|
|
59
58
|
},
|
|
60
59
|
"lerna": {
|
|
@@ -64,5 +63,5 @@
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
},
|
|
67
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "f77648095c6a3f5ed8815c7620ea765db430ae44"
|
|
68
67
|
}
|