n8n-nodes-crawl4ai
Version:
n8n nodes for Crawl4AI web crawler and data extraction
274 lines • 10.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.description = void 0;
exports.execute = execute;
const n8n_workflow_1 = require("n8n-workflow");
const utils_1 = require("../helpers/utils");
const formatters_1 = require("../helpers/formatters");
exports.description = [
{
displayName: 'URLs',
name: 'urls',
type: 'string',
required: true,
default: '',
placeholder: 'https://example.com, https://example.org',
description: 'Comma-separated list of URLs to crawl',
displayOptions: {
show: {
operation: ['crawlMultipleUrls'],
},
},
},
{
displayName: 'Browser Options',
name: 'browserOptions',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: ['crawlMultipleUrls'],
},
},
options: [
{
displayName: 'Enable JavaScript',
name: 'javaScriptEnabled',
type: 'boolean',
default: true,
description: 'Whether to enable JavaScript execution',
},
{
displayName: 'Headless Mode',
name: 'headless',
type: 'boolean',
default: true,
description: 'Whether to run browser in headless mode',
},
{
displayName: 'Timeout (Ms)',
name: 'timeout',
type: 'number',
default: 30000,
description: 'Maximum time to wait for the browser to load the page',
},
{
displayName: 'User Agent',
name: 'userAgent',
type: 'string',
default: '',
placeholder: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...',
description: 'The user agent to use (leave empty for default)',
},
{
displayName: 'Viewport Height',
name: 'viewportHeight',
type: 'number',
default: 800,
description: 'The height of the browser viewport',
},
{
displayName: 'Viewport Width',
name: 'viewportWidth',
type: 'number',
default: 1280,
description: 'The width of the browser viewport',
},
],
},
{
displayName: 'Crawler Options',
name: 'crawlerOptions',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: ['crawlMultipleUrls'],
},
},
options: [
{
displayName: 'Cache Mode',
name: 'cacheMode',
type: 'options',
options: [
{
name: 'Enabled (Read/Write)',
value: 'enabled',
description: 'Use cache if available, save new results to cache',
},
{
name: 'Bypass (Force Fresh)',
value: 'bypass',
description: 'Ignore cache, always fetch fresh content',
},
{
name: 'Only (Read Only)',
value: 'only',
description: 'Only use cache, do not make new requests',
},
],
default: 'enabled',
description: 'How to use the cache when crawling',
},
{
displayName: 'Check Robots.txt',
name: 'checkRobotsTxt',
type: 'boolean',
default: false,
description: 'Whether to respect robots.txt rules',
},
{
displayName: 'CSS Selector',
name: 'cssSelector',
type: 'string',
default: '',
placeholder: 'article.content',
description: 'CSS selector to focus on specific content (leave empty for full page)',
},
{
displayName: 'Exclude External Links',
name: 'excludeExternalLinks',
type: 'boolean',
default: false,
description: 'Whether to exclude external links from the result',
},
{
displayName: 'Excluded Tags',
name: 'excludedTags',
type: 'string',
default: '',
placeholder: 'nav,footer,aside',
description: 'Comma-separated list of HTML tags to exclude from processing',
},
{
displayName: 'Max Retries',
name: 'maxRetries',
type: 'number',
default: 3,
description: 'Maximum number of retries for failed requests',
},
{
displayName: 'Page Timeout (Ms)',
name: 'pageTimeout',
type: 'number',
default: 30000,
description: 'Maximum time to wait for the page to load',
},
{
displayName: 'Request Timeout (Ms)',
name: 'requestTimeout',
type: 'number',
default: 30000,
description: 'Maximum time to wait for network requests',
},
{
displayName: 'Stream Results',
name: 'streamEnabled',
type: 'boolean',
default: false,
description: 'Whether to stream results as they become available',
},
{
displayName: 'Word Count Threshold',
name: 'wordCountThreshold',
type: 'number',
default: 0,
description: 'Minimum number of words for content to be included',
},
],
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
displayOptions: {
show: {
operation: ['crawlMultipleUrls'],
},
},
options: [
{
displayName: 'Include Media Data',
name: 'includeMedia',
type: 'boolean',
default: false,
description: 'Whether to include media data in output (images, videos)',
},
{
displayName: 'Verbose Response',
name: 'verboseResponse',
type: 'boolean',
default: false,
description: 'Whether to include detailed data in output (HTML, status codes, etc.)',
},
{
displayName: 'Max Concurrent Crawls',
name: 'maxConcurrent',
type: 'number',
default: 5,
description: 'Maximum number of concurrent crawls',
},
],
},
];
async function execute(items, nodeOptions) {
var _a;
const allResults = [];
for (let i = 0; i < items.length; i++) {
try {
const urlsString = this.getNodeParameter('urls', i, '');
const browserOptions = this.getNodeParameter('browserOptions', i, {});
const crawlerOptions = this.getNodeParameter('crawlerOptions', i, {});
const options = this.getNodeParameter('options', i, {});
if (!urlsString) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'URLs cannot be empty.', { itemIndex: i });
}
const urls = urlsString
.split(',')
.map(url => url.trim())
.filter(url => url);
if (urls.length === 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No valid URLs provided.', { itemIndex: i });
}
const invalidUrls = urls.filter(url => !(0, utils_1.isValidUrl)(url));
if (invalidUrls.length > 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid URLs: ${invalidUrls.join(', ')}`, { itemIndex: i });
}
const browserConfig = (0, utils_1.createBrowserConfig)(browserOptions);
const crawlerConfig = (0, utils_1.createCrawlerRunConfig)({
...crawlerOptions,
...browserConfig,
maxConcurrent: options.maxConcurrent ? Number(options.maxConcurrent) : 5,
});
const crawler = await (0, utils_1.getCrawl4aiClient)(this);
const results = await crawler.crawlMultipleUrls(urls, crawlerConfig);
for (const result of results) {
const formattedResult = (0, formatters_1.formatCrawlResult)(result, options.includeMedia, options.verboseResponse);
allResults.push({
json: formattedResult,
pairedItem: { item: i },
});
}
}
catch (error) {
if (this.continueOnFail()) {
const node = this.getNode();
const errorItemIndex = (_a = error.itemIndex) !== null && _a !== void 0 ? _a : i;
allResults.push({
json: items[i].json,
error: new n8n_workflow_1.NodeOperationError(node, error.message, { itemIndex: errorItemIndex }),
pairedItem: { item: i },
});
continue;
}
throw error;
}
}
return allResults;
}
//# sourceMappingURL=crawlMultipleUrls.operation.js.map