first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,17 @@
{
"node": "n8n-nodes-base.html",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Core Nodes"],
"resources": {
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.html/"
}
]
},
"subcategories": {
"Core Nodes": ["Data Transformation"]
},
"alias": ["extract", "template", "table"]
}
+615
View File
@@ -0,0 +1,615 @@
import cheerio from 'cheerio';
import get from 'lodash/get';
import type {
INodeExecutionData,
IExecuteFunctions,
INodeType,
INodeTypeDescription,
IDataObject,
INodeProperties,
} from 'n8n-workflow';
import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
import { getResolvables, sanitizeDataPathKey } from '@utils/utilities';
import { placeholder } from './placeholder';
import type { IValueData } from './types';
import { getValue } from './utils';
export const capitalizeHeader = (header: string, capitalize?: boolean) => {
if (!capitalize) return header;
return header
.split('_')
.filter((word) => word)
.map((word) => word[0].toUpperCase() + word.slice(1))
.join(' ');
};
const extractionValuesCollection: INodeProperties = {
displayName: 'Extraction Values',
name: 'extractionValues',
placeholder: 'Add Value',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
options: [
{
name: 'values',
displayName: 'Values',
values: [
{
displayName: 'Key',
name: 'key',
type: 'string',
default: '',
description: 'The key under which the extracted value should be saved',
},
{
displayName: 'CSS Selector',
name: 'cssSelector',
type: 'string',
default: '',
placeholder: '.price',
description: 'The CSS selector to use',
},
{
displayName: 'Return Value',
name: 'returnValue',
type: 'options',
options: [
{
name: 'Attribute',
value: 'attribute',
description: 'Get an attribute value like "class" from an element',
},
{
name: 'HTML',
value: 'html',
description: 'Get the HTML the element contains',
},
{
name: 'Text',
value: 'text',
description: 'Get only the text content of the element',
},
{
name: 'Value',
value: 'value',
description: 'Get value of an input, select or textarea',
},
],
default: 'text',
description: 'What kind of data should be returned',
},
{
displayName: 'Attribute',
name: 'attribute',
type: 'string',
displayOptions: {
show: {
returnValue: ['attribute'],
},
},
default: '',
placeholder: 'class',
description: 'The name of the attribute to return the value off',
},
{
displayName: 'Skip Selectors',
name: 'skipSelectors',
type: 'string',
displayOptions: {
show: {
returnValue: ['text'],
'@version': [{ _cnd: { gt: 1.1 } }],
},
},
default: '',
placeholder: 'e.g. img, .className, #ItemId',
description: 'Comma-separated list of selectors to skip in the text extraction',
},
{
displayName: 'Return Array',
name: 'returnArray',
type: 'boolean',
default: false,
description:
'Whether to return the values as an array so if multiple ones get found they also get returned separately. If not set all will be returned as a single string.',
},
],
},
],
};
export class Html implements INodeType {
description: INodeTypeDescription = {
displayName: 'HTML',
name: 'html',
icon: { light: 'file:html.svg', dark: 'file:html.dark.svg' },
group: ['transform'],
version: [1, 1.1, 1.2],
subtitle: '={{ $parameter["operation"] }}',
description: 'Work with HTML',
defaults: {
name: 'HTML',
},
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
parameterPane: 'wide',
properties: [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Generate HTML Template',
value: 'generateHtmlTemplate',
action: 'Generate HTML template',
},
{
name: 'Extract HTML Content',
value: 'extractHtmlContent',
action: 'Extract HTML Content',
},
{
name: 'Convert to HTML Table',
value: 'convertToHtmlTable',
action: 'Convert to HTML Table',
},
],
default: 'generateHtmlTemplate',
},
{
displayName: 'HTML Template',
name: 'html',
typeOptions: {
editor: 'htmlEditor',
},
type: 'string',
default: placeholder,
noDataExpression: true,
description: 'HTML template to render',
builderHint: {
message:
'Use expressions to generate loops, reference data, etc. Does not support handlebars.',
},
displayOptions: {
show: {
operation: ['generateHtmlTemplate'],
},
},
},
{
displayName:
'<b>Tips</b>: Type ctrl+space for completions. Use <code>{{ }}</code> for expressions and <code>&lt;style&gt;</code> tags for CSS. JS in <code>&lt;script&gt;</code> tags is included but not executed in n8n.',
name: 'notice',
type: 'notice',
default: '',
displayOptions: {
show: {
operation: ['generateHtmlTemplate'],
},
},
},
{
displayName: 'Source Data',
name: 'sourceData',
type: 'options',
options: [
{
name: 'Binary',
value: 'binary',
},
{
name: 'JSON',
value: 'json',
},
],
default: 'json',
description: 'If HTML should be read from binary or JSON data',
displayOptions: {
show: {
operation: ['extractHtmlContent'],
},
},
},
{
displayName: 'Input Binary Field',
name: 'dataPropertyName',
type: 'string',
requiresDataPath: 'single',
displayOptions: {
show: {
operation: ['extractHtmlContent'],
sourceData: ['binary'],
},
},
default: 'data',
required: true,
hint: 'The name of the input binary field containing the file to be extracted',
},
{
displayName: 'JSON Property',
name: 'dataPropertyName',
type: 'string',
requiresDataPath: 'single',
displayOptions: {
show: {
operation: ['extractHtmlContent'],
sourceData: ['json'],
},
},
default: 'data',
required: true,
description:
'Name of the JSON property in which the HTML to extract the data from can be found. The property can either contain a string or an array of strings.',
},
{
...extractionValuesCollection,
displayOptions: {
show: {
operation: ['extractHtmlContent'],
'@version': [1],
},
},
},
{
...extractionValuesCollection,
default: {
values: [
{
key: '',
cssSelector: '',
returnValue: 'text',
returnArray: false,
},
],
},
displayOptions: {
show: {
operation: ['extractHtmlContent'],
'@version': [{ _cnd: { gt: 1 } }],
},
},
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['extractHtmlContent'],
},
},
options: [
{
displayName: 'Trim Values',
name: 'trimValues',
type: 'boolean',
default: true,
description:
'Whether to remove automatically all spaces and newlines from the beginning and end of the values',
},
{
displayName: 'Clean Up Text',
name: 'cleanUpText',
type: 'boolean',
default: true,
description:
'Whether to remove leading and trailing whitespaces, line breaks (newlines) and condense multiple consecutive whitespaces into a single space',
},
],
},
// ----------------------------------
// convertToHtmlTable
// ----------------------------------
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add option',
default: {},
displayOptions: {
show: {
operation: ['convertToHtmlTable'],
},
},
options: [
{
displayName: 'Capitalize Headers',
name: 'capitalize',
type: 'boolean',
default: false,
description: 'Whether to capitalize the headers',
},
{
displayName: 'Custom Styling',
name: 'customStyling',
type: 'boolean',
default: false,
description: 'Whether to use custom styling',
},
{
displayName: 'Caption',
name: 'caption',
type: 'string',
default: '',
description: 'Caption to add to the table',
},
{
displayName: 'Table Attributes',
name: 'tableAttributes',
type: 'string',
default: '',
description: 'Attributes to attach to the table',
placeholder: 'e.g. style="padding:10px"',
},
{
displayName: 'Header Attributes',
name: 'headerAttributes',
type: 'string',
default: '',
description: 'Attributes to attach to the table header',
placeholder: 'e.g. style="padding:10px"',
},
{
displayName: 'Row Attributes',
name: 'rowAttributes',
type: 'string',
default: '',
description: 'Attributes to attach to the table row',
placeholder: 'e.g. style="padding:10px"',
},
{
displayName: 'Cell Attributes',
name: 'cellAttributes',
type: 'string',
default: '',
description: 'Attributes to attach to the table cell',
placeholder: 'e.g. style="padding:10px"',
},
],
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const operation = this.getNodeParameter('operation', 0);
const nodeVersion = this.getNode().typeVersion;
if (operation === 'convertToHtmlTable' && items.length) {
let table = '';
const options = this.getNodeParameter('options', 0);
let tableStyle = '';
let headerStyle = '';
let cellStyle = '';
if (!options.customStyling) {
tableStyle = "style='border-spacing:0; font-family:helvetica,arial,sans-serif'";
headerStyle =
"style='margin:0; padding:7px 20px 7px 0px; border-bottom:1px solid #eee; text-align:left; color:#888; font-weight:normal'";
cellStyle = "style='margin:0; padding:7px 20px 7px 0px; border-bottom:1px solid #eee'";
}
const tableAttributes = (options.tableAttributes as string) || '';
const headerAttributes = (options.headerAttributes as string) || '';
const itemsData: IDataObject[] = [];
const itemsKeys = new Set<string>();
for (const entry of items) {
itemsData.push(entry.json);
for (const key of Object.keys(entry.json)) {
itemsKeys.add(key);
}
}
const headers = Array.from(itemsKeys);
table += `<table ${tableStyle} ${tableAttributes}>`;
if (options.caption) {
table += `<caption>${options.caption}</caption>`;
}
table += `<thead ${headerStyle} ${headerAttributes}>`;
table += '<tr>';
table += headers
.map((header) => '<th>' + capitalizeHeader(header, options.capitalize as boolean) + '</th>')
.join('');
table += '</tr>';
table += '</thead>';
table += '<tbody>';
itemsData.forEach((entry, entryIndex) => {
const rowsAttributes = this.getNodeParameter(
'options.rowAttributes',
entryIndex,
'',
) as string;
table += `<tr ${rowsAttributes}>`;
const cellsAttributes = this.getNodeParameter(
'options.cellAttributes',
entryIndex,
'',
) as string;
table += headers
.map((header) => {
let td = `<td ${cellStyle} ${cellsAttributes}>`;
if (typeof entry[header] === 'boolean') {
const isChecked = entry[header] ? 'checked="checked"' : '';
td += `<input type="checkbox" ${isChecked}/>`;
} else {
td += entry[header];
}
td += '</td>';
return td;
})
.join('');
table += '</tr>';
});
table += '</tbody>';
table += '</table>';
return [
[
{
json: { table },
pairedItem: items.map((_item, index) => ({
item: index,
})),
},
],
];
}
let item: INodeExecutionData;
const returnData: INodeExecutionData[] = [];
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
try {
if (operation === 'generateHtmlTemplate') {
// ----------------------------------
// generateHtmlTemplate
// ----------------------------------
let html = this.getNodeParameter('html', itemIndex) as string;
for (const resolvable of getResolvables(html)) {
html = html.replace(
resolvable,
this.evaluateExpression(resolvable, itemIndex) as string,
);
}
const result = this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray({ html }),
{
itemData: { item: itemIndex },
},
);
returnData.push(...result);
} else if (operation === 'extractHtmlContent') {
// ----------------------------------
// extractHtmlContent
// ----------------------------------
const dataPropertyName = this.getNodeParameter('dataPropertyName', itemIndex);
const extractionValues = this.getNodeParameter(
'extractionValues',
itemIndex,
) as IDataObject;
const options = this.getNodeParameter('options', itemIndex, {});
const sourceData = this.getNodeParameter('sourceData', itemIndex) as string;
item = items[itemIndex];
let htmlArray: string[] | string = [];
if (sourceData === 'json') {
if (nodeVersion === 1) {
const key = sanitizeDataPathKey(item.json, dataPropertyName);
if (item.json[key] === undefined) {
throw new NodeOperationError(
this.getNode(),
`No property named "${dataPropertyName}" exists!`,
{ itemIndex },
);
}
htmlArray = item.json[key] as string;
} else {
const value = get(item.json, dataPropertyName);
if (value === undefined) {
throw new NodeOperationError(
this.getNode(),
`No property named "${dataPropertyName}" exists!`,
{ itemIndex },
);
}
htmlArray = value as string;
}
} else {
this.helpers.assertBinaryData(itemIndex, dataPropertyName);
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(
itemIndex,
dataPropertyName,
);
htmlArray = binaryDataBuffer.toString('utf-8');
}
// Convert it always to array that it works with a string or an array of strings
if (!Array.isArray(htmlArray)) {
htmlArray = [htmlArray];
}
for (const html of htmlArray) {
const $ = cheerio.load(html);
const newItem: INodeExecutionData = {
json: {},
pairedItem: {
item: itemIndex,
},
};
// Iterate over all the defined values which should be extracted
let htmlElement;
for (const valueData of extractionValues.values as IValueData[]) {
htmlElement = $(valueData.cssSelector);
if (valueData.returnArray) {
// An array should be returned so iterate over one
// value at a time
newItem.json[valueData.key] = [];
htmlElement.each((_, el) => {
(newItem.json[valueData.key] as Array<string | undefined>).push(
getValue($(el), valueData, options, nodeVersion),
);
});
} else {
// One single value should be returned
newItem.json[valueData.key] = getValue(
htmlElement,
valueData,
options,
nodeVersion,
);
}
}
returnData.push(newItem);
}
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
pairedItem: {
item: itemIndex,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
}
@@ -0,0 +1,7 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.64062 0H10.4375V1.78125H12.0937V0H13.8906V5.39062H12.0937V3.59375H10.4531V5.39062H8.64062M16.2656 1.79687H14.6797V0H19.6562V1.79687H18.0625V5.39062H16.2656M20.4453 0H22.3281L23.4844 1.89844L24.6406 0H26.5234V5.39062H24.7266V2.71875L23.4687 4.65625L22.2109 2.71875V5.39062H20.4453M27.4141 0H29.2109V3.60937H31.7578V5.39062H27.4141" fill="white"/>
<path d="M8.57812 36.7969L6 7.85938H34.3437L31.7656 36.7812L20.1484 40" fill="#E44D26"/>
<path d="M20.1719 37.5391V10.2344H31.7578L29.5469 34.9219" fill="#F16529"/>
<path d="M11.2656 13.7734H20.1719V17.3203H15.1562L15.4844 20.9531H20.1719V24.4922H12.2344M12.3906 26.2734H15.9531L16.2031 29.1094L20.1719 30.1719V33.875L12.8906 31.8437" fill="#EBEBEB"/>
<path d="M29.0469 13.7734H20.1562V17.3203H28.7187M28.3984 20.9531H20.1562V24.5H24.5312L24.1172 29.1094L20.1562 30.1719V33.8594L27.4219 31.8437" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 972 B

+7
View File
@@ -0,0 +1,7 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.64062 0H10.4375V1.78125H12.0937V0H13.8906V5.39062H12.0937V3.59375H10.4531V5.39062H8.64062M16.2656 1.79687H14.6797V0H19.6562V1.79687H18.0625V5.39062H16.2656M20.4453 0H22.3281L23.4844 1.89844L24.6406 0H26.5234V5.39062H24.7266V2.71875L23.4687 4.65625L22.2109 2.71875V5.39062H20.4453M27.4141 0H29.2109V3.60937H31.7578V5.39062H27.4141" fill="black"/>
<path d="M8.57812 36.7969L6 7.85938H34.3437L31.7656 36.7812L20.1484 40" fill="#E44D26"/>
<path d="M20.1719 37.5391V10.2344H31.7578L29.5469 34.9219" fill="#F16529"/>
<path d="M11.2656 13.7734H20.1719V17.3203H15.1562L15.4844 20.9531H20.1719V24.4922H12.2344M12.3906 26.2734H15.9531L16.2031 29.1094L20.1719 30.1719V33.875L12.8906 31.8437" fill="#EBEBEB"/>
<path d="M29.0469 13.7734H20.1562V17.3203H28.7187M28.3984 20.9531H20.1562V24.5H24.5312L24.1172 29.1094L20.1562 30.1719V33.8594L27.4219 31.8437" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 972 B

@@ -0,0 +1,44 @@
export const placeholder = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>My HTML document</title>
</head>
<body>
<div class="container">
<h1>This is an H1 heading</h1>
<h2>This is an H2 heading</h2>
<p>This is a paragraph</p>
</div>
</body>
</html>
<style>
.container {
background-color: #ffffff;
text-align: center;
padding: 16px;
border-radius: 8px;
}
h1 {
color: #ff6d5a;
font-size: 24px;
font-weight: bold;
padding: 8px;
}
h2 {
color: #909399;
font-size: 18px;
font-weight: bold;
padding: 8px;
}
</style>
<script>
console.log("Hello World!");
</script>
`.trim();
@@ -0,0 +1,5 @@
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
describe('Test Html Node > extractHtmlContent', () => {
new NodeTestHarness().setupTests();
});
@@ -0,0 +1,418 @@
{
"name": "html extract fix",
"nodes": [
{
"parameters": {},
"id": "b421815f-bbeb-480d-a759-6a0360a050b6",
"name": "When clicking \"Execute Workflow\"",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [480, 780]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "html"
}
]
},
"options": {
"cleanUpText": true
}
},
"id": "73ed18ec-3a26-4300-b917-240faa810a33",
"name": "HTML",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 260]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "html",
"skipSelectors": "img, a"
}
]
},
"options": {}
},
"id": "d8eaf4c4-be91-43ba-b5ff-efcc7ece60b0",
"name": "HTML2",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 600]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "p",
"returnArray": true
}
]
},
"options": {}
},
"id": "145a5168-69fd-49bd-a2a0-07841854937c",
"name": "HTML3",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 760]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "=html"
}
]
},
"options": {
"trimValues": true
}
},
"id": "7a370ce9-e4c4-46e0-89a4-881a6c8a7019",
"name": "HTML1",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 420]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "div",
"returnValue": "attribute"
}
]
},
"options": {}
},
"id": "64c9005d-f9fe-457e-8e24-f1c59e76aeae",
"name": "HTML4",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 940]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "body",
"returnValue": "html"
}
]
},
"options": {}
},
"id": "eef6b477-2c28-4c20-884f-93bbd48f9d0d",
"name": "HTML5",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 1120]
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "data",
"cssSelector": "#text-id",
"returnValue": "value"
}
]
},
"options": {}
},
"id": "a548e5e3-0dcd-4f52-a581-bd046ef325b3",
"name": "HTML6",
"type": "n8n-nodes-base.html",
"typeVersion": 1.2,
"position": [1280, 1280]
},
{
"parameters": {
"data": {
"data": "<html>\n<head>\n\t<title>My Page</title>\t\n</head>\n<body>\n\t<h1>My Page</h1>\n\t<p>Hello World</p>\n\t<div class=\"content\">\n\t\t<p>Another paragraph\n</p>\n\t\t<p>Yet \r\n\r\n\t\t\t\t\t\tanother paragraph\n</p>\n\t\t<p>Andone more\n</p>\n\t</div>\n\t<img src=\"https://n8n.io/n8n-logo.png\" alt=\"n8n.io logo\" />\n\t<a href=\"https://n8n.io\">n8n.io</a>\n <input id=\"text-id\" type=\"text\" value=\"n8n\" />\n</body>\n</html>"
}
},
"id": "ed46f03d-6cde-4225-beab-fdbe82bf095f",
"name": "Code",
"type": "n8n-nodes-testing.testData",
"typeVersion": 1,
"position": [780, 740]
},
{
"parameters": {},
"id": "9d4c07df-3348-4b0e-b144-dfb038bddb99",
"name": "No Operation, do nothing",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1500, 260]
},
{
"parameters": {},
"id": "0dd56421-7e3a-4908-a933-c4e09de6b7d5",
"name": "No Operation, do nothing1",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1580, 620]
},
{
"parameters": {},
"id": "15c21267-b4b7-4805-ad40-32060400fcef",
"name": "No Operation, do nothing2",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1600, 780]
},
{
"parameters": {},
"id": "d6fd1d78-e24b-4237-bd66-620130f0e5fc",
"name": "No Operation, do nothing3",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1640, 1140]
},
{
"parameters": {},
"id": "f8b7457b-7b46-4af6-958c-ad770f29e587",
"name": "No Operation, do nothing4",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1560, 960]
},
{
"parameters": {},
"id": "f509b012-78ea-4e14-9ee4-ebdd562efe3e",
"name": "No Operation, do nothing5",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1560, 420]
},
{
"parameters": {},
"id": "173f20fb-80aa-42d1-97b1-fc7a751fbedd",
"name": "No Operation, do nothing6",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1580, 1300]
}
],
"pinData": {
"No Operation, do nothing4": [
{
"json": {
"data": {
"class": "content"
}
}
}
],
"No Operation, do nothing": [
{
"json": {
"data": "MY PAGEHello WorldAnother paragraphYet another paragraphAndone moren8n.io logo [https://n8n.io/n8n-logo.png] n8n.io [https://n8n.io]"
}
}
],
"No Operation, do nothing5": [
{
"json": {
"data": "MY PAGE\n\nHello World\n\nAnother paragraph\n\nYet another paragraph\n\nAndone more\n\nn8n.io logo [https://n8n.io/n8n-logo.png] n8n.io [https://n8n.io]"
}
}
],
"No Operation, do nothing1": [
{
"json": {
"data": "MY PAGE\n\nHello World\n\nAnother paragraph\n\nYet another paragraph\n\nAndone more"
}
}
],
"No Operation, do nothing2": [
{
"json": {
"data": ["Hello World", "Another paragraph", "Yet another paragraph", "Andone more"]
}
}
],
"No Operation, do nothing3": [
{
"json": {
"data": "\n\t<h1>My Page</h1>\n\t<p>Hello World</p>\n\t<div class=\"content\">\n\t\t<p>Another paragraph\n</p>\n\t\t<p>Yet \n\n\t\t\t\t\t\tanother paragraph\n</p>\n\t\t<p>Andone more\n</p>\n\t</div>\n\t<img src=\"https://n8n.io/n8n-logo.png\" alt=\"n8n.io logo\">\n\t<a href=\"https://n8n.io\">n8n.io</a>\n <input id=\"text-id\" type=\"text\" value=\"n8n\">\n\n"
}
}
],
"No Operation, do nothing6": [
{
"json": {
"data": "n8n"
}
}
]
},
"connections": {
"When clicking \"Execute Workflow\"": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "HTML",
"type": "main",
"index": 0
},
{
"node": "HTML1",
"type": "main",
"index": 0
},
{
"node": "HTML2",
"type": "main",
"index": 0
},
{
"node": "HTML3",
"type": "main",
"index": 0
},
{
"node": "HTML4",
"type": "main",
"index": 0
},
{
"node": "HTML5",
"type": "main",
"index": 0
},
{
"node": "HTML6",
"type": "main",
"index": 0
}
]
]
},
"HTML": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
]
]
},
"HTML6": {
"main": [
[
{
"node": "No Operation, do nothing6",
"type": "main",
"index": 0
}
]
]
},
"HTML5": {
"main": [
[
{
"node": "No Operation, do nothing3",
"type": "main",
"index": 0
}
]
]
},
"HTML4": {
"main": [
[
{
"node": "No Operation, do nothing4",
"type": "main",
"index": 0
}
]
]
},
"HTML3": {
"main": [
[
{
"node": "No Operation, do nothing2",
"type": "main",
"index": 0
}
]
]
},
"HTML2": {
"main": [
[
{
"node": "No Operation, do nothing1",
"type": "main",
"index": 0
}
]
]
},
"HTML1": {
"main": [
[
{
"node": "No Operation, do nothing5",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "170b087f-19bf-4cbd-90cf-d684fb112034",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "be251a83c052a9862eeac953816fbb1464f89dfbf79d7ac490a8e336a8cc8bfd"
},
"id": "vqwcz5PIBQmAw4SZ",
"tags": []
}
+12
View File
@@ -0,0 +1,12 @@
import type cheerio from 'cheerio';
export type Cheerio = ReturnType<typeof cheerio>;
export interface IValueData {
attribute?: string;
skipSelectors?: string;
cssSelector: string;
returnValue: string;
key: string;
returnArray: boolean;
}
+59
View File
@@ -0,0 +1,59 @@
import { convert } from 'html-to-text';
import type { IDataObject } from 'n8n-workflow';
import type { IValueData, Cheerio } from './types';
// The extraction functions
const extractFunctions: {
[key: string]: ($: Cheerio, valueData: IValueData, nodeVersion: number) => string | undefined;
} = {
attribute: ($: Cheerio, valueData: IValueData): string | undefined =>
$.attr(valueData.attribute!),
html: ($: Cheerio, _valueData: IValueData): string | undefined => $.html() || undefined,
text: ($: Cheerio, _valueData: IValueData, nodeVersion: number): string | undefined => {
if (nodeVersion <= 1.1) return $.text() || undefined;
const html = $.html() || '';
let options;
if (_valueData.skipSelectors) {
options = {
selectors: _valueData.skipSelectors.split(',').map((s) => ({
selector: s.trim(),
format: 'skip',
})),
};
}
return convert(html, options);
},
value: ($: Cheerio, _valueData: IValueData): string | undefined => $.val(),
};
/**
* Simple helper function which applies options
*/
export function getValue(
$: Cheerio,
valueData: IValueData,
options: IDataObject,
nodeVersion: number,
) {
let value = extractFunctions[valueData.returnValue]($, valueData, nodeVersion);
if (value === undefined) {
return value;
}
if (options.trimValues) {
value = value.trim();
}
if (options.cleanUpText) {
value = value
.replace(/^\s+|\s+$/g, '')
.replace(/(\r\n|\n|\r)/gm, '')
.replace(/\s+/g, ' ');
}
return value;
}