본문 바로가기
2023 상반기/Visual프로그래밍

Posting with Ai.py & TodayOutfitWithAi.py 코드

by concho 2023. 5. 23.

사용 라이브러리 설명

0-1. Posting with Ai

import pyautogui
import time
import pyperclip
import openai
import queue
import re
import googletrans
import requests
import win32clipboard
import win32con
from io import BytesIO
from PIL import Image

 

AI 관련 openai: Openai에서 만든, GPT모델과 DALLE2모델과 상호작용을 가능하게 해주는 공식 라이브러리
googletrans: 구글 번역 서비를제공하는 라이브러리
re(정규 표현식): GPT의 답변에서 원하는 부분을 추출하기 위해 사용
이미지 관련 requests: 웹 페이지를 가져오는 라이브러리(DALLE2의 이미지 출력이 URL형식이라 사용) BytesIO: 프로그램의 메모리 내에서 바이너리 데이터를 사용하기 위해 사용(이미지 처리용)
win32clipboard: 클립보드에 엑세스 해서 텍스트나 이미지를 복사하거나 붙여넣기 하는 라이브러리, pyperclip과 같이 쓰임 PIL: 이미지 처리를 위한 라이브러리
컴퓨터 자동 조작
관련
pyautogui: 키보드와 마우스 조작 자동화
win32con: 윈도우 제어, 메시지, 키 코드등을 사용하게 해준다.
pyperclip: 클립보드 사용 라이브러리

0-2. TodayOutfitWithAi

import bs4
import urllib.parse
import openai
import requests
import re
import urllib.request
import cv2
import numpy as np
import googletrans
import queue
AI 관련 openai: Openai에서 만든, GPT모델과 DALLE2모델과 상호작용을 가능하게 해주는 공식 라이브러리
googletrans: 구글 번역 서비를제공하는 라이브러리
re(정규 표현식): GPT의 답변에서 원하는 부분을 추출하기 위해 사용
이미지 관련 cv2 : 이미지나 비디오 파일을 읽고, 변환하고, 처리하는 다양한 함수와 도구를 제공합니다. 주로 컴퓨터 비전과 이미지 처리를 위해사용 numpy: 파이썬에서 수치 계산을 위한 핵심 라이브러리입니다. 다차원 배열 객체와 배열 연산에 대한 다양한 함수와 도구를 제공 주로 배열 데이터(이미지)의 처리에 사용
   
크롤링 관련 requests: 웹 서버에 GET, POST 등의 요청을 보내고 응답을 받아 정보를 사용하게 해준다.
urllib : 
- urllib.parse는 URL을 파싱하고 조작하는 데 사용
- urllib.request는 URL을 열고 데이터를 가져오는 데 사용
bs4 (BeautifulSoup): HTML과 XML 문서를 파싱하고, 문서 내에서 원하는 요소를 탐색하고 추출하는데 사용

 

이렇게 라이브러리가 모두 준비되었다면

 

1. openai 객체에 자신의 API키를 저장 해 놓는다.

(API키 발급받는 방법은 인터넷에 많이 나와있어서 생략) 

# OpenAI API 키 설정
openai.api_key = "자신의 API 키"

 

2-1. openai 객체를 이용해 기본적인 틀을 만들어 놓는다.  (코드 동일)

- 유저 입력이 들어오면 답변을 출력하게끔 함수로 제작

def ChatGPT_API(user_content):
	# messages 리스트에 사용자 입력을 딕셔너리 형태로 추가
    messages = []
    messages.append({"role": "user", "content": f"{user_content}"})
	
    # AI의 기본적인 설정(다양한 model의 AI와 설정 가능) + 답변 생성
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=messages,
        temperature=0.6
    )
	
    # GPT 의 답변 부분만 추출 (다른 정보와 답변이 같이 넘어옴)
    assistant_content = completion.choices[0].message["content"].strip()
    
    print(f"GPT : \n{assistant_content}")
    return assistant_content

 

3-1 만들어놓은 ChatGPT_API() 함수를 이용해 GPT class를 제작

1) GPT 객체에 주제를 입력.
2) 테그, 목차, 내용 메서드를 실행

3) GPT객체 안의 해당(테그, 목차, 내용)Q에 답변 저장

4) GPT객체 안의 답변 내용이 저장된 Q버퍼를 자유롭게 사용

=> ">(.+?)(\n|$)" : 문자열이 >로 시작해서 \n개행문자 or 문장의 끝 인 문자를 말한다.

class GPT:
    def __init__(self, user_input):
        self.titleQ_tm = queue.Queue()
        self.user = user_input

        self.titleQ = queue.Queue()
        self.bodyQ = queue.Queue()
        self.tagQ = queue.Queue()
    
    # 테그 생성 메서드
    def create_tag(self):
        teg_gpt = ChatGPT_API(
            f'{self.user}라는 주제에 맞는 단어를 다음 형식에 맞춰서 5개만 추천해줘,'\
            f'단어의 맨 앞에는 항상 ">"가 와야 해 \n'\
            f'>word1\n>word2\n>word3 ...')
        
        # GPT의 답변을 설정한 형식대로 Q버퍼에 저장
        for match in re.finditer(r">(.+?)(\n|$)", teg_gpt):
            tm = match.group(1)
            self.tagQ.put(tm)

        if self.tagQ.empty():
            print("GPT응답 오류")
            exit()
        else:
            print("\n테그 생성 완료")
    
    # 목차 생성 메서드
    def create_content(self):
        title_gpt = ChatGPT_API(
            f'{self.user}라는 주제에 맞게 목차를 다음 형식에 맞춰서 5개만 추천해줘,'\
            f'목차의 맨 앞에는 항상 ">"가 와야 해 \n'\
            f'>목차1\n>목차3\n>목차3 ...')

        for match in re.finditer(r">(.+?)(\n|$)", title_gpt):
            tm = match.group(1)
            self.titleQ.put(tm)
            self.titleQ_tm.put(tm)

        if self.titleQ.empty():
            print("GPT응답 오류")
            exit()
        else:
            print("\n목차 생성 완료")

    # 목차별 내용 생성 메서드
    def create_body(self):
        while not self.titleQ_tm.empty():
            self.bodyQ.put(
                ChatGPT_API(
                    f'{self.user}라는 주제와\n \
                    목차 = ({self.titleQ_tm.get()})에 맞는 대한 100자 정도의 내용을 써줘 .')
                )

3-2 만들어놓은 ChatGPT_API() 함수를 이용해 GPT class를 제작

1) GPT객체에 questionFormat()함수에서 만들어진 날씨기반 옷 추천 질문을 입력

2) create_dalle2_question 메서드를 통해 Q버퍼에 답변을 저장

3) Q버퍼에 저장된 질문을 return

class GPT:
   def __init__(self):
      self.tagQ = queue.Queue()
   
   # 
   def create_dalle2_question(self):
      dalle2_question = ChatGPT_API(
         questionFormat()
      )
      # GPT의 답변을 설정한 형식대로 Q버퍼에 저장
      dalle2_question = str(dalle2_question)
      print(f"GPT : \n{assistant_content}")
      print("============================================")
      # GPT가 코멘트를 다는 경우가 있어 그 부분을 제거하기 위한 코드
      assistant_content = assistant_content.split("\n\n")[0]
      print(f"GPT : \n{assistant_content}")
      print("============================================")

      # GPT의 답변을 설정한 형식대로 Q버퍼에 저장
      self.tagQ.put(assistant_content)

      if self.tagQ.empty():
         print("GPT응답 오류")
         exit()
      else:
         print("\ndalle2question 생성 완료")

      return self.tagQ.get()

 

 

[블로그에 포스팅 할 때 html로 url을 직접 넣을 수 있지만 Dalle2가 생성한 url은 10분 후 없어지기 때문에 클립보드에 저장해서 포스팅 하는 방법을 사용] 

4-1 이미지 to 클립보드 부분 제작 

1) 이미지를 클립보드에 저장하기 위해 클립보드를 비우고 저장하는 함수 제작

2) URL에서 이미지를 다운로드 => 바이트 파일

3) 바이트 파일 형식의 이미지 => PIL라이브러리의 Image객체로 저장

4) 이미지 크기를 조절

5) PIL객체의 이미지 => 비트맵 형식의 이미지(클립보드에 맞는 형식)

6) 클립 보드에 저장

# 이미지 클립보드에 저장
def send_to_clipboard(clip_type, data):
    win32clipboard.OpenClipboard()
    win32clipboard.EmptyClipboard()
    win32clipboard.SetClipboardData(clip_type, data)
    win32clipboard.CloseClipboard()

def big_image(image_url):
    # 이미지 다운로드
    response = requests.get(image_url)
    img_data = response.content

    # BytesIO를 사용하여 바이트 데이터를 파일처럼 취급
    img_file = BytesIO(img_data)

    # 이미지를 PIL.Image 객체로 변환
    image = Image.open(img_file)

    # 이미지 크기를 2배로 늘리기
    width, height = image.size
    new_width, new_height = width * 2, height * 2
    resized_image = image.resize((new_width, new_height))

    # 이미지를 비트맵 형식으로 변환
    output = BytesIO()
    resized_image.convert("RGB").save(output, "BMP")
    data = output.getvalue()[14:]
    output.close()

    # 클립보드에 이미지 저장
    send_to_clipboard(win32con.CF_DIB, data)
    print("이미지가 클립보드에 저장되었습니다.")

4-2 이미지 to CV화면 출력 부분 제작 

def URLimage_change_output():
   # 이미지 다운로드
   with urllib.request.urlopen(url) as url_response:
      image_array = np.asarray(bytearray(url_response.read()), dtype=np.uint8)
      image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)

   # 원하는 이미지 크기를 지정합니다. (예: 512x512)
   new_width = 512
   new_height = 512

   # 이미지 크기를 조절합니다.
   resized_image = cv2.resize(image, (new_width, new_height))

   # 이미지 출력
   cv2.imshow('Recommended clothes of the day', resized_image)
   cv2.waitKey(0)
   cv2.destroyAllWindows()

5-1 Dalle2 class 제작

1) Dalle2에는 googletrans 객체가 들어가야 하므로 객체 변수로 googletrans  객체 저장(중첩클레스)

- 함수형태로 밖으로 빼도 상관X

2)  한국어를 넣으면 영어로 번역하는 설정을 해둔 번역 메서드를 제작

3) ChatGPT_API와 비슷한 형식으로 create_image메서드 제작

class Dalle2:
    def __init__(self, user_in):
        self.translator = googletrans.Translator()
        self.dell2 = user_in

    # 번역 메서드
    def translate(self):
        result1 = self.translator.translate(self.dell2, src='ko', dest='en')
        self.dell2 = result1.text
        print(self.dell2)

    def create_image(self):
        user_pic = f'{self.dell2} realistic 3d images'
        print(user_pic)

        response = openai.Image.create(
            prompt=user_pic,
            n=1,
            size="256x256"
        )
        image_url = response['data'][0]['url']
        print(f"url = \n{image_url}")
        big_image(image_url)

5-2 Dalle2 class 제작

1) googletrans는 class밖에서 이미 함

2) ChatGPT_API와 비슷한 형식으로 create_image메서드 제작

# Dalle2===================
class Dalle2:
   def __init__(self, user_in, user_age, user_s):
      self.dell2 = user_in
      self.user_age = user_age
      self.user_s = user_s

   # 이미지 생성_출력 메서드
   def create_image(self):
      # Dalle2에 입력할 문장 구성
      imagePrompt =   f"{self.dell2} {self.user_age}-year-old {self.user_s} fashion model wearing clothes" 
      print(imagePrompt)

      response = openai.Image.create(
         prompt=imagePrompt,
         n=1,
         size="256x256"
      )
      image_url = response['data'][0]['url']
      print(f"url = \n{image_url}")
      URLimage_change_output(image_url)

 

[ 원래 해당 블로그의 API를 활용해서 포스팅을 하려 했지만 일정 시간마다 재인증을 해야하고, 1시간이라는 제한이 존재해 프로그램이 직접 키보드와 마우스를 조작해 자동으로 포스팅을 하게 대체]

 

6-1 키보드와 마우스를 조작해 프로그램이 자동으로 포스팅 하는 코드 제작

1) 사용자가 첨부한 txt파일 의 주제 개수만큼 아래 과정을 반복

 

2) 앞서 제작한GPT, DALLE2 를 이용해서 주제에 맞는 테그, 목차, 내용을 생성

3) 키보드와 마우스를 조작해 블로그의 글쓰기 창을 연다.

4) 블로그에 생성된 테그, 목차, 내용, 이미지를 포스팅

5) 창을 받으면서 포스팅을 종료

def writeKorean(txt, dur):
    pyperclip.copy(txt)
    time.sleep(dur)
    pyautogui.hotkey("ctrl", "v")
    return

for i in range(txt_q.qsize()//2):
    user_input = txt_q.get().strip()
    title = user_input
    dell2 = txt_q.get().strip()
    
    my_GPT = GPT(user_input)
    my_GPT.create_tag()
    my_GPT.create_content()
    my_GPT.create_body()
    
    my_DALLE2 = Dalle2(dell2)
    my_DALLE2.create_image()

    # 글쓰기 열기
    pyautogui.doubleClick(434, 138)
    time.sleep(4)

    # 팝업창 닫기
    pyautogui.doubleClick(662, 193)
    time.sleep(1)

    # 이미지 포스팅
    pyautogui.doubleClick(213, 414)
    pyautogui.hotkey("ctrl", "v")
    time.sleep(3)
    pyautogui.press("enter", interval=0.1)
    time.sleep(0.3)
    pyautogui.press("enter", interval=0.1)

    # 제목 쓰기
    pyautogui.click(92, 263)
    # writeKorean(fr' (AI)', 0.1)
    writeKorean(fr'{title} (AI)', 0.1)
    time.sleep(0.3)
    # 내용 쓰기
    pyautogui.click(151, 712)
    time.sleep(0.3)

    # 소주제 + 내용 포스팅
    while not my_GPT.contentQ.empty():
        line = my_GPT.titleQ.get()
        writeKorean(fr'{line}', 0.1)
        pyautogui.press("enter", interval=0.1)
        writeKorean(fr'{my_GPT.contentQ.get()}', 0.1)
        pyautogui.press("enter", interval=0.1)
        pyautogui.press("enter", interval=0.1)

    pyautogui.press("tab", interval=0.1)
    # 테그 포스팅
    while not my_GPT.tegQ.empty():
        if my_GPT.tegQ.empty():
            break
        teg = my_GPT.tegQ.get()
        time.sleep(0.3)
        writeKorean(fr'{teg}', 0.1)
        pyautogui.press("enter", interval=0.1)
    
    time.sleep(0.3)
    # 발행
    pyautogui.click(858, 995)
    time.sleep(0.3)
    # 공개
    pyautogui.click(129, 692, duration=0.1)
    time.sleep(0.4)
    pyautogui.click(511, 975)
    time.sleep(1)
    #창 닫기
    pyautogui.click(935, 16)

    print("포스팅 종료")

 

6-2  네이버에서 날씨 정보 크롤링 + 사용자 정보 얻는 과정

1) requests를 사용하여 날씨 정보를 가져옵니다.
2) BeautifulSoup을 사용하여 HTML 코드를 파싱합니다.
3) 위치 정보, 최저 온도, 최고 온도 등을 추출합니다.
4) 사용자로부터 나이, 성별, 요청 조건을 입력받습니다.

html = requests.get('https://search.naver.com/search.naver?query=날씨')

# HTML 코드 파싱하기
naver_w = bs4.BeautifulSoup(html.text,'html.parser')

# 위치정보 추출
place_tag = naver_w.find('h2', {'class': 'blind'})

place = place_tag.get_text(strip=True)

# 온도 정보 추출하기
lowest = naver_w.find('span', {'class': 'lowest'}).get_text(strip=True)
lowest = re.search(r'-?\d+°', lowest).group()

highest = naver_w.find('span', {'class': 'highest'}).get_text(strip=True)
highest = re.search(r'-?\d+°', highest).group()

temperature_tag = naver_w.find('div', {'class': 'temperature_text'}).strong
temperature = temperature_tag.get_text(strip=True)
temperature = re.search(r'\d+\.\d+°', temperature).group()

# 습도 정보 추출하기
desc_tag = naver_w.find('dd', {'class': 'desc'})
desc = desc_tag.get_text(strip=True)

#미세먼지 정보 추출하기
txt = naver_w.find('span', {'class': 'txt'}).get_text(strip=True)

#강수 정보 추출하기
blind = naver_w.find('span', {'class': 'weather before_slash'}).get_text(strip=True)

user_age = input("나이를 입력하세요 : ")
user_s = input("성별을 숫자로 입력하세요(1.여, 2.남) : ")
if user_s == '1':
   user_s = 'female'
else :
   user_s = 'male'

user_req = input("ex) 출근할 때 입을 옷을 추천해 주세요\n설정할 조건을 알려주세요 : ")

 

7. 사용자가 등록한 txt파일을 읽어들이는 부분 제작

- 형식은 주제\n 그림주제\n \n 주제\n 그림주제\n \n  ... 으로 고정

def load_into_queue(filename):
    q = queue.Queue()
    with open(filename, 'r', encoding = 'utf-8') as f:
        for line in f:
            if '\n' != line:
                q.put(line)
    return q

file_path = input("txt파일의 경로를 입력하세요: ")
file_path.replace("\\", "/")

txt_q = load_into_queue(file_path)

 

댓글