n8n과 GPT-4 Vision을 활용한 부동산 매물 자동 등록 및 정보 추출 시스템 구축: Zillow, Realtor.com 스크래핑부터 맞춤 알림까지

수동적인 부동산 매물 검색은 이제 그만! n8n과 GPT-4 Vision을 결합하여 Zillow, Realtor.com 등 부동산 플랫폼에서 자동으로 매물 정보를 스크래핑하고, 이미지 분석을 통해 핵심 정보를 추출하며, 사용자 맞춤 알림을 제공하는 강력한 시스템을 구축해 보세요. 시간 절약은 물론, 시장 경쟁력까지 확보할 수 있습니다.

1. The Challenge / Context

부동산 시장은 빠르게 변화하며, 새로운 매물이 쏟아져 나옵니다. 투자자나 주택 구매 희망자는 이러한 변화를 실시간으로 파악하고, 자신에게 맞는 매물을 빠르게 찾아야 합니다. 하지만 Zillow, Realtor.com과 같은 플랫폼을 매일 확인하고, 매물 정보를 일일이 수집하고 분석하는 것은 매우 번거롭고 시간 소모적인 작업입니다. 또한, 단순히 텍스트 정보만으로는 매물의 상태나 특징을 정확히 파악하기 어렵습니다. GPT-4 Vision이 등장하기 전에는, 이미지 분석을 통한 부동산 정보 추출은 매우 복잡하고 비용이 많이 드는 기술이었습니다. 이제는 자동화된 스크래핑과 이미지 분석을 통해 더욱 효율적이고 정확한 부동산 정보 수집 및 분석이 가능해졌습니다.

2. Deep Dive: n8n (노코드 자동화 플랫폼)

n8n은 강력한 오픈 소스 워크플로우 자동화 플랫폼입니다. 코딩 없이도 다양한 서비스와 애플리케이션을 연결하여 복잡한 워크플로우를 구축할 수 있습니다. 웹 스크래핑, 데이터 변환, API 호출, 데이터베이스 연동, 이메일/슬랙 알림 등 다양한 기능을 제공하며, 사용자가 원하는 방식으로 워크플로우를 커스터마이징할 수 있습니다. 핵심은 노드 기반의 시각적인 인터페이스로, 각 노드는 특정 작업을 수행하며, 노드들을 연결하여 데이터 흐름을 정의합니다. 이는 복잡한 백엔드 로직을 간단하게 시각화하고 관리할 수 있게 해줍니다.

3. Step-by-Step Guide / Implementation

이제 n8n과 GPT-4 Vision을 활용하여 부동산 매물 자동 등록 및 정보 추출 시스템을 구축하는 단계를 자세히 살펴보겠습니다.

Step 1: n8n 설치 및 기본 설정

먼저 n8n을 설치해야 합니다. n8n은 Docker, npm, n8n Cloud 등 다양한 방식으로 설치할 수 있습니다. 여기서는 Docker를 사용하여 설치하는 방법을 예시로 설명합니다.

# Docker Compose 파일을 생성합니다.
version: '3.7'
services:
  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - 5678:5678
    volumes:
      - ~/.n8n:/home/node/.n8n
    environment:
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=your_username
      - N8N_BASIC_AUTH_PASSWORD=your_password
      - NODE_ENV=production

# Docker Compose를 실행합니다.
docker-compose up -d

위 코드에서 `your_username`과 `your_password`를 원하는 값으로 변경해야 합니다. Docker Compose 실행 후, 웹 브라우저에서 `localhost:5678`로 접속하면 n8n 인터페이스를 사용할 수 있습니다.

Step 2: 웹 스크래핑 워크플로우 구축 (Zillow 예시)

Zillow에서 매물 정보를 스크래핑하는 워크플로우를 구축합니다. HTTP Request 노드를 사용하여 Zillow의 특정 지역 매물 목록 페이지를 가져오고, HTML Extract 노드를 사용하여 원하는 정보를 추출합니다. Python 스크립트 노드를 사용하여 데이터 정제 및 변환을 수행할 수 있습니다.

// HTTP Request 노드 설정
{
  "nodes": [
    {
      "parameters": {
        "url": "https://www.zillow.com/homes/san-francisco-ca/",
        "method": "GET",
        "responseFormat": "string"
      },
      "name": "HTTP Request",
      "type": "n8n-nodes-http.httpRequest",
      "typeVersion": 1,
      "position": [
        200,
        200
      ]
    },
    {
      "parameters": {
        "mode": "html",
        "selector": ".list-card-info a",
        "output": "text"
      },
      "name": "HTML Extract",
      "type": "n8n-nodes-html.htmlExtract",
      "typeVersion": 1,
      "position": [
        400,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "Function",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "jsCode": "items.forEach(item => {\n  item.json.url = 'https://www.zillow.com' + item.json.text;\n  delete item.json.text;\n});\n\nreturn items;"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        600,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "Set",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "values": [
          {
            "name": "url",
            "value": "={{$json.url}}",
            "type": "string"
          }
        ],
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        800,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "HTTP Request1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "url": "={{$json.url}}",
        "method": "GET",
        "responseFormat": "string"
      },
      "name": "HTTP Request1",
      "type": "n8n-nodes-http.httpRequest",
      "typeVersion": 1,
      "position": [
        1000,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "HTML Extract1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "mode": "html",
        "selector": "h1.Text-c11n-8-14-0__sc-aiai24-0.krsKbz",
        "output": "text"
      },
      "name": "HTML Extract1",
      "type": "n8n-nodes-html.htmlExtract",
      "typeVersion": 1,
      "position": [
        1200,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "Function1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "jsCode": "items.forEach(item => {\n  item.json.title = item.json.text;\n  delete item.json.text;\n});\n\nreturn items;"
      },
      "name": "Function1",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1400,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "Set1",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "values": [
          {
            "name": "title",
            "value": "={{$json.title}}",
            "type": "string"
          }
        ],
        "options": {}
      },
      "name": "Set1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        1600,
        200
      ],
      "connections": {
        "main": [
          [
            {
              "node": "Console",
              "type": "main",
              "index": 0
            }
          ]
        ]
      }
    },
    {
      "parameters": {
        "message": "Title: {{$json.title}}"
      },
      "name": "Console",
      "type": "n8n-nodes-base.console",
      "typeVersion": 1,
      "position": [
        1800,
        200
      ]
    }
  ],
  "connections": {}
}

위 코드는 Zillow 매물 목록에서 각 매물의 URL을 추출하고, 해당 URL에 접속하여 매물의 제목(title)을 추출하는 워크플로우입니다. `.list-card-info a` 셀렉터를 사용하여 매물 목록에서 링크를 추출하고, 각 링크에 접속하여 `h1.Text-c11n-8-14-0__sc-aiai24-0.krsKbz` 셀렉터를 사용하여 제목을 추출합니다.

Step 3: GPT-4 Vision을 활용한 이미지 분석

매물 페이지에서 이미지를 추출하고, GPT-4 Vision API를 사용하여 이미지 분석을 수행합니다. GPT-4 Vision은 이미지 내 객체 인식, 텍스트 추출, 장면 이해 등 다양한 기능을 제공합니다. 예를 들어, 이미지를 분석하여 "주방 상태가 좋음", "최근 리모델링 완료", "넓은 정원 보유" 등의 정보를 추출할 수 있습니다.

// Function 노드 설정 (이미지 URL 추출)
const cheerio = require('cheerio');

items.forEach(item => {
  const $ = cheerio.load(item.json.data);
  const imageUrl = $('.StyledPhotosSection__StyledCarouselSlide-sc-19m2v68-2.ffXvjQ.carousel-slide img').attr('src');
  item.json.imageUrl = imageUrl;
});

return items;

// HTTP Request 노드 설정 (GPT-4 Vision API 호출)
{
  "nodes": [
    {
      "parameters": {
        "url": "https://api.openai.com/v1/chat/completions",
        "method": "POST",
        "headerParameters": [
          {
            "name": "Authorization",
            "value": "Bearer YOUR_OPENAI_API_KEY"
          },
          {
            "name": "Content-Type",
            "value": "application/json"
          }
        ],
        "bodyParameters": {
          "model": "gpt-4-vision-preview",
          "messages": [
            {
              "role": "user",
              "content": [
                {
                  "type": "text",
                  "text": "이 이미지를 분석해서 부동산 매물로서의 장점을 3가지로 요약해주세요. 그리고 전체적인 인테리어 스타일이 무엇인지 알려주세요."
                },
                {
                  "type": "image_url",
                  "image_url": {
                    "url": "={{$json.imageUrl}}"
                  }
                }
              ]
            }
          ],
          "max_tokens": 300
        },
        "options": {}
      },
      "name": "GPT-4 Vision",
      "type": "n8n-nodes-http.httpRequest",
      "typeVersion": 1,
      "position": [
        800,
        400
      ]
    }
  ],
  "connections": {}
}

위 코드에서 YOUR_OPENAI_API_KEY를 OpenAI API 키로 변경해야 합니다. Function 노드는 Cheerio 라이브러리를 사용하여 HTML을 파싱하고, 이미지 URL을 추출합니다. HTTP Request 노드는 GPT-4 Vision API를 호출하여 이미지 분석을 요청합니다. 요청 내용에는 이미지 분석 목표(부동산 매물로서의 장점 요약, 인테리어 스타일 파악)를 명시합니다.

Step 4: 데이터베이스 저장 및 알림 설정

스크래핑 및 이미지 분석 결과를 데이터베이스에 저장하고, 특정 조건(예: 가격, 지역, 특징)에 맞는 매물이 발견되면 사용자에게 알림을 보냅니다. 데이터베이스 노드를 사용하여 데이터베이스에 데이터를 저장하고, Email Send 노드나 Slack 노드를 사용하여 알림을 보낼 수 있습니다.

// MySQL 노드 설정 (데이터베이스 연결)
{
  "nodes": [
    {
      "parameters": {
        "authentication": "predefinedCredentialType",
        "credentialPropertyName": "mysql",
        "table": "listings",
        "columns": [
          {
            "name": "title",
            "type": "STRING"
          },
          {
            "name": "description",
            "type": "STRING"
          },
          {
            "name": "imageUrl",
            "type": "STRING"
          },
          {
            "name": "gpt4_analysis",
            "type": "STRING"
          }
        ],
        "options": {}
      },
      "name": "MySQL",
      "type": "n8n-nodes-db.mysql",
      "typeVersion": 1,
      "position": [
        1000,
        600
      ]
    }
  ],
  "connections": {}
}

// Email Send 노드 설정 (알림 전송)
{
  "nodes": [
    {
      "parameters": {
        "fromEmail": "your_email@example.com",
        "toEmail": "recipient_email@example.com",
        "subject": "새로운 부동산 매물 알림",
        "text": "새로운 매물이 발견되었습니다: {{ $json.title }}\n\n{{ $json.description }}\n\nGPT-4 Vision 분석 결과: {{ $json.gpt4_analysis }}\n\n이미지 URL: {{ $json.imageUrl }}"
      },
      "name": "Email Send",
      "type": "n8n-nodes-email.emailSend",
      "typeVersion": 1,
      "position": [
        1200,
        600
      ]
    }
  ],
  "connections": {}
}

위 코드에서 your_email@example.comrecipient_email@example.com을 실제 이메일 주소로 변경해야 합니다. MySQL 노드는 데이터베이스 연결 정보를 설정하고, 스크래핑 및 이미지 분석 결과를 테이블에 저장합니다. Email Send 노드는 새로운 매물 정보를 이메일로 전송합니다. GPT-4 Vision 분석 결과를 포함하여 더욱 자세한 정보를 제공할 수 있습니다.

4. Real-world Use Case / Example

개인 부동산 투자자인 저는 n8n과 GPT-4 Vision을 활용하여 매일 1시간 이상 소요되던 매물 검색 시간을 10분 이내로 단축했습니다. 이전에는 여러 웹사이트를 돌아다니며 매물 정보를 수집하고, 사진을 보며 일일이 분석해야 했습니다. 하지만 이제는 n8n 워크플로우가 자동으로 매물 정보를 수집하고, GPT-4 Vision이 사진을 분석하여 핵심 정보를 요약해 줍니다. 이를 통해 저는 더욱 중요한 투자 결정에 집중할 수 있게 되었습니다. 특히, GPT-4 Vision이 제공하는 인테리어 스타일 분석은 매물의 잠재적인 가치를 평가하는 데 큰 도움이 됩니다.

5. Pros & Cons / Critical Analysis

  • Pros:
    • 자동화를 통해 시간과 노력을 크게 절약할 수 있습니다.
    • GPT-4 Vision을 활용하여 텍스트 정보 외에 이미지 정보까지 분석하여 더욱 정확한 정보를 얻을 수 있습니다.
    • n8n의 유연성을 통해 사용자 맞춤 워크플로우를 구축할 수 있습니다.
    • 오픈 소스 플랫폼이므로 라이선스 비용 부담이 적습니다.
  • Cons:
    • 웹사이트 구조 변경에 따라 스크래핑 로직을 수정해야 할 수 있습니다.
    • GPT-4 Vision API 사용량에 따라 비용이 발생할 수 있습니다.
    • n8n의 초기 설정 및 워크플로우 구축에 대한 학습 곡선이 존재합니다.
    • Zillow나 Realtor.com의 서비스 약관을 준수해야 하며, 과도한 스크래핑은 제재를 받을 수 있습니다.

6. FAQ

  • Q: GPT-4 Vision API 키는 어떻게 얻을 수 있나요?
    A: OpenAI 웹사이트에서 API 키를 발급받을 수 있습니다. 유료 플랜을 구독해야 GPT-4 Vision API를 사용할 수 있습니다.
  • Q: n8n 워크플로우를 스케줄링할 수 있나요?
    A: 네, n8n의 Trigger 노드를 사용하여 워크플로우 실행 시간을 스케줄링할 수 있습니다. Cron 표현식을 사용하여 원하는 주기로 워크플로우를 실행할 수 있습니다.
  • Q: 다른 부동산 플랫폼도 스크래핑할 수 있나요?
    A: 네, HTTP Request 노드와 HTML Extract 노드를 사용하여 다른 부동산 플랫폼의 웹사이트도 스크래핑할 수 있습니다. 하지만 각 웹사이트의 HTML 구조에 맞춰 셀렉터를 수정해야 합니다.
  • Q: GPT-4 Vision 외에 다른 이미지 분석 API를 사용할 수 있나요?
    A: 네, Google Cloud Vision API, Amazon Rekognition 등 다양한 이미지 분석 API를 사용할 수 있습니다. n8n의 HTTP Request 노드를 사용하여 해당 API를 호출하고, 결과를 처리할 수 있습니다.

7. Conclusion

n8n과 GPT-4 Vision을 활용한 부동산 매물 자동 등록 및 정보 추출 시스템은 부동산 투자 및 주택 구매를 위한 강력한 도구입니다. 자동화된 스크래핑과 이미지 분석을 통해 시간과 노력을 절약하고, 더욱 정확한 정보를 얻을 수 있습니다. 지금 바로 n8n을 설치하고, 위에 제시된 코드를 활용하여 자신만의 부동산 정보 시스템을 구축해 보세요. n8n 공식 문서를 참고하면 더욱 자세한 정보를 얻을 수 있습니다.