# [한라대학교 공지 알림 봇] 유지보수

- Author: @mildsalmon
- Published: 2020-03-02
- Updated: 2021-09-14
- Source: http://blex.me/@mildsalmon/%ED%95%9C%EB%9D%BC%EB%8C%80%ED%95%99%EA%B5%90-%EA%B3%B5%EC%A7%80-%EC%95%8C%EB%A6%BC-%EB%B4%87-%EC%A0%9C%EC%9E%91%EA%B8%B0-1-%EC%9C%A0%EC%A7%80%EB%B3%B4%EC%88%98
- Tags: python, 텔레그램봇, 한라대학교, 웹크롤링, 공지알림봇, 유지보수

---

# Intro

내가 다시 이 시리즈를 작성할 일은 없을 줄 알았는데...

# 사건 설명

### 사건 개요

![](https://static.blex.me/images/content/2020/3/2/bUiSlzFjBSSotJTDuRXj.png)

학교에서 일반공지 게시글의 제목을 바꿨다.

### 수정 과정

```python
...
new_file.write(posts[0].text)
...
elif before != post.text:
...
```

지금까지 사용한 코드는 post.text (게시글의 제목)으로 최신 게시글인지 판단했다.<br>
게시한 공지의 제목을 바꾼다는 생각을 하지 못해서이다.<br>
충분히 가능했으나, 에측하지 못해서 에러가 발생했다.<br>

그래서 학교 측에서 변경하기 힘든, 변경할리 없는 값을 찾기로 했다.<br>

```
[ <a href="view.jsp?spage=1&amp;boardId=23401&amp;boardSeq=7748511&amp;mcategoryId=&amp;id=kr_060101000000&amp;column=&amp;search=">코로나19 원주 2번 확진자 동선 중 '이마트24원주한라대...</a>,
 <a href="view.jsp?spage=1&amp;boardId=23401&amp;boardSeq=7746112&amp;mcategoryId=&amp;id=kr_060101000000&amp;column=&amp;search=">코로나 19 확산 방지를 위한 도서관 운영시간 변경</a>,
 <a href="view.jsp?spage=1&amp;boardId=23401&amp;boardSeq=7746005&amp;mcategoryId=&amp;id=kr_060101000000&amp;column=&amp;search=">2019 동계방학 시내통학버스 운행시간 연장 안내</a>,
...]
```

메모장을 열고 내가 크롤링해오는 데이터를 붙여넣고 비교, 분석을 시작했다.<br>
가능하면 내가 크롤링 한 데이터 중에서 값을 찾아야한다.<br>
내가 크롤링해서 가지고 있는 데이터 밖에서 찾으면 크롤링할 부분이 더 늘어나기 때문이다.<br>
이것은 용량, 속도에 영향을 끼친다. 물론 아주 미미하겠지만.<br>

데이터를 보면 `boardSeq=` 이 부분만 다르다.<br>
boardnum인거같다.<br>
boardId는 학사 공지인지 일반 공지인지 확인해주는 게시판 ID이다.<br>
그러면 저 데이터에서 boardnum을 분리해야한다.<br>

posts는 리스트 형식의 `<class 'bs4.element.Tag'>`이고 posts[0]은 `<class 'bs4element.Tag'>`이다.<br>
posts[0]이 str이 아니다.<br>
그래서 str로 강제형변환을 해준다.<br>

``` python
split_posts = str(posts[0]).split("&amp;")   # 가장 최신 공지랑 같은지 검사
```

str로 강제형변환을 하면, split으로 문자열을 쪼갤 수 있다.<br>
쪼개는 지점은 `boardSeq=7748511` 앞 뒤를 분리하는게 가장 좋다.<br>
`boardSeq=~` 앞 뒤에는 `&amp;`가 있다.<br>
이걸 기점으로 분리하자.<br>

```
['<a href="view.jsp?spage=1',
 'boardId=23401',
 'boardSeq=7748511',
 'mcategoryId=',
 'id=kr_060101000000',
 'column=',
 'search=">코로나19 원주 2번 확진자 동선 중 \'이마트24원주한라대...</a>']
```

우리가 사용할 데이터는 세번째인 2번 인덱스다.<br>

그리고 메시지가 한꺼번에 20개가 날라오니 많이 당황스러웠다.<br>
더군다나 내가 알바를 하고 있을 때 저렇게 날라와서 더 당황했다.<br>
그래서 최신 게시글을 메시지로 보내는 수를 좀 더 제한하는 것도 나쁘지 않다고 생각한다.<br>
최대 url 10개를 보낼 수 있지만,<br>
서버가 항상 켜져있어서 3개 혹은 5개까지면 충분히 1시간 안에 발생하는 게시글을 전부 크롤링 할 수 있다고 생각한다.<br>
공지 게시판이 자유게시판처럼 매 분마다 게시글이 쏟아져나오는 곳이 아니기 때문이다.<br>
그래서 일단은 최대 최신 게시글을 메시지로 보내는 수를 10개에서 5개로 제한했다.<br>

# 유지보수 결과

```python
import requests
from bs4 import BeautifulSoup
import sys
import telegram
import os
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='공지')

for i in range(len(num)):           # 공지로 위로 올라간 게시글 제외한 최신 게시글 분류
    del posts[0]

split_posts = str(posts[0]).split("&amp;")[2]   # 가장 최신 공지랑 같은지 검사

if not(os.path.isfile(os.path.join(BASE_DIR, 'latest.txt'))):
    new_file = open("latest.txt", 'w+',encoding='utf-8')
    new_file.write(split_posts)
    new_file.close()

with open(os.path.join(BASE_DIR, 'latest.txt'), 'r+',encoding='utf-8') as f_read:    # DB 구현후 변경 에정
    before = f_read.readline()

    for post in posts:                      # 기존 크롤링 한 부분과 최신 게시글 사이에 게시글이 존재하는지 확인
        split_post = str(post).split("&amp;")[2]
        print("post = " + post.text)
        print("new = " + split_post)
        print("before = " + before)         # split_posts
        if before == split_post:
            print("최신글입니다.")
            break
        elif before != split_post:
            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.write(split_posts)

print("\n")
```

# 마치며

한라대 공지 알림 봇을 사용하는 인원 수가 두자리를 넘겼다.<br>
더 많이 늘어나기를 바라며,<br>
버그는 더 발생하지 않기를 바라며 마치겠다.<br>

감사합니다.
