문서 정보
GPT with Obsidian Save Flask App
ChatGPT 대화 내용을 Obsidian에 저장하는 Flask 앱 개발 문서
📋 개요
이 문서는 Playwright와 MCP(Model Context Protocol)를 활용해 ChatGPT 대화 내용을 Obsidian의 .md 파일로 저장하는 Flask 웹 애플리케이션 개발 과정을 설명합니다. Flask 앱은 웹 인터페이스와 API를 제공하며, Playwright로 ChatGPT 대화를 수집하고 MCP 서버를 통해 Obsidian Vault에 저장합니다. Mac 환경을 기준으로 하며, 대화별 파일 저장, 날짜별 통합 저장, 태그 및 YAML 프론트매터 지원을 포함합니다.
요구사항
- 목표: ChatGPT 대화 내용을 자동으로 추출해 Obsidian Vault에
.md파일로 저장. - 기술 스택:
- Flask: 웹 서버 및 API.
- Playwright: ChatGPT 웹 UI 스크레이핑.
- MCP: Obsidian Vault와의 동기화.
- Obsidian: 대화 기록 저장 및 관리.
- 기능:
- 웹 UI를 통한 대화 수집 트리거.
- 대화별 또는 날짜별
.md파일 저장. - YAML 프론트매터에 태그 및 생성 날짜 포함.
- MCP 서버를 통한 Vault 동기화.
✅ 전제 조건
-
설치 필요:
- Python 3.x 및
pip. - Node.js (Playwright 및 MCP용).
- Python 패키지:
flask,playwright,requests. - Node.js 패키지:
playwright,@modelcontextprotocol/sdk,@modelcontextprotocol/server-filesystem. -
명령어:
pip install flask playwright requests playwright install npm install playwright @modelcontextprotocol/sdk @modelcontextprotocol/server-filesystem
- Python 3.x 및
-
환경 설정:
- Obsidian Vault 경로 (예:
/Users/yourname/ObsidianVault). - MCP 서버 실행:
npx @modelcontextprotocol/server-filesystem ~/ObsidianVault --port 3030. - ChatGPT 로그인 정보 (수동 로그인 또는 API 키).
- Obsidian Vault 경로 (예:
🧩 구성 개요
- Flask 앱:
- 웹 인터페이스: 대화 수집 및 저장 트리거.
- API 엔드포인트: Playwright 실행 및
.md파일 저장.
- Playwright: ChatGPT 웹페이지에서 대화 추출, 임시 파일(
messages.txt)에 저장. - MCP 서버: Obsidian Vault에
.md파일 동기화. - 저장 옵션:
- 대화별 개별 파일 (예:
Chat_2025-05-13_001.md). - 날짜별 통합 파일 (예:
ChatGPT_2025-05-13.md). - YAML 프론트매터에 태그와 타임스탬프 포함.
- 대화별 개별 파일 (예:
- 자동화: Flask 앱 내 스케줄링 또는 외부 트리거.
🛠️ 개발 과정
1. 디렉토리 구조
chatgpt-to-obsidian/
├── app.py # Flask 앱 메인 파일
├── chatgpt_export.js # Playwright 스크립트
├── templates/
│ └── index.html # 웹 인터페이스 HTML
├── static/
│ └── style.css # 스타일시트
├── requirements.txt # Python 의존성
├── package.json # Node.js 의존성
└── ObsidianVault/ # Obsidian Vault 디렉토리 (예시)
2. Flask 앱 (app.py)
Flask 앱은 웹 UI와 /scrape 엔드포인트를 제공하며, Playwright로 수집한 대화를 MCP 서버를 통해 저장합니다.
from flask import Flask, render_template, request, jsonify
import subprocess
import os
from datetime import datetime
import requests
app = Flask(__name__)
# 설정
OBSIDIAN_VAULT = "/Users/yourname/ObsidianVault" # 실제 Vault 경로로 변경
MCP_ENDPOINT = "http://localhost:3030"
NODE_SCRIPT = os.path.join(os.path.dirname(__file__), "chatgpt_export.js")
def save_to_mcp(content, filename, tags=[]):
"""MCP 서버를 통해 Obsidian Vault에 저장"""
file_path = f"ChatGPT/{filename}"
headers = {"Content-Type": "text/plain"}
payload = {
"vault": OBSIDIAN_VAULT,
"content": f"""---
tags: [{', '.join(tags)}]
created_at: {datetime.now().strftime('%Y-%m-%d')}
---
# ChatGPT 대화 기록 - {datetime.now().strftime('%Y-%m-%d')}
{content}
"""
}
response = requests.post(f"{MCP_ENDPOINT}/write/{file_path}", json=payload, headers=headers)
return response.status_code == 200
@app.route("/")
def index():
"""웹 인터페이스 렌더링"""
return render_template("index.html")
@app.route("/scrape", methods=["POST"])
def scrape():
"""ChatGPT 대화 수집 및 저장"""
try:
# Playwright 스크립트 실행
result = subprocess.run(["node", NODE_SCRIPT], capture_output=True, text=True)
if result.returncode != 0:
return jsonify({"error": result.stderr}), 500
# 출력 파싱
with open("messages.txt", "r") as f:
messages = f.read().splitlines()
# 옵션 처리
date = datetime.now().strftime("%Y-%m-%d")
tags = request.form.get("tags", "ChatGPT,AI").split(",")
separate_files = request.form.get("separate", "false").lower() == "true"
if separate_files:
for i, msg in enumerate(messages, 1):
filename = f"Chat_{date}_{i}.md"
content = f"### Message {i}\n{msg}"
save_to_mcp(content, filename, tags)
else:
filename = f"ChatGPT_{date}.md"
content = "\n\n".join(f"### Message {i}\n{msg}" for i, msg in enumerate(messages, 1))
save_to_mcp(content, filename, tags)
return jsonify({"message": "대화가 성공적으로 저장되었습니다!", "files": filename})
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == "__main__":
app.run(debug=True, port=5000)
3. Playwright 스크립트 (chatgpt_export.js)
ChatGPT 대화를 수집해 messages.txt에 저장합니다.
const { chromium } = require('playwright');
const fs = require('fs');
(async () => {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
try {
await page.goto('https://chat.openai.com/');
await page.waitForSelector('.text-base', { timeout: 30000 });
const messages = await page.$$eval('.text-base', elements =>
elements.map(el => {
const role = el.closest('[data-user-message]') ? 'User' : 'Assistant';
return `${role}: ${el.innerText.trim()}`;
})
);
fs.writeFileSync('messages.txt', messages.join('\n'));
} catch (error) {
console.error('Error:', error);
process.exit(1);
} finally {
await browser.close();
}
})();
4. 웹 인터페이스 (templates/index.html)
웹 UI로 대화 수집을 트리거합니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>ChatGPT to Obsidian</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>ChatGPT 대화 → Obsidian 저장</h1>
<form id="scrape-form">
<label for="tags">태그 (쉼표로 구분):</label>
<input type="text" id="tags" name="tags" value="ChatGPT,AI"><br>
<label for="separate">대화별 개별 파일 저장:</label>
<input type="checkbox" id="separate" name="separate"><br>
<button type="submit">대화 수집 및 저장</button>
</form>
<div id="result"></div>
<script>
document.getElementById('scrape-form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const response = await fetch('/scrape', {
method: 'POST',
body: formData
});
const result = await response.json();
document.getElementById('result').innerText = JSON.stringify(result, null, 2);
});
</script>
</body>
</html>
5. 스타일시트 (static/style.css)
body { font-family: Arial, sans-serif; margin: 20px; }
form { margin: 20px 0; }
label { margin-right: 10px; }
button { padding: 10px 20px; }
#result { margin-top: 20px; white-space: pre-wrap; }
6. 의존성
-
Python (
requirements.txt):flask==2.3.3 playwright==1.44.0 requests==2.31.0설치:
pip install -r requirements.txt -
Node.js (
package.json):{ "dependencies": { "playwright": "^1.44.0", "@modelcontextprotocol/sdk": "latest", "@modelcontextprotocol/server-filesystem": "latest" } }설치:
npm install
🚀 실행 방법
-
MCP 서버 실행:
npx @modelcontextprotocol/server-filesystem ~/ObsidianVault --port 3030 -
Flask 앱 실행:
python app.py -
웹 인터페이스 접속:
http://localhost:5000에 접속.- 태그 입력, 대화별 저장 옵션 선택 후 "대화 수집 및 저장" 클릭.
-
결과 확인:
-
Obsidian Vault의
ChatGPT폴더에.md파일 생성. - 예:
ChatGPT_2025-05-13.md또는Chat_2025-05-13_001.md.
📂 저장 예시
단일 파일 (ChatGPT_2025-05-13.md):
---
tags: [ChatGPT, AI]
created_at: 2025-05-13
---
# ChatGPT 대화 기록 - 2025-05-13
### Message 1
User: 플레이라이트로 채팅 내용을 옵시디언에 저장할 수 있을까?
### Message 2
Assistant: 가능합니다. 다음과 같은 방식으로 저장할 수 있습니다...
대화별 파일 (Chat_2025-05-13_1.md):
---
tags: [ChatGPT, AI]
created_at: 2025-05-13
---
# ChatGPT 대화 기록 - 2025-05-13
### Message 1
User: 플레이라이트로 채팅 내용을 옵시디언에 저장할 수 있을까?
🛠️ 개발 과정 요약
-
초기 워크플로우:
- Playwright로 ChatGPT 대화 추출.
fs로.md파일 생성.- MCP 서버로 Obsidian Vault 동기화.
cron또는launchd로 자동화 제안.-
Flask 앱 변환:
-
Flask로 웹 UI와 API 엔드포인트 구현.
- Playwright 스크립트를 subprocess로 호출.
- MCP 서버를 통해
.md파일 저장. - 대화별/날짜별 저장, 태그 및 YAML 지원.
-
개선점:
-
사용자/봇 메시지 구분.
- 에러 처리 및 타임아웃 설정.
- 웹 UI에 태그 입력 및 저장 옵션 추가.
⚠️ 주의사항
- ChatGPT DOM 변경:
.text-base셀렉터는 UI 업데이트로 바뀔 수 있음. 정기적 테스트 필요.
- 로그인 처리:
- 수동 로그인 필요. API 키 또는 쿠키로 자동화 가능.
- MCP 서버:
- Flask 앱 실행 전 MCP 서버가
localhost:3030에서 실행 중이어야 함.
- Flask 앱 실행 전 MCP 서버가
- Obsidian Vault:
OBSIDIAN_VAULT경로를 실제 경로로 설정.
📝 추가 확장 제안
-
로그인 자동화:
- ChatGPT API 또는 쿠키로 자동 로그인.
-
예:
page.evaluate("() => { localStorage.setItem('auth_token', 'your_token'); }")
-
스케줄링:
-
apscheduler로 주기적 실행:from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() scheduler.add_job(scrape, 'interval', hours=24) scheduler.start()
-
-
API 확장:
-
/api/scrape엔드포인트로 외부 호출 지원:@app.route("/api/scrape", methods=["POST"]) def api_scrape(): data = request.json tags = data.get("tags", ["ChatGPT", "AI"]) separate = data.get("separate", False) # 로직 추가
-
-
Obsidian 플러그인 연동:
- Dataview로 태그/날짜 쿼리.
- Templater로 파일 생성 템플릿화.
-
다른 플랫폼:
-
Grok 대화 저장:
x.comUI 스크레이핑 또는 xAI API (https://x.ai/api). - Claude 대화: Anthropic API 또는 Playwright.
-
배포:
-
Heroku, AWS, Docker로 Flask 앱 배포.
🔚 결론
이 Flask 앱은 Playwright와 MCP를 활용해 ChatGPT 대화를 Obsidian에 효과적으로 저장합니다. 웹 UI와 API로 유연한 사용이 가능하며, 대화별/날짜별 저장, 태그 및 YAML 지원으로 Obsidian과의 호환성을 높였습니다. 추가 요구사항(예: 서브폴더 구조, 자동화, 다른 플랫폼 지원)이 있다면, 이를 반영해 코드를 확장할 수 있습니다.