한라대학교 공지 알림 봇 제작기 (8) - 유지보수 2탄

'한라대학교 공지 알림 봇 제작기' 시리즈한라대학교 공지 알림 봇 제작기 (8) - 유지보수 2탄

mildsalmon

흔치않고, 진귀하다.

Sign in to view email

Intro

다시는 이 시리즈에 글 쓸 일이 없길 바랐지만.

짜잔.
그럴리가 없죠.

시작하겠습니다.
유지보수 2탄

사건 설명

사건 개요

알람이 폭팔적으로 와서,
'또 제목을 바꾼건가?', '난 잘 고쳤는데?'라는 생각을 했다.
그런데 그게 아니였다.

게시글을 지운것이다.

게시글은 3가지 방식으로 작동된다.
작성,수정,삭제
작성수정에 대한 대책은 바로 앞 게시글에서 해결했다.
하지만 삭제를 할 줄은...
상상도 하지 못했다.

어쨋든 유지보수를 시작하자.

수정 과정

게시글 삭제에 대처하기 위해서는 크롤링한 데이터에 대한 의존성이 높아지게된다.
구상을 이렇게 했다.
크롤링한 데이터를 좀 더 활용해보자.
각 게시글의 제목은 리스트 형식으로 데이터가 정제된다.
정제된 데이터인<a>태그는 각 태그마다 고유한 값(boardSeq)이 존재한다.
그럼 이 고유한 값만 모아서 리스트 형태로 저장시킨 후, 로컬에 저장한다.
그리고 최신 게시글을 판단하는 부분에서 로컬의 값 3~5개 정도와 크롤링한 데이터를 비교하는 보조 검사대를 만들자.

대충 이런 모습이다.

자 그럼 만들자.

리스트가 2개 필요하다.
local에 파일로 저장할 리스트, 코드를 실행하면서 사용할(그림에서 웹 데이터) 리스트.

for post in posts:
    save_boardSeq.append(str(post).split("&amp")[2])  # 최신 공지랑 같은지 검사
    use_boardSeq.append(str(post).split("&amp;")[2])

save_boardSeq[0] = save_boardSeq[0].lstrip(";")

기존에는 boardSeq를 가장 최신 게시물 한개만 저장했다.
변경 후에는 크롤링한 모든 게시글의 boardSeq를 리스트로 저장한다.
save_boardSeq에서 맨 왼쪽';'를 제거하는 이유는 나중에 데이터를 불러올 때 ;를 기준으로 리스트를 만들 것이다.
그런데 맨 왼쪽에 ';'이 있으면 리스트를 만들 때 ['', 'boardSeq=~', 'boardSeq=~'] 이런식으로 나온다.
즉, 공백 원소를 제거하기 위함이다.
save_boardSeq만으로 데이터를 검사하려고 하니 많이 불편했다.
그래서 하나 더 만들고 '&amp;'로 ;까지 다 날려서 순수한 boardSeq를 만들었다.

if not (os.path.isfile(os.path.join(BASE_DIR, 'latest.txt'))):
    new_file = open("latest.txt", 'w+', encoding='utf-8')
    new_file.writelines(save_boardSeq)
    new_file.close()

writelines는 리스트 형태로도 파일에 저장이 가능하다.
writeline은 불가능.

    before = f_read.readline().split(";")
    for i, post in enumerate(posts):  # 기존 크롤링 한 부분과 최신 게시글 사이에 게시글이 존재하는지 확인
        print("post = " + post.text)
        print("new = " + use_boardSeq[i])
        print("before = " + before[0])  #
        if before[0] == use_boardSeq[i]:
            print("최신글입니다.")
            break
        elif before[1] == use_boardSeq[i]:
            print("두번째 게시글이랑 체크, 첫 게시글 삭제된거냐")
            break
        elif before[2] == use_boardSeq[i]:
            print("게시글 2개 삭제는 에바자나")
            break
        else:
            url = "http://www.halla.ac.kr/mbs/kr/jsp/board/" + post.get('href')
            print(url)
            try:
                if post != posts[5]:  # 10번 post 이상 넘어가는지 확인 / 텔레그램 메시지가 url 10개 이상 한번에 못보냄
                    bot.sendMessage(chat_id=my_chat_id, text="일반공지 : " + post.text)
                    bot.sendMessage(chat_id=my_chat_id, text=url)
                else:
                    break
            except Exception as ex:
                print("timeout")  # 짧은 시간에 message를 과도하게 보내면 timeout이 뜨는것같다.
                break  # message를 많이 보내서 발생한다기 보다는, 한번에 보낼 수 있는 url의 양이 10개로 제한되어 있는듯

before 부분에서 한 줄로 저장된 데이터를 문자열 형태로 받게된다.
받은 문자열을 ';'를 기준으로 리스트를 만들었다.
python은 for문에서 리스트를 이용하여 루프를 돌릴때 enumerate를 사용하면 인덱스를 같이 넘겨준다.

  1. 새로운 공지가 안올라온 경우
  • 첫 if문에서 참이면 for문을 벗어난다.
  1. 새로운 공지가 작성된 경우
  • if문, elif문 전부 거짓이면 else문을 통과하고 if문이랑 일치하는지 루프를 돌면서 확인한다.
    맞다면 for문을 탈출하고, 아니라면 반복한다.
  1. 이전 게시글이 삭제된 경우
  • if문을 넘어서 elif문을 만날 것이다.
    web data기준 2번 게시글의 데이터도 가지고 있어서 elif로 검증이 가능하다.
    elif문은 일부로 2개만 만들었다.
    즉 local에 2번 인덱스까지만 검사한다.
    한 번에 게시글 3개가 날라갈일은 없다고 생각하고 만들었다.
  1. 게시글이 수정되는 경우
  • 게시글 확인을 게시글 제목이 아닌, 게시글 고유 숫자로 검사하기 때문에 상관없다.

elif 안에 print를 적은 이유는 만약에 저 elif문이 실행될 경우 로그를 찍어보기 위해서 남겼다.

유지보수 결과

# -*- coding: utf-8 -*-

########## 서비스 코드

import requests
from bs4 import BeautifulSoup
import os
import telegram
import sys
import time

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

my_token = '봇 토큰'
my_chat_id = "채널 주소 / 서비스 채널은 '@***' / 테스트 채널은 '-***'"
req = requests.get('http://www.halla.ac.kr/mbs/kr/jsp/board/list.jsp?boardId=23401&mcategoryId=&id=kr_060101000000')
# 일반 = boardId = 23401
client_errors = [400, 401, 403, 404, 408]
server_errors = [500, 502, 503, 504]

print(time.strftime("%c", time.localtime(time.time())))

if req.status_code in client_errors:
    print(req.status_code + ": 클라이언트 에러")
    sys.exit(1)
elif req.status_code in server_errors:
    print(req.status_code + ": 서버 에러")
    sys.exit(1)

bot = telegram.Bot(token=my_token)
html = req.text
soup = BeautifulSoup(html, 'html.parser')
posts = soup.select('td > a')
num = soup.find_all(title='공지')
save_boardSeq = []
use_boardSeq = []

for i in range(len(num)):  # 공지로 위로 올라간 게시글 제외한 최신 게시글 분류
    del posts[0]

for post in posts:
    save_boardSeq.append(str(post).split("&amp")[2])  # 가장 최신 공지랑 같은지 검사
    use_boardSeq.append(str(post).split("&amp;")[2])

save_boardSeq[0] = save_boardSeq[0].lstrip(";")

if not (os.path.isfile(os.path.join(BASE_DIR, 'latest.txt'))):
    new_file = open("latest.txt", 'w+', encoding='utf-8')
    new_file.writelines(save_boardSeq)
    new_file.close()

with open(os.path.join(BASE_DIR, 'latest.txt'), 'r+', encoding='utf-8') as f_read:  # DB 구현후 변경 에정
    before = f_read.readline().split(";")
    for i, post in enumerate(posts):  # 기존 크롤링 한 부분과 최신 게시글 사이에 게시글이 존재하는지 확인
        print("post = " + post.text)
        print("new = " + use_boardSeq[i])
        print("before = " + before[0])  #
        if before[0] == use_boardSeq[i]:
            print("최신글입니다.")
            break
        elif before[1] == use_boardSeq[i]:
            print("두번째 게시글이랑 체크, 첫 게시글 삭제된거냐")
            break
        elif before[2] == use_boardSeq[i]:
            print("게시글 2개 삭제는 에바자나")
            break
        else:
            url = "http://www.halla.ac.kr/mbs/kr/jsp/board/" + post.get('href')
            print(url)
            try:
                if post != posts[5]:  # 10번 post 이상 넘어가는지 확인 / 텔레그램 메시지가 url 10개 이상 한번에 못보냄
                    bot.sendMessage(chat_id=my_chat_id, text="일반공지 : " + post.text)
                    bot.sendMessage(chat_id=my_chat_id, text=url)
                else:
                    break
            except Exception as ex:
                print("timeout")  # 짧은 시간에 message를 과도하게 보내면 timeout이 뜨는것같다.
                break  # message를 많이 보내서 발생한다기 보다는, 한번에 보낼 수 있는 url의 양이 10개로 제한되어 있는듯

with open(os.path.join(BASE_DIR, 'latest.txt'), 'w+', encoding='utf-8') as f_write:
    f_write.writelines(save_boardSeq)

print("\n")

마치며

알람 갯수를 10개에서 5개로 조정했는데
테스트 코드쪽에만 적용이 된 모양이다.
오늘 이 사건이 발생했을때 메세지 20개 (url 10개)가 동시에 날라오는 것을 보고 참... 많은 생각이 들었다.
테스트 코드 뿐만 아니라 실 서비스 코드에서도 확인을 꼭 잘하자.
귀찮고 힘들어도 두번 세번 확인해야 한다.
안그러면 대참사가 일어날테니까...

그리고 그림판을 너무 오랜만에 사용해서 도형들이 별로 이쁘지 않다.
원래는 더 자유롭게 사용했었는데..
뭐 하다보면 나아지겠지.


'한라대학교 공지 알림 봇 제작기' 시리즈
매번 학교 홈페이지에 가서 최신 공지사항이 올라왔는지 확인하기 불편해서 나만 쓰려고 만든 토이프로젝트였으나, 나처럼 불편해할 사람들이 있으리라 생각해 배포 중인 서비스
baealex
6개월, 2주전

태스트 코드 뿐만 아니라 오타 제보합니다 🙂

mildsalmon
6개월, 2주전

@baealex 수정했습니다. 감사합니다 ㅎㅎ

로그인된 사용자만 댓글을 작성할 수 있습니다.