성능 향상

This commit is contained in:
2025-06-27 10:47:23 +09:00
parent a5a1da6770
commit 82958bd843

View File

@ -1,180 +1,234 @@
import pymysql, ftputil, hashlib, os, sys # -*- coding: utf-8 -*-
"""
gnu_autoupload.py
기능:
1. Selenium을 이용해 날씨 정보를 캡처 (weather_capture.py 호출)
2. FTP를 이용해 이미지 업로드
3. 그누보드 DB에 게시글 및 첨부파일 정보 자동 등록
"""
import os
import sys
import time
import subprocess
import tempfile
import hashlib
from datetime import datetime from datetime import datetime
from PIL import Image from PIL import Image
import subprocess import pymysql
import time import ftputil
import os
from config import DB_CONFIG, FTP_CONFIG, MAIN from config import DB_CONFIG, FTP_CONFIG, MAIN
# 오늘 날짜 기반으로 파일명 생성
today_str = datetime.today().strftime('%Y%m%d')
base_dir = os.path.dirname(os.path.abspath(__file__)) # 현재 파이썬 파일의 경로
capture_script = os.path.join(base_dir, 'weather_capture.py')
weather_filename = f'weather_capture_{today_str}.png'
weather_file = os.path.join(base_dir, weather_filename)
# 파일 존재 확인 및 생성 루프 # ---------------------------
while not os.path.isfile(weather_file): # 이미지 캡처 관련 함수
print(f"[{datetime.now().strftime('%H:%M:%S')}] {weather_file} 파일이 없습니다. {capture_script} 실행 중...") # ---------------------------
subprocess.call(['python3', capture_script]) def capture_image(script_path, output_path, max_attempts=5):
#time.sleep(1) """
weather_capture.py를 실행해 이미지 파일을 생성
print(f"[{datetime.now().strftime('%H:%M:%S')}] {weather_file} 파일을 확인했습니다. 계속 진행합니다.") :param script_path: 캡처 스크립트 경로
:param output_path: 기대되는 이미지 파일 경로
# 파일 업로드 경로 설정 :param max_attempts: 최대 재시도 횟수
FTP_CONFIG['UPLOAD_DIR'] = f"/www/data/file/{MAIN['board']}/" :return: 성공 여부 (True/False)
"""
# 게시글 제목을 오늘 날짜를 반영하도록 수정 for attempt in range(max_attempts):
MAIN['subject'] = f"[{datetime.now().strftime('%Y-%m-%d')}] 날씨 정보" print(f"[{datetime.now().strftime('%H:%M:%S')}] 캡처 시도 {attempt + 1}/{max_attempts}")
MAIN['file1'] = MAIN['file2'] = weather_file try:
subprocess.run(['python3', script_path], check=True)
except subprocess.CalledProcessError as e:
print(f"[오류] weather_capture.py 실행 실패: {e}")
if os.path.isfile(output_path):
print(f"[성공] 이미지 캡처 완료: {output_path}")
return True
time.sleep(2)
print(f"[실패] {max_attempts}회 시도 후에도 이미지가 생성되지 않았습니다.")
return False
# ---------------------------
# 파일 업로드 및 처리 관련
# ---------------------------
def file_type(ext):
"""
파일 확장자를 기반으로 그누보드용 파일 유형 코드 반환
"""
return {
'gif': '1', 'jpeg': '2', 'jpg': '2', 'png': '3', 'swf': '4',
'psd': '5', 'bmp': '6', 'tif': '7', 'tiff': '7', 'jpc': '9',
'jp2': '10', 'jpx': '11', 'jb2': '12', 'swc': '13', 'iff': '14',
'wbmp': '15', 'xbm': '16'
}.get(ext.lower(), '0')
def file_type(x): # 그누보드의 bf_type 값을 반환하는 함수입니다. (디폴트 : 0)
return {'gif' : '1', 'jpeg' : '2', 'jpg' : '2', 'png' : '3', 'swf' : '4', 'psd' : '5',
'bmp' : '6', 'tif' : '7', 'tiff' : '7', 'jpc' : '9', 'jp2' : '10', 'jpx' : '11',
'jb2' : '12', 'swc' : '13', 'iff' : '14', 'wbmp' : '15', 'xbm' : '16'}.get(x.lower(), '0')
def file_upload(filename, bf_file): # FTP를 이용하여 파일을 업로드하는 함수입니다. def get_filename(filename):
"""
원본 파일명을 기반으로 해시된 파일명을 생성
"""
ms = datetime.now().microsecond
encoded_name = filename.encode('utf-8')
return f'{ms}_{hashlib.sha1(encoded_name).hexdigest()}'
def file_upload(filename, bf_file):
"""
FTP 서버에 파일 업로드
:param filename: 로컬 경로
:param bf_file: 서버에 저장될 파일명
"""
try: try:
with ftputil.FTPHost(FTP_CONFIG['HOST'], FTP_CONFIG['USER'], FTP_CONFIG['PASS']) as fh: with ftputil.FTPHost(FTP_CONFIG['HOST'], FTP_CONFIG['USER'], FTP_CONFIG['PASS']) as fh:
fh.chdir(FTP_CONFIG['UPLOAD_DIR']) fh.chdir(FTP_CONFIG['UPLOAD_DIR'])
fh.upload(filename, bf_file, callback = None) fh.upload(filename, bf_file)
print(f"[업로드 완료] {filename}") print(f"[업로드 완료] {filename}{bf_file}")
return True
except Exception as e: except Exception as e:
print(f"[FTP 오류] {type(e).__name__}: {e}") print(f"[FTP 오류] {type(e).__name__}: {e}")
return False
return
def get_filename(filename): # 파일명을 변환하는 함수입니다. # ---------------------------
ms = datetime.now().microsecond # 게시글 작성 함수
encoded_name = filename.encode('utf-8') # ---------------------------
result = f'{ms}_{hashlib.sha1(encoded_name).hexdigest()}' def write_board(board, subject, content, mb_id, nickname, ca_name=None, file_list=None):
return result """
그누보드 DB에 게시글과 파일을 한 트랜잭션으로 삽입
:param board: 게시판 ID
:param subject: 제목
:param content: 내용
:param mb_id: 회원 ID
:param nickname: 작성자 이름
:param ca_name: 카테고리명
:param file_list: 첨부파일 경로 리스트
"""
try:
# 데이터베이스 연결
conn = pymysql.connect(
host=DB_CONFIG['HOST'],
user=DB_CONFIG['USER'],
db=DB_CONFIG['DBNAME'],
password=DB_CONFIG['PASS'],
charset='utf8'
)
curs = conn.cursor()
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
def board_write(board, subject, content, mb_id, nickname, ca_name=None, file_list=None): # 게시글 번호(wr_num) 계산
conn = pymysql.connect(host=DB_CONFIG['HOST'], curs.execute(f"SELECT wr_num FROM g5_write_{board}")
user=DB_CONFIG['USER'], wr_num = str(int(curs.fetchone()[0]) - 1)
db=DB_CONFIG['DBNAME'],
password=DB_CONFIG['PASS'],
charset='utf8')
curs = conn.cursor()
sql = f"SELECT wr_num FROM g5_write_{board}" # 게시글 삽입
print(f"[쿼리 실행] {sql}") curs.execute(f"""INSERT INTO g5_write_{board} SET wr_num = {wr_num},
curs.execute(sql) wr_reply = '', wr_comment = 0, ca_name = %s, wr_option = 'html1', wr_subject = %s,
conn.commit() wr_content = %s, wr_link1 = '', wr_link2 = '', wr_link1_hit = 0, wr_link2_hit = 0,
wr_num = str(int(curs.fetchone()[0]) - 1) wr_hit = 1, wr_good = 0, wr_nogood = 0, mb_id = %s, wr_password = '',
print(f"[결과] wr_num: {wr_num}") wr_name = %s, wr_email = '', wr_homepage = '', wr_datetime = %s, wr_last = %s,
wr_ip = '127.0.0.1'""",
(ca_name, subject, content, mb_id, nickname, now, now))
now = datetime.today().strftime('%Y-%m-%d %H:%M:%S') # wr_id 조회
curs.execute(f"SELECT wr_id FROM g5_write_{board} ORDER BY wr_id DESC LIMIT 1")
wr_id = str(curs.fetchone()[0])
sql = f"""INSERT INTO g5_write_{board} SET wr_num = {wr_num}, # wr_parent 업데이트
wr_reply = '', wr_comment = 0, ca_name = '{ca_name}', wr_option = 'html1', wr_subject = '{subject}', curs.execute(f"UPDATE g5_write_{board} SET wr_parent = {wr_id} WHERE wr_id = {wr_id}")
wr_content = '{content}', wr_link1 = '', wr_link2 = '',
wr_link1_hit = 0, wr_link2_hit = 0, wr_hit = 1, wr_good = 0, wr_nogood = 0,
mb_id = '{mb_id}', wr_password = '', wr_name = '{nickname}', wr_email = '', wr_homepage = '',
wr_datetime = '{now}', wr_last = '{now}', wr_ip = '111.111.111.111',
wr_comment_reply = '', wr_facebook_user = '', wr_twitter_user = '',
wr_1 = '', wr_2 = '', wr_3 = '', wr_4 = '', wr_5 = '', wr_6 = '', wr_7 = '', wr_8 = '', wr_9 = '', wr_10 = ''
"""
print(f"[쿼리 실행] {sql}")
curs.execute(sql)
conn.commit()
print("[결과] 게시글 INSERT 완료")
sql = f"SELECT wr_id FROM g5_write_{board}" # 새글 테이블 등록
print(f"[쿼리 실행] {sql}") curs.execute(f"""INSERT INTO g5_board_new (bo_table, wr_id, wr_parent, bn_datetime, mb_id)
curs.execute(sql) VALUES (%s, %s, %s, %s, %s)""", (board, wr_id, wr_id, now, mb_id))
wr_id = str(curs.fetchall()[-1][0])
conn.commit()
print(f"[결과] wr_id: {wr_id}")
sql = f"UPDATE g5_write_{board} SET wr_parent = {wr_id} WHERE wr_id = {wr_id}" # 게시글 수 증가
print(f"[쿼리 실행] {sql}") curs.execute(f"SELECT bo_count_write FROM g5_board WHERE bo_table = %s", (board,))
curs.execute(sql) bo_count_write = int(curs.fetchone()[0])
conn.commit() curs.execute(f"UPDATE g5_board SET bo_count_write = %s WHERE bo_table = %s",
print("[결과] wr_parent 업데이트 완료") (bo_count_write + 1, board))
sql = f"""INSERT INTO g5_board_new (bo_table, wr_id, wr_parent, bn_datetime, mb_id) # 파일 업로드 처리
VALUES ('{board}', '{wr_id}', '{wr_id}', '{now}', '{mb_id}')""" file_count = 0
print(f"[쿼리 실행] {sql}") if file_list:
curs.execute(sql) for idx, file in enumerate(file_list):
conn.commit() ext = os.path.splitext(file)[1].lstrip('.')
print("[결과] 새글 INSERT 완료") bf_file = f"{get_filename(file)}.{ext}"
sql = f"SELECT bo_count_write FROM g5_board WHERE bo_table = '{board}'" if file_upload(file, bf_file):
print(f"[쿼리 실행] {sql}") img_type = file_type(ext)
curs.execute(sql) width, height = (0, 0)
bo_count_write = str(int(curs.fetchone()[0])) if img_type != '0':
conn.commit() with Image.open(file) as img:
print(f"[결과] 현재 게시글 수: {bo_count_write}") width, height = img.size
sql = f"UPDATE g5_board SET bo_count_write = {bo_count_write} + 1 WHERE bo_table = '{board}'" size = os.path.getsize(file)
print(f"[쿼리 실행] {sql}") curs.execute(f"""INSERT INTO g5_board_file SET bo_table = %s, wr_id = %s, bf_no = %s,
curs.execute(sql) bf_source = %s, bf_file = %s, bf_content = '', bf_download = 0,
conn.commit() bf_filesize = %s, bf_width = %s, bf_height = %s, bf_type = %s, bf_datetime = %s""",
print("[결과] 게시글 수 증가 완료") (board, wr_id, idx, os.path.basename(file), bf_file,
size, width, height, img_type, now))
file_count += 1
else:
print(f"[경고] 파일 업로드 실패: {file}")
if not file_list: # 첨부파일 수 업데이트
print("[종료] 첨부파일 없음. 연결 종료.") curs.execute(f"UPDATE g5_write_{board} SET wr_file = %s WHERE wr_id = %s", (file_count, wr_id))
conn.close()
sys.exit()
file_count = len(file_list) # 트랜잭션 커밋 (단 1회)
for cnt, file in enumerate(file_list):
ext = os.path.splitext(file)[1].lstrip('.')
bf_file = f'{get_filename(file)}.{ext}'
file_upload(file, bf_file)
type = file_type(ext)
if type != '0':
im = Image.open(file)
width, height = im.size
else:
width, height = 0, 0
size = os.path.getsize(file)
sql = f"""INSERT INTO g5_board_file SET bo_table = '{board}', wr_id = '{wr_id}',
bf_no = '{cnt}', bf_source = '{weather_filename}', bf_file = '{bf_file}',
bf_content = '', bf_download = 0, bf_filesize = '{size}',
bf_width = '{width}', bf_height = '{height}', bf_type = '{type}', bf_datetime = '{now}'"""
print(f"[쿼리 실행] {sql}")
curs.execute(sql)
conn.commit() conn.commit()
print(f"[결과] 파일({file}) 업로드 정보 INSERT 완료") print("[완료] 게시글 작성 및 파일 업로드 완료")
sql = f"UPDATE g5_write_{board} SET wr_file = '{file_count}' WHERE wr_id = '{wr_id}'" except Exception as e:
print(f"[쿼리 실행] {sql}") # 예외 발생 시 롤백
curs.execute(sql) conn.rollback()
conn.commit() print(f"[DB 오류] {type(e).__name__}: {e}")
print(f"[결과] 첨부파일 수 wr_file = {file_count} 업데이트 완료") finally:
# 연결 종료
conn.close() conn.close()
print("[종료] MySQL 연결 종료")
# ---------------------------
# 메인 실행 함수
# ---------------------------
def main(): def main():
board = MAIN['board'] # 파일 경로 및 이름 설정
ca_name = MAIN['ca_name'] today = datetime.today().strftime('%Y%m%d')
subject = MAIN['subject'] script_dir = os.path.dirname(os.path.abspath(__file__))
content = MAIN['content'] capture_script = os.path.join(script_dir, 'weather_capture.py')
mb_id = MAIN['mb_id'] weather_filename = f'weather_capture_{today}.png'
nickname = MAIN['nickname'] weather_file = os.path.join(script_dir, weather_filename)
file_list = [MAIN['file1'], MAIN['file2']]
# file_list = [MAIN['file1']] # 이미지 생성 시도
board_write(board, subject, content, mb_id, nickname, ca_name, file_list) if not capture_image(capture_script, weather_file):
return
# 설정값 동적 갱신
FTP_CONFIG['UPLOAD_DIR'] = f"/www/data/file/{MAIN['board']}/"
MAIN['subject'] = f"[{datetime.now().strftime('%Y-%m-%d')}] 날씨 정보"
MAIN['file1'] = MAIN['file2'] = weather_file
file_list = [MAIN['file1']]
# 게시글 작성
write_board(
board=MAIN['board'],
subject=MAIN['subject'],
content=MAIN['content'],
mb_id=MAIN['mb_id'],
nickname=MAIN['nickname'],
ca_name=MAIN['ca_name'],
file_list=file_list
)
# 캡처 파일 삭제
try:
if os.path.isfile(weather_file):
os.remove(weather_file)
print(f"[삭제 완료] {weather_file}")
except Exception as e:
print(f"[삭제 오류] {type(e).__name__}: {e}")
# ---------------------------
# 스크립트 실행 진입점
# ---------------------------
if __name__ == "__main__": if __name__ == "__main__":
main() main()
# main() 완료 후 이미지 파일 삭제
if os.path.isfile(weather_file):
try:
os.remove(weather_file)
print(f"[삭제 완료] {weather_file} 파일을 삭제했습니다.")
except Exception as e:
print(f"[삭제 오류] {type(e).__name__}: {e}")
else:
print(f"[파일 없음] {weather_file} 파일을 찾을 수 없습니다.")