通过Webhooks使用ScreenshotMachine API按需生成网站截图
中级
这是一个Building Blocks领域的自动化工作流,包含 12 个节点。主要使用 If, Code, Webhook, HttpRequest, RespondToWebhook 等节点。 通过Webhooks使用ScreenshotMachine API按需生成网站截图
前置要求
- •HTTP Webhook 端点(n8n 会自动生成)
- •可能需要目标 API 的认证凭证
分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "cXnFp41w8eVCH9tx",
"meta": {
"instanceId": "1777696fb9fddfee653e70940936c2b1e28ba1f1bde53b7182fbd6eb01988706",
"templateCredsSetupCompleted": true
},
"name": "通过 Webhooks 使用 ScreenshotMachine API 按需生成网站截图",
"tags": [],
"nodes": [
{
"id": "57d3821e-cba4-47fc-b92a-c1dd2e6337ca",
"name": "注意:Webhook 输入",
"type": "n8n-nodes-base.stickyNote",
"position": [
1500,
1740
],
"parameters": {
"height": 340,
"content": "此节点监听传入的 POST 请求。它期望接收包含 'url' 属性(您想要截图的网站 URL)的 JSON 正文。"
},
"typeVersion": 1
},
{
"id": "6b55c36a-1e8f-4609-af02-e09347b1f06e",
"name": "注意:URL 验证与安全",
"type": "n8n-nodes-base.stickyNote",
"position": [
2060,
1700
],
"parameters": {
"color": 3,
"width": 400,
"height": 360,
"content": "此关键节点验证传入的 'url' 以防止服务器端请求伪造(SSRF)漏洞。它检查有效的 HTTP/HTTPS 协议,并确保 URL 不指向内部/私有 IP 地址或本地主机。"
},
"typeVersion": 1
},
{
"id": "4d67e039-3198-4ff1-b72b-94dccf3125df",
"name": "注意:截图 API 调用(GET)",
"type": "n8n-nodes-base.stickyNote",
"position": [
2500,
1660
],
"parameters": {
"color": 4,
"width": 340,
"height": 320,
"content": "此节点使用验证后的 URL 向 ScreenshotMachine API 发起 HTTP GET 请求。请记得将 URL 参数中的 'YOUR_API_KEY' 替换为您的实际 API 密钥。"
},
"typeVersion": 1
},
{
"id": "296d8f30-1015-4070-acc0-7e1b2e3cbac8",
"name": "注意:Webhook 响应",
"type": "n8n-nodes-base.stickyNote",
"position": [
2880,
1780
],
"parameters": {
"color": 5,
"width": 340,
"height": 480,
"content": "1. 如果 URL 无效或被安全检查阻止,它将发送清晰的错误消息。"
},
"typeVersion": 1
},
{
"id": "548687ec-3af4-4d2b-a8ac-93eecaabcb6c",
"name": "注意:解析 URL(HEAD 请求)",
"type": "n8n-nodes-base.stickyNote",
"position": [
1780,
1820
],
"parameters": {
"color": 2,
"height": 340,
"content": "此节点对传入的 URL 执行 HEAD 请求。"
},
"typeVersion": 1
},
{
"id": "86316119-a064-4052-a27c-07c957c71802",
"name": "接收 URL Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
1560,
1920
],
"webhookId": "caf8f5dc-4834-45bb-96b0-d4b508f93e1b",
"parameters": {
"path": "caf8f5dc-4834-45bb-96b0-d4b508f93e1b",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "1abb0d7c-9064-4e75-89be-cc7c409ecb84",
"name": "解析 URL(HEAD 请求)",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueErrorOutput",
"position": [
1840,
2000
],
"parameters": {
"url": "={{$json.body.url}}",
"method": "HEAD",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "1a9218c1-a132-4109-88da-0ac24236de34",
"name": "验证 URL 防止 SSRF",
"type": "n8n-nodes-base.code",
"position": [
2120,
1900
],
"parameters": {
"jsCode": "for (const item of $input.all()) {\n let isValidUrl = false; // Start with false, will be set to true if passes checks\n let errorMessage = '';\n let finalUrl = null; // This will store the validated/resolved URL\n\n const headResponse = item.json; // Assuming the HTTP Request node outputs directly to item.json\n\n // Check if the HEAD request was successful (status 2xx or 3xx for redirects)\n const statusCode = headResponse.statusCode;\n console.log('DEBUG: HEAD Request Status Code:', statusCode);\n console.log('DEBUG: HEAD Request Headers:', JSON.stringify(headResponse.headers));\n\n if (statusCode >= 200 && statusCode < 400) {\n // Get the final URL after redirects, or original URL if no redirect\n let resolvedUrlString = headResponse.headers?.['x-final-url'] || headResponse.url || headResponse.request?.url;\n\n console.log('DEBUG: Resolved URL string from HEAD request:', resolvedUrlString);\n\n if (resolvedUrlString) {\n finalUrl = resolvedUrlString;\n\n // Manual parsing of protocol and hostname from resolvedUrlString\n let protocol = '';\n let hostname = '';\n\n const protocolMatch = finalUrl.match(/^(\\w+):\\/\\//);\n if (protocolMatch && protocolMatch[1]) {\n protocol = protocolMatch[1];\n // Remove protocol and leading slashes for hostname extraction\n let tempUrl = finalUrl.substring(protocol.length + 3); // e.g., \"https://www.example.com/path\" -> \"www.example.com/path\"\n const slashIndex = tempUrl.indexOf('/');\n if (slashIndex !== -1) {\n hostname = tempUrl.substring(0, slashIndex); // e.g., \"www.example.com\"\n } else {\n hostname = tempUrl; // e.g., \"www.example.com\" (if no path)\n }\n }\n console.log('DEBUG: Manually parsed Protocol:', protocol, 'Hostname:', hostname);\n\n // Basic protocol check\n if (protocol === 'http' || protocol === 'https') {\n isValidUrl = true; // Protocol is good so far\n } else {\n isValidUrl = false;\n errorMessage = 'Only HTTP or HTTPS protocols are allowed.';\n console.log('ERROR: Protocol check failed. Detected protocol:', protocol);\n }\n\n // Check for private IPs or localhost to prevent SSRF (only if valid so far)\n if (isValidUrl && hostname) { // Ensure hostname was extracted\n if (hostname === 'localhost' || hostname === '127.0.0.1') {\n isValidUrl = false;\n errorMessage = 'Access to localhost is not allowed for security reasons.';\n console.log('ERROR: Localhost or 127.0.0.1 detected.');\n } else {\n const parts = hostname.split('.');\n // Check if it's an IPv4 address (4 parts) and all parts are numbers\n if (parts.length === 4 && parts.every(part => !isNaN(parseInt(part)) && isFinite(part) && parseInt(part) >= 0 && parseInt(part) <= 255)) {\n const ip = parts.map(Number);\n if (ip[0] === 10 || (ip[0] === 172 && ip[1] >= 16 && ip[1] <= 31) || (ip[0] === 192 && ip[1] === 168)) {\n isValidUrl = false;\n errorMessage = 'Access to private IP addresses is not allowed for security reasons.';\n console.log('ERROR: Private IPv4 address detected:', ip);\n }\n }\n }\n } else if (!hostname) { // If hostname couldn't be extracted, it's not valid\n isValidUrl = false;\n errorMessage = 'Could not determine hostname from URL.';\n console.log('ERROR: Hostname could not be extracted.');\n }\n\n } else { // No resolved URL string from HEAD request\n isValidUrl = false;\n errorMessage = 'Failed to get a resolved URL string from HEAD request response.';\n console.log('ERROR: No resolved URL string found in HEAD request output.');\n }\n } else { // HEAD request returned non-2xx/3xx status\n isValidUrl = false;\n errorMessage = `URL is unreachable or returned status code ${statusCode || 'unknown'}.`;\n console.log(`ERROR: HEAD request failed with status code: ${statusCode}`);\n }\n\n // Add validation status to the item's JSON\n item.json.isValidUrl = isValidUrl;\n item.json.errorMessage = errorMessage;\n item.json.validatedUrl = isValidUrl ? finalUrl : null;\n item.json.statusCode = statusCode; // Add status code for debug from HEAD request\n console.log('DEBUG: Final isValidUrl for this item:', isValidUrl, 'Final errorMessage:', errorMessage);\n}\n\nreturn $input.all();"
},
"typeVersion": 2
},
{
"id": "922a97b7-1df7-40f6-836e-b8ade7a48176",
"name": "如果 URL 有效",
"type": "n8n-nodes-base.if",
"position": [
2380,
1980
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "or",
"conditions": [
{
"id": "9c45d5bc-62c0-487c-a4ad-e2409785cf94",
"operator": {
"type": "string",
"operation": "notExists",
"singleValue": true
},
"leftValue": "={{ $json.errorMessage ?? $json.error}}",
"rightValue": ""
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "627362d9-f789-4810-9e2b-5678ee5ac209",
"name": "拍摄截图",
"type": "n8n-nodes-base.httpRequest",
"position": [
2620,
1840
],
"parameters": {
"url": "=https://api.screenshotmachine.com?key=YOUR_API_KEY&url={{$json.validatedUrl}}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "68c3d952-de02-4de1-bd4e-363a28e1ab05",
"name": "响应截图数据",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3000,
1920
],
"parameters": {
"options": {},
"respondWith": "allIncomingItems"
},
"typeVersion": 1.2
},
{
"id": "38161c96-327c-42af-ab14-5825f6f2aafd",
"name": "响应验证错误",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
3000,
2080
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "={{ $json.error. }}"
},
"typeVersion": 1.2
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "e3b7d5c4-f779-4012-9bd1-91135725e0ac",
"connections": {
"IF URL Valid": {
"main": [
[
{
"node": "Take Screenshot",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond with Validation Error",
"type": "main",
"index": 0
}
]
]
},
"Take Screenshot": {
"main": [
[
{
"node": "Respond with Screenshot Data",
"type": "main",
"index": 0
}
]
]
},
"Receive URL Webhook": {
"main": [
[
{
"node": "Resolve URL (HEAD Request)",
"type": "main",
"index": 0
}
]
]
},
"Validate URL for SSRF": {
"main": [
[
{
"node": "IF URL Valid",
"type": "main",
"index": 0
}
]
]
},
"Resolve URL (HEAD Request)": {
"main": [
[
{
"node": "Validate URL for SSRF",
"type": "main",
"index": 0
}
],
[
{
"node": "IF URL Valid",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
中级 - 构建模块
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
通过 Webhook 的内容摘要生成器 (ApyHub)
通过 Webhook 的内容摘要生成器 (ApyHub)
Webhook
Http Request
Respond To Webhook
+1
8 节点ist00dent
构建模块
通过 TimeZoneDB API 集成转换时区
使用TimeZoneDB API集成转换时区
Webhook
Http Request
Respond To Webhook
+1
6 节点ist00dent
构建模块
通过 Webhook 使用 ExchangeRate.host 进行货币转换
使用 ExchangeRate.host 通过 Webhook 进行货币转换
Webhook
Http Request
Respond To Webhook
+1
6 节点ist00dent
构建模块
通过 Webhook 验证 JSON 字符串
通过Webhook验证JSON字符串
Code
Webhook
Respond To Webhook
+1
6 节点ist00dent
构建模块
通过Webhook进行IP地理位置查询
通过Webhook使用IP-API.com查询IP地理位置详情
Webhook
Http Request
Respond To Webhook
+1
6 节点ist00dent
构建模块
通过 Webhook 查询公共假日
通过 Webhook 使用 Nager.Date API 查询公共假日
Webhook
Http Request
Respond To Webhook
+1
6 节点ist00dent
构建模块
工作流信息
难度等级
中级
节点数量12
分类1
节点类型6
作者
ist00dent
@ist00dentI’m a dedicated automation engineer passionate about no-code and low-code solutions. I design and implement robust n8n workflows—integrating APIs, databases, and messaging—to eliminate manual tasks and accelerate delivery. Leveraging Python and C#, I build scalable, adaptable automations that empower teams to focus on high-value work.
外部链接
在 n8n.io 查看 →
分享此工作流