- 1
- 이니스프리
- 조회 수 24004
안녕하세요? ^-^
이번 주말에는 비가 많이 오네요~
다들 비 피해 없으시기를 기원할게요!
저번에 '소스 공유' 게시판에 작성했던 'Python으로 구현한 그누보드 자동 글쓰기 함수'( https://studyforus.com/share/808613 )를 보완하여
멀티 파일 업로드까지 가능하도록 업데이트 하였습니다 ^^
그누보드 PHP 내장함수를 사용할 수 없기 때문에 해당 함수를 파이썬으로 변환하여 적절히 처리했구요~
다만 replace_filename() 함수에서 SHA1 방식 암호화를 하는 부분 중에 사용자 IP는 제외했습니다.
참고로 pymysql과 ftputil을 제외하면 Anaconda에 포함된 기본 모듈만 사용했어요 :)
아래 스크립트는 외부에서 그누보드 DB 및 FTP에 접속하는 것을 전제로 구현되었기 때문에
호스팅 또는 VPS에서 DB와 FTP에 외부접속할 수 있도록 설정하셔야 정상적으로 작동합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | import pymysql, ftputil, hashlib, os, sys from datetime import datetime from PIL import Image 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를 이용하여 파일을 업로드하는 함수입니다. with ftputil.FTPHost( 'URL을입력하세요' , 'FTP계정명을입력하세요' , 'FTP비번을입력하세요' ) as fh: fh.chdir( '업로드할디렉토리를입력하세요ex./web/data/file/board' ) fh.upload(filename, bf_file, callback = None ) 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, file_list = None ): # MySQL connection 및 cursor 생성 conn = pymysql.connect(host = '연결할URL을입력하세요' , user = 'MySQL유저명을입력하세요' , password = 'MySQL비번을입력하세요' , db = 'MySQL디비명을입력하세요' , charset = 'utf8' ) curs = conn.cursor() # 작성글 INSERT sql = f "select wr_num from g5_write_{board}" curs.execute(sql) wr_num = str ( int (curs.fetchone()[ 0 ]) - 1 ) print (wr_num) now = datetime.today().strftime( '%Y-%m-%d %H:%M:%S' ) # 그누보드의 날짜 형식 준수 (ex: 2021-04-05 23:45:15) sql = f"insert into g5_write_{board} set wr_num = {wr_num}, \ wr_reply = ' ', wr_comment = 0, 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_1 = ' ', wr_2 = ' ', wr_3 = ' ', wr_4 = ' ', wr_5 = ' ', \ wr_6 = ' ', wr_7 = ' ', wr_8 = ' ', wr_9 = ' ', wr_10 = ' ', \ wr_comment_reply = ' ', wr_facebook_user = ' ', wr_twitter_user = ' ', \ as_re_name = ' ', as_tag = ' ', as_map = ' ', as_icon = ' ', as_thumb = ' ', as_video = ' '" curs.execute(sql) # 부모 아이디에 UPDATE sql = f "select wr_id from g5_write_{board}" curs.execute(sql) wr_id = str (curs.fetchall()[ - 1 ][ 0 ]) print (f "wr_id : {wr_id}" ) sql = f "update g5_write_{board} set wr_parent = {wr_id} where wr_id = {wr_id}" curs.execute(sql) # 새글 INSERT 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}' )" curs.execute(sql) # 게시글 1 증가 sql = f "select bo_count_write from g5_board where bo_table = '{board}'" curs.execute(sql) bo_count_write = str ( int (curs.fetchone()[ 0 ])) print (curs.fetchall()) print (bo_count_write) sql = f "update g5_board set bo_count_write = {bo_count_write} + 1 where bo_table = '{board}'" curs.execute(sql) # 파일 업로드 및 관련 정보를 테이블에 저장 if not file_list or file_list = = [''] or file_list = = []: # 첨부파일이 없는 경우에는 스크립트를 중단합니다. 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' : # 이미지 파일의 경우 가로 및 세로를 구하고, 그 외의 경우에는 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 = '{file}' , bf_file = '{bf_file}' , \ bf_content = ' ', bf_download = 0, bf_filesize = ' {size}', \ bf_width = '{width}' , bf_height = '{height}' , bf_type = '{type}' , bf_datetime = '{now}' " curs.execute(sql) # 파일의 개수를 게시물에 업데이트 sql = f "update g5_write_board set wr_file = '{file_count}' where wr_id = '{wr_id}'" curs.execute(sql) # MySQL connection 닫기 conn.close() return def main(): board = '게시판명을입력하세요' subject = '제목을입력하세요' content = '내용을입력하세요HTML태그도가능합니다' mb_id = '아이디를입력하세요' nickname = '닉네임을입력하세요' file_list = [ '업로드할파일명을입력하세요' , '업로드할파일명을입력하세요' ] board_write(board, subject, content, mb_id, nickname, file_list) if __name__ = = "__main__" : main() |
위 스크립트를 활용하여 3개의 파일(PNG, GIF, PY)을 업로드한 결과는 다음과 같습니다 ^^
영문이나 숫자가 아닌 한글로 된 파일명도 정상적으로 업로드되는 것을 확인했어요~
아미나에서 테스트하였지만 그누보드 5.4에서도 대동소이할 것으로 생각되네요 :)
(냥냥펀치!)
테스트해보니 작성자명을 계정의 실제 닉네임과 달리 적용한 경우에
위 함수로 글을 올린 후 로그인하여 글을 수정하면 해당 계정의 실제 닉네임으로 변경되는 점을 주의하셔야 됩니다!
crontab에 넣고 장기적으로 실사용하시려면 다음과 같은 부분을 보완하시는 것을 권장합니다 ^^
1. 오류 처리
파일 업로드 실패 등의 경우에 글과 첨부파일을 삭제하고 sys.exit() 하면 좋을 것 같네요.
2. 보안
사이트 운영자만 글 내용과 이미지를 선별하여 사용할 것이라면 별다른 보안 이슈는 없겠지만,
보안 관련하여 명랑폐인 님께서 올려주신 '게시판 파싱후 글등록 처리 함수'을 참고하시면 도움이 될듯요~
만약 타 사이트에서 파싱한 결과물을 위 함수를 이용하여 그대로 올리는 목적으로 사용하시려면
SQL injection, steganography 등 기법에도 대응할 수 있도록 보완해야 안전할 것 같습니다 ㅎㄷㄷ
허접한 스크립트인데 읽어주셔서 감사합니다!
다음에는 위 함수를 활용하여 브라우저에 접속하지 않은 상태에서
곧바로 글 작성 및 첨부파일 업로드를 할 수 있는 GUI 프로그램을 작성해볼게요~
그럼 다들 좋은 주말 되시고, 내일까지 비가 온다니 아침에 우산 꼭 챙기세요 ^-^
감사합니다!!