ChatGPT를 이용한 자동화 블로그를 완성했다. 자동화 블로그의 목적은 순수 나의 재미를 위해 만들었고, 구글 에드센스로 부터 수익은 전혀 기대하지 않고 있다.
생각보다 까다롭고 개발자가 아니라면 따라 하기 어려울 거라고 생각이 든다.
준비물
서버 1대, 파이썬 초급이해, 리눅스 환경 구축 경험, github action에 대한 이해, ChatGPT 유료 결제
자 그럼 시작해 보자
1. 블로그 선택
처음으로 해야 할 일은 블로그를 선택하는 일이다.
티스토리, 네이버 블로그, github 페이지 등등 다양한 블로그가 있지만, ChatGPT가 마크다운 형식으로 파일을 저장해 주기 때문에 github페이지를 사용했다.
2. jekyll theme 선택
나는 Type-on-Strap을 선택했다. 전에 Chirpy가 유명해서 사용했었는데 테스트코드가 너무 힘들게 돌아서 자주 수정해줘야 하는 이슈때문에 이 테마를 선택했다.
gh-pages를 통해서 블로그 개설하는 건 구글링 하면 자세히 나오기 때문에 건너뛰겠다.
3. 서버 구입
서버는 집에서 살고 있는 백앤드 개발자가 가지고 노는 서버가 4개 있어서 부탁해 하나 달라고 해서 사용했다.
4. 리눅스 환경 구축
이제 저 서버를 원격으로 연결해서 환경을 구축해야 한다.
그래서 Proxmox라는 서비스를 이용해서 환경을 구축했다.
이렇게 내 IP를 입력해서 Proxmox 환경에 접속할 수 있게 된다.
자세한 설정은 가상환경으로 설정했고, 램은 4기가, 스토리지는 4기가 정도 할당했다는 건 기억이 나지만 디테일을 잘.. 기억이 나지 않는다. 설정하는 방법 또한 구글링 하면 잘 나오니까 이 부분도 스킵하겠다.
꼭 이렇게 해야 해?라고 생각 할 수 도 있다.
이게 아니라면 클라우드로 하는 방법이 있긴 한데 나는 8시간에 딱 1번 스크립트가 돌기 때문에 이 방법을 선택했다.
AWS Lambda도 이런 용도로 사용할 수 있는데 해보니까 잘 안 돼서 그냥 이렇게 했다.
5. 로직
이렇게 세팅을 해놓고 원격으로 들어가서 코딩하기엔 너무나도 불편하기 때문에 개인 환경에서 잘 돌아가는 로직을 만들고, 깃에다가 올린 다음에 clone 하는 방향으로 개발을 진행했다.
1. systemd를 이용해서 8시간마다 스크립트 실행
2. publish.sh 스크립트 실행
# active python
echo "started"
cd /home/yoon/blog/1yoouoo.github.io
/usr/bin/python3 main.py >> /home/yoon/blog/1yoouoo.github.io/main.log 2>&1
git pull
git config --local user.name "blanc"
git config --local user.email "1yoouoo@gmail.com"
sleep 0.1
git add -A
git commit -m "publish blog"
git push origin HEAD
3. 파이썬 파일 실행
파이썬 로직0. 귀여운 고양이 사진 추가
1. Random으로 카테고리 선택 ['javascript', 'typescript', 'reactjs', 'html', 'css', 'nestjs', 'nodejs'] 중 1개
2. 시간이 싱크가 안 맞으면 테스트코드 에러를 뱉을 수 있기 때문에 어제 일자로 생성
3. Stackoverflow의 카테고리에서 실시간 조회 수 높은 제목 선택
4. 적절한 제목으로 다듬기
5. 제목에 관련한 블로그 포스팅 내용 담기
6. 마지막에 위 내용의 요약 적기
7. 위 제목과 관련된 추천 사이트 3~5개 제공
8. 파일 생성 후 md파일로 적절한 경로에 저장
9. 파이썬 실행 종료
# 파이썬 전체 코드
import openai
from stackapi import StackAPI
import os
import time
from datetime import datetime, timedelta
import random
import pytz
seoul_tz = pytz.timezone('Asia/Seoul')
now = datetime.now(seoul_tz)
# Create a StackAPI object for the Stack Overflow site
print('#####################################################################')
SITE = StackAPI('stackoverflow')
one_year_ago = (datetime.utcnow() - timedelta(days=365)).strftime('%Y-%m-%d')
# Random하게 tag선택
tag_list = ['javascript', 'typescript',
'reactjs', 'html', 'css', 'nestjs', 'nodejs']
selected_tag = random.choice(tag_list)
print(f'카테고리는 {selected_tag} 로 하겠습니다.')
try:
# Fetch the 10 most popular questions with the tag
questions = SITE.fetch('questions', pagesize=10, fromdate=one_year_ago, sort='votes', order='desc', tagged=selected_tag)
# Check if there are any questions in the response
if not questions['items']:
print("No questions found.")
os.exit()
print("프로그램 강제 종료.")
else:
# 0 ~ 50 한 개만 선택
question = questions['items'][random.randint(0, 50)]
topic = question['title']
tags = question['tags']
print(f'주제는 {topic} 로 하겠습니다.')
except Exception as e:
print("StackAPI 오류발생 : ", e)
raise SystemExit("프로그램 종료.")
openai.api_key = os.environ.get('OPEN_API_KEY')
def generate_response(prompt, max_tokens, temperature):
completion = openai.Completion.create(
engine="text-davinci-003",
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature, # creativity
top_p=1,
frequency_penalty=0,
presence_penalty=0
)
return completion
def generate_blog_to_title(topic):
prompt = f'''
{topic} is about an error in "https://stackoverflow.com/".
Read it and replace it with the appropriate title.
Do not use like "Blog title: ", just give me a title
'''
return generate_response(prompt, max_tokens=100, temperature=0.2)
def generate_blog_common_error(title):
prompt = f'''
{title} is a question about what error.
Write a subheading at the top for what you're going to write about.
Generate an article about {title}, but do not include any conclusions or summaries in your response.
Please stop generating text when you encounter the words "Conclusion" or "Summary" or "In conclustion" or "In summary".
Write down at least two common mistakes or why you're getting these errors.
Use Highlight, Bold, Italic and Apostrophe for Emphasis to keywords.
Put the javascript or typescript code to make it easier to understand in the middle of the description.
And explain each example in code as long as possible.
Write that code using language with related to error.
Make it at least 10 paragraphs long, the average person would have to read for 20 minutes.
The people reading this are developers in their 20s and 30s, so we need to explain it with code based.
All posts should be written in markdown format.
'''
return generate_response(prompt, max_tokens=2500, temperature=0.5)
# 에러 해결방법 생성
def generate_blog_error_body(title):
prompt = f'''
I need a error handling blog post.
{title} is the title of the question about what error.
Skip the title and go straight to the body of the post.
Use Highlight, Bold, Italic and Apostrophe for Emphasis to keywords.
When it comes to this error, Write in such detail that people will see this and all the errors will be resolved.
Write a step-by-step solution to this error.
Put the javascript or typescript code to make it easier to understand in the middle of the description.
And explain each example in code as long as possible.
The people reading this are developers in their 20s and 30s, so we need to explain it with code based.
Make it at least 20 paragraphs long, the average person would have to read for 35 minutes.
All posts should be written in markdown format.
'''
return generate_response(prompt, max_tokens=2500, temperature=0.5)
# 참고 할만한 홈페이지
def generate_blog_recommend_site(title):
prompt = f'''
Recommend a few official sites to read about {title}.
At the top, write the title "Recommended sites".
Write it in MD format, leaving only the URL address.
Refer users to sites that don't have 404 errors when they visit. A site that is actually usable.
'''
return generate_response(prompt, max_tokens=400, temperature=0.5)
try:
# 제목 생성
title_response = generate_blog_to_title(topic)
title = '\n'.join(
title_response['choices'][0]['text'].strip().split('\n')[:])
print(f"제목 만들었습니다. {title} :",
title_response["usage"]["total_tokens"])
except Exception as e:
print("제목 오류발생 : ", e)
raise SystemExit("프로그램 종료.")
try:
# 본문 생성
common_error_response = generate_blog_common_error(title)
common_error = '\n'.join(
common_error_response['choices'][0]['text'].strip().split('\n')[1:])
print("본문 1/2 만들었습니다. :",
common_error_response["usage"]["total_tokens"])
error_body_response = generate_blog_error_body(title)
error_body = '\n'.join(
error_body_response['choices'][0]['text'].strip().split('\n')[1:])
print("본문 2/2 만들었습니다. :",
error_body_response["usage"]["total_tokens"])
except Exception as e:
print("본문 오류발생 : ", e)
raise SystemExit("프로그램 종료.")
try:
# 참고할만한 사이트
recommend_site_response = generate_blog_recommend_site(title)
recommend_site = '\n'.join(
recommend_site_response['choices'][0]['text'].strip().split('\n')[:])
print(f"참고할만한 사이트를 만들었습니다. {title} :",
recommend_site_response["usage"]["total_tokens"])
except Exception as e:
print("참고할만한 사이트 생성 오류 발생 : ", e)
raise SystemExit("프로그램 종료.")
# 어제 일자 생성
yesterday = datetime.now() - timedelta(days=1)
timestring = yesterday.strftime('%Y-%m-%d')
title = title.replace('"', '')
filename = f"{timestring}-{'-'.join(title.lower().split())}.md"
page_outline = f'''---
layout: post
title: "{title}"
tags: {tags}
---
'''
output = page_outline + '\n' + \
"![Image of a Cat](http://source.unsplash.com/1600x900/?cat)" + \
'\n' + common_error + '\n' + error_body + '\n' + recommend_site
print("파일을 생성하기 전 잠깐 기다립니다.")
blog_directory = "/home/yoon/blog/1yoouoo.github.io/_posts"
time.sleep(2)
# 파일 이름 생성
filepath = os.path.join(blog_directory, filename)
try:
print("파일을 생성하겠습니다.")
with open(filepath, 'w') as f:
f.write(output)
f.close()
except Exception as e:
print("파일 생성 오류 발생 : ", e)
raise SystemExit("프로그램 종료.")
print("모든 작업이 끝났습니다.")
print('#####################################################################')
4. 파이썬 실행 종료 확인 후 git commit && push
5. push 트리거로 Github Actions 실행
6. 테스트 후 gh-page 배포
배포 주소
위 과정은 이 깔끔한 배포까지의 전체과정 중 일부분이다.
생각보다 트러블 슈팅이 너무 많았고 그걸 다루기에는 너무 많은 시간이 소요될 거 같아서.. 천천히 시간 날 때마다 블로깅 해보겠다.
그리고 위 파이썬 코드는 참고하기 아주 좋을 것이다. 까다로운 ChatGPT 녀석의 프롬프트를 최적화한 것이기 때문
만약에 구글 에드센스가 달리고 실제로 수익이 발생한다면 서브도메인을 만들어서 2개든 10개든 만들 생각이다.
이미 확장성 있게 만들어놔서 불리는 건 시간문제다..!
+ 6/16 추가
6/13에 gpt4-0613이 업데이트 되어서 적용해보았다.
3개월 뒤에 depressed된다고 하지만 우선적으로 적용해보았다. 블로그의 퀄리티가 훨씬 더 높아질 거 같다.
def generate_response(prompt, max_tokens, temperature):
completion = openai.ChatCompletion.create(
model="gpt-4-0613",
messages=[
{"role": "system", "content": "You are a blog post generator."},
{"role": "user", "content": prompt}
],
max_tokens=max_tokens,
temperature=temperature, # creativity
top_p=1,
frequency_penalty=0,
presence_penalty=0
)
return completion
모델에 gpt-4-0613을 추가하고
메세지에는 role을 추가할 수 있다..
system에게는 blog post generator라고 역할을 부여하고, 그 외에는 기존에 있던 prompt를 사용했다.
몇번 실행해봤는데 블로그 글이 1.5배정도 풍성해진거 같다. 3개월 뒤에 또 업데이트를 할 예정이다~~~
지금은 github pages를 이용해서 배포해놨는데 새로운 도메인을 파서 vercel에 배포해놓은 상태이다.
서브도메인으로 각 언어, 라이브러리, 프레임워크 별로 나누어서 자동화를 구축중이다. 현재 9개정도 서브도메인을 만들어놨는데
구글 색인이 될 때까지 기다리는 중이다. 색인이 완료되고 광고 허가가 나오면 다시 블로깅 해보겠다..! 끝!!
+ 8/1 추가
아.. 생각대로 되지않는다 ..
블로그 생성이나 스케쥴링은 잘 동작하지만 글의 퀄리티가 너무 낮은 모양이다.
실제 클라이언트들은 대다수 영어권 국가인데 글을 보자마자 AI가 쓴 블로그임을 알아채서 페이지를 바로 이탈해버린다.
이탈속도가 워낙 빠르다 보니까 구글에서도 페이지에 좋은 점수를 주지 못하고 좋은 점수를 받지 못한 페이지는 노출 순위에서 밀리기 때문에 결과적으로 망한 페이지가 되어버린다.
chatGPT의 API로 한달에 1~2만원정도 결제가 되는데 이건 장기적으로 봐도 손해일 거같아서 잠정중단을 시켰다.
역시 생각대로 되는 건 없구나..ㅎㅜ
'재미' 카테고리의 다른 글
크롬 익스텐션으로 사내툴 개선 (1) | 2023.11.02 |
---|---|
드디어 열린 취업의 문 (3) | 2023.10.17 |
뛰는 고양이를 코드로 구현해보자 (5) | 2023.07.29 |