first-commit

This commit is contained in:
2025-06-24 17:02:50 +09:00
commit 380af2156c
9 changed files with 346 additions and 0 deletions

12
data/config.sample.py Normal file
View File

@ -0,0 +1,12 @@
FTP_CONFIG = {
'HOST' : 'FTP_HOST',
'USER' : 'FTP_USER',
'PASS' : 'FTP_PW',
'UPLOAD_DIR' : 'UPLOAD_DIR'
}
DB_CONFIG = {
'HOST' : 'HOST',
'USER' : 'DBUSER',
'DBNAME' : 'DBNAME',
'PASS' : 'DB_PW'
}

178
data/gnu_autoupload.py Normal file
View File

@ -0,0 +1,178 @@
import pymysql, ftputil, hashlib, os, sys
from datetime import datetime
from PIL import Image
import config import *
import subprocess
import time
import os
# 오늘 날짜 기반으로 파일명 생성
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(['python', capture_script])
#time.sleep(1)
print(f"[{datetime.now().strftime('%H:%M:%S')}] {weather_file} 파일을 확인했습니다. 계속 진행합니다.")
# 파일 업로드 경로 설정
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
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를 이용하여 파일을 업로드하는 함수입니다.
try:
with ftputil.FTPHost(FTP_CONFIG['HOST'], FTP_CONFIG['USER'], FTP_CONFIG['PASS']) as fh:
fh.chdir(FTP_CONFIG['UPLOAD_DIR'])
fh.upload(filename, bf_file, callback = None)
print(f"[업로드 완료] {filename}")
except Exception as e:
print(f"[FTP 오류] {type(e).__name__}: {e}")
return
def get_filename(filename): # 파일명을 변환하는 함수입니다.
ms = datetime.now().microsecond
encoded_name = filename.encode('utf-8')
result = f'{ms}_{hashlib.sha1(encoded_name).hexdigest()}'
return result
def board_write(board, subject, content, mb_id, nickname, ca_name=None, file_list=None):
conn = pymysql.connect(host=DB_CONFIG['HOST'],
user=DB_CONFIG['USER'],
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(sql)
conn.commit()
wr_num = str(int(curs.fetchone()[0]) - 1)
print(f"[결과] wr_num: {wr_num}")
now = datetime.today().strftime('%Y-%m-%d %H:%M:%S')
sql = f"""INSERT INTO g5_write_{board} SET wr_num = {wr_num},
wr_reply = '', wr_comment = 0, ca_name = '{ca_name}', wr_option = 'html1', wr_subject = '{subject}',
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(sql)
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(sql)
conn.commit()
print("[결과] wr_parent 업데이트 완료")
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}')"""
print(f"[쿼리 실행] {sql}")
curs.execute(sql)
conn.commit()
print("[결과] 새글 INSERT 완료")
sql = f"SELECT bo_count_write FROM g5_board WHERE bo_table = '{board}'"
print(f"[쿼리 실행] {sql}")
curs.execute(sql)
bo_count_write = str(int(curs.fetchone()[0]))
conn.commit()
print(f"[결과] 현재 게시글 수: {bo_count_write}")
sql = f"UPDATE g5_board SET bo_count_write = {bo_count_write} + 1 WHERE bo_table = '{board}'"
print(f"[쿼리 실행] {sql}")
curs.execute(sql)
conn.commit()
print("[결과] 게시글 수 증가 완료")
if not file_list:
print("[종료] 첨부파일 없음. 연결 종료.")
conn.close()
sys.exit()
file_count = len(file_list)
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()
print(f"[결과] 파일({file}) 업로드 정보 INSERT 완료")
sql = f"UPDATE g5_write_{board} SET wr_file = '{file_count}' WHERE wr_id = '{wr_id}'"
print(f"[쿼리 실행] {sql}")
curs.execute(sql)
conn.commit()
print(f"[결과] 첨부파일 수 wr_file = {file_count} 업데이트 완료")
conn.close()
print("[종료] MySQL 연결 종료")
def main():
board = MAIN['board']
ca_name = MAIN['ca_name']
subject = MAIN['subject']
content = MAIN['content']
mb_id = MAIN['mb_id']
nickname = MAIN['nickname']
file_list = [MAIN['file1'], MAIN['file2']]
# file_list = [MAIN['file1']]
board_write(board, subject, content, mb_id, nickname, ca_name, file_list)
if __name__ == "__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} 파일을 찾을 수 없습니다.")

56
data/weather.py Normal file
View File

@ -0,0 +1,56 @@
import requests
import json
import re
from datetime import datetime
base_date = datetime.now().strftime('%Y%m%d') # 오늘 날짜
serviceKey = 'mHrZoSnzVc+2S4dpCe3A1CgI9cAu1BRttqRdoEy9RGbnKAKyQT4sqcESDqqY3grgBGQMuLeEgWIS3Qxi8rcDVA=='
url = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst"
params = {
'serviceKey': serviceKey,
'numOfRows': '1000',
'pageNo': '1',
'dataType': 'JSON',
'base_date': base_date,
'base_time': '0800', # 02:00 부터 3시간 단위 발표
'nx': '57',
'ny': '130'
}
response = requests.get(url, params=params)
def parse_precip(value):
if value == '강수없음':
return 0.0
elif '1mm 미만' in value:
return 0.5
else:
# 숫자만 추출 (예: '1.0mm' → 1.0)
match = re.search(r"[\d.]+", value)
if match:
return float(match.group())
else:
return 0.0
try:
data = response.json()
total_rainfall = 0.0
print("📅 시간대별 강수량 (단기예보 기준):")
for item in data['response']['body']['items']['item']:
if item['category'] == 'PCP' and item['fcstDate'] == base_date:
time = item['fcstTime']
if 900 < int(time) < 2300:
value = item['fcstValue']
mm = parse_precip(value)
print(f" {time}시 → {mm}mm")
total_rainfall += mm
print(f"\n🌧️ 총 예상 강수량: {total_rainfall:.1f}mm")
except json.decoder.JSONDecodeError:
print("⚠️ 응답이 JSON 형식이 아닙니다.")
print(response.text)

43
data/weather_capture.py Normal file
View File

@ -0,0 +1,43 @@
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from datetime import datetime
import os
import time
# 현재 스크립트 경로 기준 저장
script_dir = os.path.dirname(os.path.abspath(__file__))
# 브라우저 옵션 설정
options = Options()
options.add_argument('--headless')
options.add_argument('--window-size=1802,1467')
driver = webdriver.Chrome(options=options)
driver.get('https://www.weather.go.kr/w/weather/forecast/short-term.do#dong/4148026200/37.73208578534846/126.79463099866948')
time.sleep(5)
# 첫 번째 탭 클릭
tab_button = driver.find_element(By.XPATH, '//*[@id="digital-forecast"]/div[1]/div[3]/div[1]/div/div/a[2]')
ActionChains(driver).move_to_element(tab_button).click().perform()
time.sleep(2)
# 두 번째 항목 클릭
list_button = driver.find_element(By.XPATH, '//*[@id="digital-forecast"]/div[1]/div[3]/ul/div[1]/a[2]')
ActionChains(driver).move_to_element(list_button).click().perform()
time.sleep(2)
# 캡처 대상 요소 찾기
target_element = driver.find_element(By.XPATH, '/html/body/div[2]/section/div/div[2]')
# 파일 저장 경로 설정
#timestamp = datetime.now().strftime('%Y%m%d_%H%M') #시간까지 표시
timestamp = datetime.now().strftime('%Y%m%d') #날짜만 표시
save_path = os.path.join(script_dir, f'weather_capture_{timestamp}.png')
# 요소 스크린샷 저장
target_element.screenshot(save_path)
driver.quit()
print(f'📸 캡처 완료! 저장 위치: {save_path}')