Python과 AI를 사용한 맞춤형 날씨 이메일
중급
이것은Personal Productivity, Multimodal AI분야의자동화 워크플로우로, 11개의 노드를 포함합니다.주로 Code, Gmail, FormTrigger, HttpRequest, Agent 등의 노드를 사용하며. OpenWeatherMap, Python, GPT-4.1-mini를 사용하여 개인화된 날씨 보고서 생성
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
- •대상 API의 인증 정보가 필요할 수 있음
- •OpenAI API Key
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"id": "BrYg4iQRGkBoypeu",
"meta": {
"instanceId": "bdc54da2c96840612a04bf3fd3a4cd97a7a7bd7c1152bbe41d5615f09311c097",
"templateCredsSetupCompleted": true
},
"name": "Custom Weather Email using Python and AI",
"tags": [],
"nodes": [
{
"id": "822a0d12-4591-40d0-85f9-0c940372ed5f",
"name": "날씨 데이터 가져오기",
"type": "n8n-nodes-base.httpRequest",
"position": [
-80,
304
],
"parameters": {
"url": "https://api.openweathermap.org/data/2.5/weather",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "=q",
"value": "={{ $json.City }}"
},
{
"name": "appid",
"value": "<your_API_key>"
},
{
"name": "units",
"value": "metric"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "99ddfbb3-c834-4017-9ec4-f32f870e1482",
"name": "날씨 데이터 처리",
"type": "n8n-nodes-base.code",
"position": [
32,
496
],
"parameters": {
"language": "python",
"pythonCode": "import json\nfrom datetime import datetime\nimport math\n\n# Get the weather data from the previous node\nweather_data = items[0]['json']\n\n# Extract relevant information\ncity = weather_data['name']\ncountry = weather_data['sys']['country']\ntemperature = weather_data['main']['temp']\nfeels_like = weather_data['main']['feels_like']\nhumidity = weather_data['main']['humidity']\npressure = weather_data['main']['pressure']\ndescription = weather_data['weather'][0]['description']\nwind_speed = weather_data['wind']['speed']\nvisibility = weather_data.get('visibility', 0) / 1000 # Convert to km\n\n# Calculate temperature in Fahrenheit\ntemp_fahrenheit = (temperature * 9/5) + 32\nfeels_like_fahrenheit = (feels_like * 9/5) + 32\n\n# Calculate wind speed in different units\nwind_speed_kmh = wind_speed * 3.6\nwind_speed_mph = wind_speed * 2.237\n\n# Determine comfort level based on temperature and humidity\ndef get_comfort_level(temp, humidity):\n if temp < 0:\n return \"Freezing\"\n elif temp < 10:\n return \"Cold\"\n elif temp > 35:\n return \"Very Hot\"\n elif temp > 28:\n return \"Hot\"\n elif humidity > 80:\n return \"Humid\"\n elif 18 <= temp <= 24 and 40 <= humidity <= 60:\n return \"Ideal\"\n elif 15 <= temp <= 27 and humidity <= 70:\n return \"Comfortable\"\n else:\n return \"Moderate\"\n\n# Get clothing suggestion\ndef get_clothing_suggestion(temp, wind, desc):\n if \"rain\" in desc.lower() or \"drizzle\" in desc.lower():\n clothing = \"Waterproof jacket and umbrella\"\n elif \"snow\" in desc.lower():\n clothing = \"Heavy winter coat, boots, gloves, and hat\"\n elif temp < 0:\n clothing = \"Heavy winter coat, thermal layers, gloves, and hat\"\n elif temp < 10:\n clothing = \"Warm jacket or heavy sweater\"\n elif temp < 18:\n clothing = \"Light jacket or cardigan\"\n elif temp < 25:\n clothing = \"Long sleeves or light sweater\"\n elif temp < 30:\n clothing = \"T-shirt and light pants/shorts\"\n else:\n clothing = \"Light, breathable clothing and sun protection\"\n \n if wind > 5:\n clothing += \" (consider wind protection)\"\n \n return clothing\n\n# Get activity recommendation\ndef get_activity_recommendation(temp, desc, wind):\n desc_lower = desc.lower()\n \n if \"thunderstorm\" in desc_lower or \"heavy rain\" in desc_lower:\n return \"Stay indoors, perfect for indoor activities\"\n elif \"rain\" in desc_lower or \"drizzle\" in desc_lower:\n return \"Indoor activities recommended, or covered outdoor areas\"\n elif \"snow\" in desc_lower:\n return \"Winter sports, snowman building, or cozy indoor activities\"\n elif temp < -5:\n return \"Limit outdoor exposure, indoor activities recommended\"\n elif temp < 5:\n return \"Short outdoor activities, winter sports, or indoor activities\"\n elif 15 <= temp <= 25 and wind < 7:\n return \"Perfect for any outdoor activities - hiking, sports, picnics\"\n elif temp > 30:\n return \"Early morning or evening outdoor activities, stay hydrated\"\n elif wind > 10:\n return \"Be cautious with outdoor activities, secure loose items\"\n else:\n return \"Good for moderate outdoor activities\"\n\n# Determine weather emoji\ndef get_weather_emoji(desc):\n desc_lower = desc.lower()\n if \"clear\" in desc_lower:\n return \"☀️\"\n elif \"cloud\" in desc_lower:\n return \"☁️\" if \"few\" not in desc_lower else \"⛅\"\n elif \"rain\" in desc_lower:\n return \"🌧️\"\n elif \"thunderstorm\" in desc_lower:\n return \"⛈️\"\n elif \"snow\" in desc_lower:\n return \"❄️\"\n elif \"mist\" in desc_lower or \"fog\" in desc_lower:\n return \"🌫️\"\n else:\n return \"🌤️\"\n\ncomfort_level = get_comfort_level(temperature, humidity)\nweather_emoji = get_weather_emoji(description)\n\n# Create a comprehensive weather report\nweather_report = {\n \"location\": f\"{city}, {country}\",\n \"timestamp\": datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"),\n \"weather_emoji\": weather_emoji,\n \"current_conditions\": {\n \"temperature_celsius\": round(temperature, 1),\n \"temperature_fahrenheit\": round(temp_fahrenheit, 1),\n \"feels_like_celsius\": round(feels_like, 1),\n \"feels_like_fahrenheit\": round(feels_like_fahrenheit, 1),\n \"description\": description.title(),\n \"humidity\": humidity,\n \"pressure\": pressure,\n \"visibility_km\": round(visibility, 1),\n \"wind_speed_ms\": round(wind_speed, 1),\n \"wind_speed_kmh\": round(wind_speed_kmh, 1),\n \"wind_speed_mph\": round(wind_speed_mph, 1)\n },\n \"analysis\": {\n \"comfort_level\": comfort_level,\n \"is_good_weather\": temperature >= 15 and temperature <= 25 and humidity <= 70 and \"rain\" not in description.lower(),\n \"clothing_suggestion\": get_clothing_suggestion(temperature, wind_speed, description),\n \"activity_recommendation\": get_activity_recommendation(temperature, description, wind_speed),\n \"weather_quality_score\": min(100, max(0, \n 100 - abs(20 - temperature) * 3 - max(0, humidity - 60) - max(0, wind_speed - 5) * 5\n ))\n },\n \"raw_data\": weather_data\n}\n\n# Return the processed data\nreturn [{\"json\": weather_report}]"
},
"typeVersion": 2
},
{
"id": "0d850dd1-2930-407f-9f87-f418bc588f4a",
"name": "이메일 내용 생성",
"type": "n8n-nodes-base.code",
"position": [
160,
320
],
"parameters": {
"language": "python",
"pythonCode": "# Get the processed weather data\nweather_data = items[0]['json']\n\n# Create HTML email content\nhtml_content = f\"\"\"\n<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Weather Report</title>\n <style>\n body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 20px; background-color: #f5f7fa; }}\n .container {{ max-width: 600px; margin: 0 auto; background-color: white; border-radius: 15px; overflow: hidden; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }}\n .header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; }}\n .header h1 {{ margin: 0; font-size: 2em; }}\n .location {{ font-size: 1.2em; margin-top: 10px; opacity: 0.9; }}\n .section {{ padding: 25px; border-bottom: 1px solid #eee; }}\n .section:last-child {{ border-bottom: none; }}\n .section h2 {{ color: #2c3e50; margin-top: 0; display: flex; align-items: center; gap: 10px; }}\n .temp-main {{ font-size: 3em; font-weight: bold; color: #e74c3c; text-align: center; margin: 20px 0; }}\n .temp-feels {{ font-size: 1.2em; color: #7f8c8d; text-align: center; margin-bottom: 20px; }}\n .conditions-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0; }}\n .condition-item {{ background-color: #f8f9fa; padding: 15px; border-radius: 8px; text-align: center; }}\n .condition-value {{ font-size: 1.4em; font-weight: bold; color: #2c3e50; }}\n .condition-label {{ font-size: 0.9em; color: #7f8c8d; margin-top: 5px; }}\n .comfort-badge {{ display: inline-block; padding: 8px 16px; border-radius: 20px; font-weight: bold; text-transform: uppercase; font-size: 0.9em; }}\n .comfort-ideal {{ background-color: #d4edda; color: #155724; }}\n .comfort-comfortable {{ background-color: #cce5ff; color: #004085; }}\n .comfort-moderate {{ background-color: #fff3cd; color: #856404; }}\n .comfort-hot {{ background-color: #f8d7da; color: #721c24; }}\n .comfort-cold {{ background-color: #e2e3e5; color: #383d41; }}\n .recommendation {{ background-color: #e8f5e8; padding: 20px; border-radius: 10px; margin: 15px 0; border-left: 4px solid #28a745; }}\n .score {{ text-align: center; margin: 20px 0; }}\n .score-circle {{ display: inline-block; width: 80px; height: 80px; border-radius: 50%; background: conic-gradient(#28a745 0deg {weather_data['analysis']['weather_quality_score'] * 3.6}deg, #e9ecef {weather_data['analysis']['weather_quality_score'] * 3.6}deg 360deg); position: relative; }}\n .score-inner {{ position: absolute; top: 10px; left: 10px; width: 60px; height: 60px; border-radius: 50%; background-color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #2c3e50; }}\n .footer {{ background-color: #f8f9fa; padding: 20px; text-align: center; font-size: 0.9em; color: #6c757d; }}\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>{weather_data['weather_emoji']} Weather Report</h1>\n <div class=\"location\">{weather_data['location']}</div>\n <div style=\"font-size: 0.9em; margin-top: 5px;\">{weather_data['timestamp']}</div>\n </div>\n \n <div class=\"section\">\n <div class=\"temp-main\">{weather_data['current_conditions']['temperature_celsius']}°C</div>\n <div class=\"temp-feels\">Feels like {weather_data['current_conditions']['feels_like_celsius']}°C</div>\n <div style=\"text-align: center; font-size: 1.3em; color: #495057; margin-bottom: 20px;\">\n {weather_data['current_conditions']['description']}\n </div>\n \n <div style=\"text-align: center; margin: 20px 0;\">\n <span class=\"comfort-badge comfort-{weather_data['analysis']['comfort_level'].lower().replace(' ', '-')}\">\n {weather_data['analysis']['comfort_level']}\n </span>\n </div>\n </div>\n \n <div class=\"section\">\n <h2>📊 Current Conditions</h2>\n <div class=\"conditions-grid\">\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['humidity']}%</div>\n <div class=\"condition-label\">Humidity</div>\n </div>\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['pressure']}</div>\n <div class=\"condition-label\">Pressure (hPa)</div>\n </div>\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['wind_speed_kmh']}</div>\n <div class=\"condition-label\">Wind (km/h)</div>\n </div>\n <div class=\"condition-item\">\n <div class=\"condition-value\">{weather_data['current_conditions']['visibility_km']}</div>\n <div class=\"condition-label\">Visibility (km)</div>\n </div>\n </div>\n </div>\n \n <div class=\"section\">\n <h2>🎯 Recommendations</h2>\n <div class=\"recommendation\">\n <strong>👕 What to Wear:</strong><br>\n {weather_data['analysis']['clothing_suggestion']}\n </div>\n <div class=\"recommendation\">\n <strong>🏃 Activities:</strong><br>\n {weather_data['analysis']['activity_recommendation']}\n </div>\n </div>\n \n <div class=\"section\">\n <h2>⭐ Weather Quality Score</h2>\n <div class=\"score\">\n <div class=\"score-circle\">\n <div class=\"score-inner\">{int(weather_data['analysis']['weather_quality_score'])}</div>\n </div>\n <div style=\"margin-top: 10px; color: #6c757d;\">Out of 100</div>\n </div>\n </div>\n \n <div class=\"footer\">\n <strong>🤖 Generated by n8n Weather Pipeline</strong><br>\n Powered by Python • OpenWeatherMap API<br>\n <em>Stay informed, stay prepared!</em>\n </div>\n </div>\n</body>\n</html>\n\"\"\"\n\n# Create plain text version\ntext_content = f\"\"\"\n🌤️ DAILY WEATHER REPORT\n\n📍 Location: {weather_data['location']}\n🕒 Time: {weather_data['timestamp']}\n\n🌡️ CURRENT CONDITIONS\n- Temperature: {weather_data['current_conditions']['temperature_celsius']}°C ({weather_data['current_conditions']['temperature_fahrenheit']}°F)\n- Feels Like: {weather_data['current_conditions']['feels_like_celsius']}°C ({weather_data['current_conditions']['feels_like_fahrenheit']}°F)\n- Condition: {weather_data['current_conditions']['description']}\n- Humidity: {weather_data['current_conditions']['humidity']}%\n- Wind Speed: {weather_data['current_conditions']['wind_speed_kmh']} km/h\n- Pressure: {weather_data['current_conditions']['pressure']} hPa\n- Visibility: {weather_data['current_conditions']['visibility_km']} km\n\n💡 ANALYSIS\n- Comfort Level: {weather_data['analysis']['comfort_level']}\n- Weather Quality Score: {int(weather_data['analysis']['weather_quality_score'])}/100\n\n👕 CLOTHING RECOMMENDATION\n{weather_data['analysis']['clothing_suggestion']}\n\n🏃 ACTIVITY RECOMMENDATION\n{weather_data['analysis']['activity_recommendation']}\n\n---\nGenerated by n8n Weather Pipeline\nPowered by Python & OpenWeatherMap API\n\"\"\"\n\n# Return the email content\nreturn [{\n \"json\": {\n \"subject\": f\"{weather_data['weather_emoji']} Weather Report - {weather_data['location']} ({weather_data['current_conditions']['temperature_celsius']}°C)\",\n \"html_content\": html_content,\n \"text_content\": text_content,\n \"weather_data\": weather_data\n }\n}]"
},
"typeVersion": 2
},
{
"id": "22cbe6d0-2360-4039-a7b5-fc242c3061ff",
"name": "메시지 보내기",
"type": "n8n-nodes-base.gmail",
"position": [
800,
304
],
"webhookId": "0fa8e721-11fd-4a08-b150-d6b1c58a59f3",
"parameters": {
"sendTo": "emailAddress",
"message": "={{ $json.output }}",
"options": {},
"subject": "={{ $('Generate Email Content').item.json.subject }}"
},
"typeVersion": 2.1
},
{
"id": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
"name": "AI 에이전트",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
384,
304
],
"parameters": {
"text": "=You are an email drafting expert. Take the weather information from {{ $json.text_content }} and make it pretty and easy to read. Use proper spacing, use of paragraphs and bullet points.\n\nPrepare your email in html format.\n\nAdd a joke relevant to the weather if possible. ",
"options": {},
"promptType": "define"
},
"typeVersion": 2.1
},
{
"id": "1557d3ed-d6fa-4542-ab01-961873998ca4",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
432,
512
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4.1-mini"
},
"options": {}
},
"typeVersion": 1.2
},
{
"id": "eccbbc05-da0c-40b9-8954-3f0fadb0640f",
"name": "폼 제출 시",
"type": "n8n-nodes-base.formTrigger",
"position": [
-320,
304
],
"webhookId": "28ccc6a1-25c4-4f3c-9358-4365285f7cdf",
"parameters": {
"options": {},
"formTitle": "Enter a City",
"formFields": {
"values": [
{
"fieldLabel": "City",
"placeholder": "New York"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "52859605-1f9d-4e87-a722-920ac296095b",
"name": "스티커 메모",
"type": "n8n-nodes-base.stickyNote",
"position": [
-400,
192
],
"parameters": {
"height": 304,
"content": "Enter the name of a city in the form"
},
"typeVersion": 1
},
{
"id": "bcd1c3e0-ee2d-479d-9fc4-1d391d20c0e9",
"name": "스티커 메모1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-144,
176
],
"parameters": {
"width": 480,
"height": 464,
"content": "Add OpenWeather API key to get current weather information about the cit, replace <your_API_key> with your actual API key. \nPython script will process and generate custom email relating to the weather. "
},
"typeVersion": 1
},
{
"id": "49c4cdfb-13ea-41fa-90bc-7ced8147fc19",
"name": "스티커 메모2",
"type": "n8n-nodes-base.stickyNote",
"position": [
352,
176
],
"parameters": {
"width": 352,
"height": 480,
"content": "Add OpenAI API Key. AI Agent will add a custom joke about the weather for the city you entered as part of the email message"
},
"typeVersion": 1
},
{
"id": "46ed9532-4693-4740-819f-81a1ddf51ca1",
"name": "스티커 메모3",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
176
],
"parameters": {
"height": 304,
"content": "Add your Gmail credentials and update header and body of message. Node will send the custom email to the recipient"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "83a69a4e-f4cd-472f-812e-1492a06ddbc5",
"connections": {
"0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5": {
"main": [
[
{
"node": "22cbe6d0-2360-4039-a7b5-fc242c3061ff",
"type": "main",
"index": 0
}
]
]
},
"1557d3ed-d6fa-4542-ab01-961873998ca4": {
"ai_languageModel": [
[
{
"node": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"822a0d12-4591-40d0-85f9-0c940372ed5f": {
"main": [
[
{
"node": "99ddfbb3-c834-4017-9ec4-f32f870e1482",
"type": "main",
"index": 0
}
]
]
},
"eccbbc05-da0c-40b9-8954-3f0fadb0640f": {
"main": [
[
{
"node": "822a0d12-4591-40d0-85f9-0c940372ed5f",
"type": "main",
"index": 0
}
]
]
},
"99ddfbb3-c834-4017-9ec4-f32f870e1482": {
"main": [
[
{
"node": "0d850dd1-2930-407f-9f87-f418bc588f4a",
"type": "main",
"index": 0
}
]
]
},
"0d850dd1-2930-407f-9f87-f418bc588f4a": {
"main": [
[
{
"node": "0c5c9ca9-ec89-4420-ad8c-0a96ccb2b5b5",
"type": "main",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 개인 생산성, 멀티모달 AI
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
매일 생일 축하 (코드 노드 최종 버전)
基于NASA图片、GPT-4、Gmail및Slack의자동화太空主题生日이메일
If
Code
Gmail
+
If
Code
Gmail
17 노드Yanagi Chinatsu
개인 생산성
Airtop, GPT-4 Mini 및 Gmail을 사용한 웹사이트 UX 및 SEO 품질 분석
Airtop, GPT-4 Mini 및 Gmail을 사용하여 웹사이트 UX 및 SEO 품질 분석
Set
Code
Html
+
Set
Code
Html
33 노드LukaszB
콘텐츠 제작
Apollo 데이터 스크래핑 및 리치 프로세스 1 ✅
Apollo, AI 파싱 및 예정 이메일 후속 조치를 사용한 잠재 고객 자동 생성
If
Code
Wait
+
If
Code
Wait
39 노드Deniz
콘텐츠 제작
RapidAPI, Hunter.io, GPT 및 Gmail 기반 자동화된 B2B 리드 생성
RapidAPI, Hunter.io, GPT 및 Gmail 기반 자동화된 B2B 잠재 고객 발굴
If
Set
Code
+
If
Set
Code
21 노드Jimmy Gay
리드 생성
Gmail, GPT-4 및 벡터 지식 베이스를 사용한 자동화된 고객 지원 시스템
Gmail, GPT-4 및 벡터 지식 베이스를 사용한 자동화된 고객 지원 시스템
If
Set
Code
+
If
Set
Code
32 노드Khair Ahammed
AI RAG
[템플릿] GA4 보고서
AI 통찰력과 Gmail을 사용하여 자동으로 GA4 KPI 보고서를 생성하고 전송
Code
Gmail
Form Trigger
+
Code
Gmail
Form Trigger
13 노드Simeon Penev
기타