@crawlee/linkedom 4.0.0-beta.122 → 4.0.0-beta.124

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,8 +1,7 @@
1
- import type { BasicCrawlingContext, CrawlingContext, EnqueueLinksOptions, ErrorHandler, GetUserDataFromRequest, HttpCrawlerOptions, InternalHttpCrawlingContext, InternalHttpHook, IRequestManager, RequestHandler, RouterHandler, RouterRoutes, RouteSchemas, RoutesFromSchemas, SkippedRequestCallback } from '@crawlee/http';
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 { BatchAddRequestsResult, Dictionary } from '@crawlee/types';
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
- * Helper function for extracting URLs from the parsed HTML and adding them to the request queue.
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?: LinkeDOMCrawlerEnqueueLinksOptions): Promise<BatchAddRequestsResult>;
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
- enqueueLinks: (enqueueOptions?: LinkeDOMCrawlerEnqueueLinksOptions) => Promise<BatchAddRequestsResult>;
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 { enqueueLinks, HttpCrawler, NavigationSkippedError, resolveBaseUrlForEnqueueLinksFiltering, Router, tryAbsoluteURL, } from '@crawlee/http';
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
- enqueueLinks: async (enqueueOptions) => {
129
- return (await linkedomCrawlerEnqueueLinks({
130
- options: {
131
- ...enqueueOptions,
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
- })); // TODO make this type safe, see https://github.com/apify/crawlee/issues/4024
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.122",
3
+ "version": "4.0.0-beta.124",
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,9 +49,9 @@
49
49
  "dependencies": {
50
50
  "@apify/timeout": "^0.4.4",
51
51
  "@apify/utilities": "^2.15.5",
52
- "@crawlee/http": "4.0.0-beta.122",
53
- "@crawlee/types": "4.0.0-beta.122",
54
- "@crawlee/utils": "4.0.0-beta.122",
52
+ "@crawlee/http": "4.0.0-beta.124",
53
+ "@crawlee/types": "4.0.0-beta.124",
54
+ "@crawlee/utils": "4.0.0-beta.124",
55
55
  "cheerio": "^1.0.0",
56
56
  "linkedom": "^0.18.10",
57
57
  "tslib": "^2.8.1"
@@ -63,5 +63,5 @@
63
63
  }
64
64
  }
65
65
  },
66
- "gitHead": "2c3e87fefdb9e1fca4c144f03167d8524cfdc2e5"
66
+ "gitHead": "0694ee1b94c755b98141671baa93cc363f2bf8e3"
67
67
  }