Automatische Synchronisation lokaler Ereignisse mit Google Kalender

Fortgeschritten

Dies ist ein AI-Bereich Automatisierungsworkflow mit 11 Nodes. Hauptsächlich werden Code, Html, HttpRequest, GoogleCalendar, ScheduleTrigger und andere Nodes verwendet, kombiniert mit KI-Technologie für intelligente Automatisierung. Automatische Synchronisation lokaler Ereignisse mit Google Kalender mit n8n

Voraussetzungen
  • Möglicherweise sind Ziel-API-Anmeldedaten erforderlich
Workflow-Vorschau
Visualisierung der Node-Verbindungen, mit Zoom und Pan
Workflow exportieren
Kopieren Sie die folgende JSON-Konfiguration und importieren Sie sie in n8n
{
  "id": "y1ZOHX6Zq13C68dP",
  "meta": {
    "instanceId": "60046904b104f0f72b2629a9d88fe9f676be4035769f1f08dad1dd38a76b9480"
  },
  "name": "AutoSync_Local_Events_to_Google_Calendar",
  "tags": [],
  "nodes": [
    {
      "id": "433e4192-599f-4f3d-a251-651b81db4960",
      "name": "Täglicher Ereignis-Sync-Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -2740,
        220
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "94f20c26-9a90-43ba-8ab1-b1107c17345c",
      "name": "Ereignisseite abrufen (Bright Data)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2520,
        220
      ],
      "parameters": {
        "url": "https://api.brightdata.com/request",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "zone",
              "value": "n8n_unblocker"
            },
            {
              "name": "url",
              "value": "https://www.nypl.org/events/calendar"
            },
            {
              "name": "country",
              "value": "us"
            },
            {
              "name": "format",
              "value": "raw"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer API_KEY"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "e55c8584-f673-4201-bda7-42cdbadf49bf",
      "name": "Ereignisdaten extrahieren (HTML Parser)",
      "type": "n8n-nodes-base.html",
      "position": [
        -2220,
        220
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "Title",
              "cssSelector": ".event-title",
              "returnArray": true
            },
            {
              "key": "Location",
              "cssSelector": ".event-location",
              "returnArray": true
            },
            {
              "key": "Audience",
              "cssSelector": ".event-audience",
              "returnArray": true
            },
            {
              "key": "Time",
              "cssSelector": ".event-time",
              "returnArray": true
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "48196549-0378-4cd9-8e1d-f289c4b50180",
      "name": "Ereignisdaten bereinigen & formatieren",
      "type": "n8n-nodes-base.code",
      "position": [
        -2000,
        220
      ],
      "parameters": {
        "jsCode": "const data = items[0].json;\n\n// Extract arrays\nconst titles = data.Title || [];\nconst locations = data.Location || [];\nconst audiences = data.Audience || [];\nconst rawTimes = data.Time || [];\n\n// Step 1: Remove invalid \"time\" placeholders\nconst invalidTimeLabels = [\"Date/Time\", \"Title/Description\", \"Location\", \"Audience\"];\nconst times = rawTimes.filter(time => !invalidTimeLabels.includes(time.trim()));\n\n// Step 2: Safely calculate number of valid events\nconst eventCount = Math.min(titles.length, locations.length, audiences.length, times.length);\n\n// Helper: Convert \"Today @ 10 AM\" → ISO string with timezone\nfunction parseTimeToISO(rawTime) {\n  const match = rawTime.match(/@ ([0-9]{1,2})(?::([0-9]{2}))?\\s?(AM|PM)/i);\n  const now = new Date();\n\n  if (!match) return null;\n\n  let hour = parseInt(match[1]);\n  const minute = match[2] ? parseInt(match[2]) : 0;\n  const meridian = match[3].toUpperCase();\n\n  if (meridian === \"PM\" && hour !== 12) hour += 12;\n  if (meridian === \"AM\" && hour === 12) hour = 0;\n\n  const start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute);\n  const end = new Date(start.getTime() + 60 * 60 * 1000); // 1-hour event\n\n  const offsetMinutes = start.getTimezoneOffset();\n  const offsetHours = Math.floor(Math.abs(offsetMinutes) / 60);\n  const offsetMins = Math.abs(offsetMinutes) % 60;\n  const offsetSign = offsetMinutes > 0 ? \"-\" : \"+\";\n  const offset = `${offsetSign}${String(offsetHours).padStart(2, \"0\")}:${String(offsetMins).padStart(2, \"0\")}`;\n\n  const toISOStringWithOffset = (d) => {\n    const pad = (n) => n.toString().padStart(2, \"0\");\n    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:00${offset}`;\n  };\n\n  return {\n    start: toISOStringWithOffset(start),\n    end: toISOStringWithOffset(end),\n  };\n}\n\n// Step 3: Build cleaned and enriched event objects\nconst results = [];\n\nfor (let i = 0; i < eventCount; i++) {\n  const titleText = titles[i];\n  const timeText = times[i]?.trim();\n  const parsedTime = parseTimeToISO(timeText);\n\n  results.push({\n    json: {\n      title: titleText.split('\\n')[0]?.trim(),\n      description: titleText.trim(),\n      location: locations[i]?.trim(),\n      audience: audiences[i]?.trim(),\n      time: timeText,\n      start: { dateTime: parsedTime?.start || null },\n      end: { dateTime: parsedTime?.end || null },\n      sourceUrl: \"https://www.nypl.org\" + (titleText.match(/\\[([^\\]]+)\\]/)?.[1] || '')\n    }\n  });\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "945e912a-609a-42e0-82a4-c3d36623a767",
      "name": "Google Kalenderereignisse erstellen",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        -1700,
        220
      ],
      "parameters": {
        "end": "={{ $json.end.dateTime }}",
        "start": "={{ $json.start.dateTime }}",
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "f14d18c7802fe01f77d5200ada9658c96cbc69b3cb9ff7b2914dc63bf6f263e3@group.calendar.google.com",
          "cachedResultName": "Community Events"
        },
        "additionalFields": {
          "attendees": [],
          "description": "=Title: {{ $json.title }}\nDescription: {{ $json.description }}\nLocation: {{ $json.location }}\nAudience: {{ $json.audience }}\nTime: {{ $json.time }}"
        }
      },
      "credentials": {
        "googleCalendarOAuth2Api": {
          "id": "ZiXaTJAXCcyzY5iy",
          "name": "Google Calendar account"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "6c5825a7-6a1e-485c-820b-ed4188c6b718",
      "name": "Notizzettel",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2800,
        -540
      ],
      "parameters": {
        "color": 5,
        "width": 440,
        "height": 980,
        "content": "## 🧭 **Section 1: Event Data Fetcher**\n\n### 🔁 `🕒 Daily Event Sync Trigger`\n\n> **Node:** *Schedule Trigger*\n> ⏰ This node sets **when** your automation runs. For example, you might want it to run every morning at 8:00 AM to check for new community events.\n\n🔍 Why it matters:\n\n> This ensures your public calendar is always up to date without manual work.\n\n---\n\n### 🌐 `🌐 Fetch Event Page (Bright Data)`\n\n> **Node:** *HTTP Request using Bright Data Web Unlocker*\n> 🛡 This node sends a request to a **local government or library event page** using Bright Data’s **Web Unlocker proxy**, which helps bypass blocks or bot detection.\n\n🔧 What’s special here:\n\n* It uses your **Bright Data proxy zone**\n* It targets a public events URL (e.g., a `.gov` or `.org`)\n* Makes sure you get the raw HTML page — even if the site tries to hide it\n\n✅ Benefit:\n\n> This lets you scrape data reliably from websites that are normally hard to reach for bots.\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "aa5624b4-6c2e-4d70-a05b-4e038ee82dfb",
      "name": "Notizzettel1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2300,
        -660
      ],
      "parameters": {
        "color": 3,
        "width": 440,
        "height": 1100,
        "content": "## 🧩 **Section 2: Smart Event Extractor**\n\n### 🧩 `🧩 Extract Event Data (HTML Parser)`\n\n> **Node:** *HTML*\n> 📄 This node parses the raw HTML response from the website and extracts useful information like:\n\n* 🏷 Event Titles\n* 📍 Locations\n* 👥 Target Audience\n* ⏰ Raw Times\n\nIt uses selectors (like CSS) to grab content from specific parts of the page.\n\n🎯 Goal:\n\n> Transform messy website code into **structured data** you can actually use.\n\n---\n\n### 🧠 `🧠 Clean & Format Event Data`\n\n> **Node:** *Code*\n> 🧼 This is the brain of your workflow. It takes the raw extracted data and:\n\n* Matches titles with their correct time/location\n* Skips garbage data like `\"Date/Time\"` headers\n* 🧮 Converts time to proper ISO format (like `2025-06-21T11:00:00-07:00`) for Google Calendar\n\n🚀 Bonus:\n\n> This ensures only **valid**, well-structured events are passed forward — nothing broken, empty, or misaligned.\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b19c44ba-1abb-4d0b-a854-1d844163ffdf",
      "name": "Notizzettel2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1800,
        -240
      ],
      "parameters": {
        "color": 6,
        "width": 320,
        "height": 680,
        "content": "## 📅 **Section 3: Auto Calendar Creator**\n\n### 📅 `📅 Create Google Calendar Events`\n\n> **Node:** *Google Calendar*\n> 🧙‍♂️ This node automatically **creates public calendar events** using the cleaned-up data.\n\nHere’s what it sets:\n\n* **Summary:** Event title\n* **Description:** Full details (e.g., location, what it’s about)\n* **Start/End Time:** In the correct Google Calendar format\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "85488a2c-a51a-4561-8f7d-44901a3a04a1",
      "name": "Notizzettel9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4600,
        -500
      ],
      "parameters": {
        "color": 4,
        "width": 1300,
        "height": 320,
        "content": "=======================================\n            WORKFLOW ASSISTANCE\n=======================================\nFor any questions or support, please contact:\n    Yaron@nofluff.online\n\nExplore more tips and tutorials here:\n   - YouTube: https://www.youtube.com/@YaronBeen/videos\n   - LinkedIn: https://www.linkedin.com/in/yaronbeen/\n=======================================\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2395bf91-efa9-4149-b411-38d18924c5c5",
      "name": "Notizzettel4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4600,
        -160
      ],
      "parameters": {
        "color": 4,
        "width": 1289,
        "height": 2298,
        "content": "# **📅 AutoSync Local Events to Google Calendar**\n\n### *A No-Code Automation to Scrape, Clean, and Publish Community Events*\n\n---\n## 🧭 **Section 1: Event Data Fetcher**\n\n### 🔁 `🕒 Daily Event Sync Trigger`\n\n> **Node:** *Schedule Trigger*\n> ⏰ This node sets **when** your automation runs. For example, you might want it to run every morning at 8:00 AM to check for new community events.\n\n🔍 Why it matters:\n\n> This ensures your public calendar is always up to date without manual work.\n\n---\n\n### 🌐 `🌐 Fetch Event Page (Bright Data)`\n\n> **Node:** *HTTP Request using Bright Data Web Unlocker*\n> 🛡 This node sends a request to a **local government or library event page** using Bright Data’s **Web Unlocker proxy**, which helps bypass blocks or bot detection.\n\n🔧 What’s special here:\n\n* It uses your **Bright Data proxy zone**\n* It targets a public events URL (e.g., a `.gov` or `.org`)\n* Makes sure you get the raw HTML page — even if the site tries to hide it\n\n✅ Benefit:\n\n> This lets you scrape data reliably from websites that are normally hard to reach for bots.\n\n---\n\n## 🧩 **Section 2: Smart Event Extractor**\n\n### 🧩 `🧩 Extract Event Data (HTML Parser)`\n\n> **Node:** *HTML*\n> 📄 This node parses the raw HTML response from the website and extracts useful information like:\n\n* 🏷 Event Titles\n* 📍 Locations\n* 👥 Target Audience\n* ⏰ Raw Times\n\nIt uses selectors (like CSS) to grab content from specific parts of the page.\n\n🎯 Goal:\n\n> Transform messy website code into **structured data** you can actually use.\n\n---\n\n### 🧠 `🧠 Clean & Format Event Data`\n\n> **Node:** *Code*\n> 🧼 This is the brain of your workflow. It takes the raw extracted data and:\n\n* Matches titles with their correct time/location\n* Skips garbage data like `\"Date/Time\"` headers\n* 🧮 Converts time to proper ISO format (like `2025-06-21T11:00:00-07:00`) for Google Calendar\n\n🚀 Bonus:\n\n> This ensures only **valid**, well-structured events are passed forward — nothing broken, empty, or misaligned.\n\n---\n\n## 📅 **Section 3: Auto Calendar Creator**\n\n### 📅 `📅 Create Google Calendar Events`\n\n> **Node:** *Google Calendar*\n> 🧙‍♂️ This node automatically **creates public calendar events** using the cleaned-up data.\n\nHere’s what it sets:\n\n* **Summary:** Event title\n* **Description:** Full details (e.g., location, what it’s about)\n* **Start/End Time:** In the correct Google Calendar format\n\n🫶 Why this matters:\n\n> Now your scraped community events are published straight to a **Google Calendar** — which you can embed on a website, share with citizens, or subscribe to.\n\n---\n\n## 🧩 Summary: Why This Workflow Rocks\n\n| 🔧 Step              | 📌 What It Does                                       | 🎁 Benefit to You                            |\n| -------------------- | ----------------------------------------------------- | -------------------------------------------- |\n| **Schedule Trigger** | Starts the automation daily                           | Keeps your calendar always fresh             |\n| **HTTP Request**     | Grabs raw event info from a gov/library site          | Reliable access to public event data         |\n| **HTML Parser**      | Pulls structured info from messy HTML                 | Turns code into useful content               |\n| **Code Node**        | Fixes formatting, converts time, aligns titles & more | Clean data ready for use                     |\n| **Calendar Node**    | Publishes events into your Google Calendar            | Fully automated public calendar — no effort! |\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f4286f93-9457-4042-9b4d-bbb8304eaac1",
      "name": "Notizzettel5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1420,
        -240
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 240,
        "content": "## I’ll receive a tiny commission if you join Bright Data through this link—thanks for fueling more free content!\n\n### https://get.brightdata.com/1tndi4600b25"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "69086b47-9272-4887-8844-db532999a0ba",
  "connections": {
    "433e4192-599f-4f3d-a251-651b81db4960": {
      "main": [
        [
          {
            "node": "94f20c26-9a90-43ba-8ab1-b1107c17345c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "48196549-0378-4cd9-8e1d-f289c4b50180": {
      "main": [
        [
          {
            "node": "945e912a-609a-42e0-82a4-c3d36623a767",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "94f20c26-9a90-43ba-8ab1-b1107c17345c": {
      "main": [
        [
          {
            "node": "e55c8584-f673-4201-bda7-42cdbadf49bf",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "e55c8584-f673-4201-bda7-42cdbadf49bf": {
      "main": [
        [
          {
            "node": "48196549-0378-4cd9-8e1d-f289c4b50180",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Häufig gestellte Fragen

Wie verwende ich diesen Workflow?

Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.

Für welche Szenarien ist dieser Workflow geeignet?

Fortgeschritten - Künstliche Intelligenz

Ist es kostenpflichtig?

Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.

Workflow-Informationen
Schwierigkeitsgrad
Fortgeschritten
Anzahl der Nodes11
Kategorie1
Node-Typen6
Schwierigkeitsbeschreibung

Für erfahrene Benutzer, mittelkomplexe Workflows mit 6-15 Nodes

Autor
Yaron Been

Yaron Been

@yaron-nofluff

Building AI Agents and Automations | Growth Marketer | Entrepreneur | Book Author & Podcast Host If you need any help with Automations, feel free to reach out via linkedin: https://www.linkedin.com/in/yaronbeen/ And check out my Youtube channel: https://www.youtube.com/@YaronBeen/videos

Externe Links
Auf n8n.io ansehen

Diesen Workflow teilen

Kategorien

Kategorien: 34