• 목록
  • 아래로
  • 위로

안녕하세요? ^-^

 

이번 주말에는 비가 많이 오네요~ 

 

다들 비 피해 없으시기를 기원할게요!

 

 

저번에 '소스 공유' 게시판에 작성했던 'Python으로 구현한 그누보드 자동 글쓰기 함수'( https://studyforus.com/share/808613 )를 보완하여

 

멀티 파일 업로드까지 가능하도록 업데이트 하였습니다 ^^

 

그누보드 PHP 내장함수를 사용할 수 없기 때문에 해당 함수를 파이썬으로 변환하여 적절히 처리했구요~

 

다만 replace_filename() 함수에서 SHA1 방식 암호화를 하는 부분 중에 사용자 IP는 제외했습니다.

 

참고로 pymysql과 ftputil을 제외하면 Anaconda에 포함된 기본 모듈만 사용했어요 :)

 

 

아래 스크립트는 외부에서 그누보드 DB 및 FTP에 접속하는 것을 전제로 구현되었기 때문에

 

호스팅 또는 VPS에서 DB와 FTP에 외부접속할 수 있도록 설정하셔야 정상적으로 작동합니다.

 

 

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에서도 대동소이할 것으로 생각되네요 :)

 

 

img 20210516 084554.png.jpg

(냥냥펀치!)

 

 

테스트해보니 작성자명을 계정의 실제 닉네임과 달리 적용한 경우에

 

위 함수로 글을 올린 후 로그인하여 글을 수정하면 해당 계정의 실제 닉네임으로 변경되는 점을 주의하셔야 됩니다!

 

 

crontab에 넣고 장기적으로 실사용하시려면 다음과 같은 부분을 보완하시는 것을 권장합니다 ^^

 

1. 오류 처리

 

파일 업로드 실패 등의 경우에 글과 첨부파일을 삭제하고 sys.exit() 하면 좋을 것 같네요.

 

 

2. 보안

 

사이트 운영자만 글 내용과 이미지를 선별하여 사용할 것이라면 별다른 보안 이슈는 없겠지만,

 

보안 관련하여 명랑폐인 님께서 올려주신 '게시판 파싱후 글등록 처리 함수'을 참고하시면 도움이 될듯요~

 

https://sir.kr/g5_tip/6977

 

만약 타 사이트에서 파싱한 결과물을 위 함수를 이용하여 그대로 올리는 목적으로 사용하시려면

 

SQL injection, steganography 등 기법에도 대응할 수 있도록 보완해야 안전할 것 같습니다 ㅎㄷㄷ

 

 

허접한 스크립트인데 읽어주셔서 감사합니다!

 

다음에는 위 함수를 활용하여 브라우저에 접속하지 않은 상태에서

 

곧바로 글 작성 및 첨부파일 업로드를 할 수 있는 GUI 프로그램을 작성해볼게요~ 

 

그럼 다들 좋은 주말 되시고, 내일까지 비가 온다니 아침에 우산 꼭 챙기세요 ^-^

 

감사합니다!!

 

OAUTH2님 포함 2명이 추천

추천인 2

작성자
이니스프리 107 Lv. (90%) 931490/933120EXP

당분간 일신상의 사정으로 쪽지나 댓글로 답변을 드리기 어렵습니다. 죄송합니다.

 

CSVpuymXAAAVVpd.jpg

댓글 0

권한이 없습니다.
번호 제목 글쓴이 날짜 조회 수
[Python] 그누보드 자동 글 작성 + 멀티 파일 업로드 스크립트 image 이니스프리 이니스프리 21.05.16.09:32 218
26 0. 행의 분리 및 결합 / 멀티라인 문자열 image 이니스프리 이니스프리 21.04.18.11:53 151
25 [Selenium] proxy를 사용하지 않고 개발자도구에서 Network 탭의 로그를 얻기 image 이니스프리 이니스프리 21.03.06.23:35 268
24 [번외편 - 한컴한글] 스마트한 문서 작성을 위한 팁 (작성 중) 이니스프리 이니스프리 20.08.08.13:50 238
23 [Requests-HTML] Requests와 비교할 때의 장점! (네이버 뉴스 등 크롤링) 7 image 이니스프리 이니스프리 20.05.14.19:08 652
22 22. [OpenPyXL] 엑셀 문서 저장하기 / 시트 추가하기 / 셀에 입력하기 14 image 이니스프리 이니스프리 20.02.02.00:46 2540
21 21. [OpenPyXL] 엑셀 문서를 열고 시트에서 셀의 값을 얻기 3 image 이니스프리 이니스프리 20.02.01.23:46 792
20 [BeautifulSoup] 자식태그를 제거하기 - .decompose() & .extract() 10 image 이니스프리 이니스프리 20.01.16.00:07 3874
19 [python-telegram-bot] 4096자 글자수 제한을 우회하는 방법 image 이니스프리 이니스프리 20.01.12.11:25 784
18 [Requests] Selenium을 이용한 Cloudflare의 우회 8 image 이니스프리 이니스프리 20.01.08.19:51 2345
17 9. [정리] 파이써닉한 파이썬 문법 팁 for COS Pro image 이니스프리 이니스프리 20.01.04.23:47 616
16 [번외편] 네이버 동영상에서 자막을 파일로 추출하는 방법 2 image 이니스프리 이니스프리 19.12.25.22:34 2456
15 4. 문자열 처리 (2) - 문자열 조작에 대한 기본함수 및 메서드 1 image 이니스프리 이니스프리 19.12.12.02:27 414
14 6. 리스트와 관련된 기본함수 및 메서드 7 image 이니스프리 이니스프리 19.12.10.21:16 954
13 3. 문자열 처리 (1) - 문자열 접근 및 검색방법 4 image 이니스프리 이니스프리 19.12.09.20:00 1032
12 [Selenium] Requests의 session을 Selenium으로 보내기 image 이니스프리 이니스프리 19.12.07.14:17 2397
11 1. 반복문 (1) - while 문 5 image 이니스프리 이니스프리 19.12.06.20:50 1105
10 (序) Python 강좌를 시작하며... 2 image 이니스프리 이니스프리 19.12.05.21:55 370
9 [Pillow] 파이썬 Pillow 라이브러리를 이용하여 이미지의 배경을 transparent하게 만들기 11 image 이니스프리 이니스프리 19.08.04.13:40 3021
8 [번외편 - 법률] 나무위키의 서술 중 법률용어의 사용에 있어 바로잡아야 할 부분 image 이니스프리 이니스프리 19.07.17.22:44 360