보안뉴스(boannews.com) 크롤러 텔레그램 연동

2019. 9. 5. 23:08 개발 이야기/python

 

오늘은 뉴스 크롤러를 만들어 볼 건데요.

 

이 블로그를 참고했습니다.


먼저 텔레그램 봇이 필요한데요

 

텔레그램 앱을 설치하신 후에!

/newbot

 

이라고 치시면 안내에 따라 어렵지 않게 만드실 수 있습니다!

그러므로 봇 만드는 것은 건너뛰고요!

 

저는 일단 특정 카테고리에 있는 뉴스의 새로운 기사를 보고 싶었는데요

 

그래서 각각 카테고리 별로 분류를 해서 작성했습니다.

 

먼저 카테고리 별 URL을 딕셔너리를 이용해서 담았는데요

 

if __name__ == "__main__":
    # https://www.boannews.com/media/s_list.asp?skind=5     취약점 경고 및 보안 업데이트
    # https://www.boannews.com/media/s_list.asp?skind=6     정책
    # https://www.boannews.com/media/list.asp?mkind=1     security
    # https://www.boannews.com/media/list.asp?mkind=3    defense

    urls = {1: 'https://www.boannews.com/media/s_list.asp?skind=5',
            2: 'https://www.boannews.com/media/s_list.asp?skind=6',
            3: 'https://www.boannews.com/media/list.asp?mkind=1',
            4: 'https://www.boannews.com/media/list.asp?mkind=3'}
    executeCrawler(urls)

 

이렇게 urls라는 딕셔너리에 시리얼 넘버와 함께 담았습니다 이유는 시리얼 넘버도

 

사용하기 때문인데요 

 

다음은 실행 부분입니다 

def executeCrawler(urls):
    for key in urls:
        crawler(urls.get(key), key)

실행 부분의 함수는 URL KEY를 가지고 총 4번 실행되게끔 했는데요

 

이유는 DB를 사용하지 않고 txt 파일을 가지고 데이터를 저장하는데

 

추후에 용량이 커졌을 때 조금이나마 검색 속도가 더 빠르지 않을까 하는 생각에 분류를 해봤습니다.

 

다음은 실행 부분의 함수가 실질적으로 실행시키는 크롤러 부분!

 

def crawler(url, key):
    news_link = []
    reponse = requests.get(url)
    html = reponse.text
    soup = BeautifulSoup(html, 'html.parser')  # news_area
    url = '/media/view.asp'
    idx = 'idx='
    kind = 'kind'

    for link in soup.find_all('a', href=True):
        notices_link = link['href']

        if notices_link.find(url) != -1 and notices_link.find(idx) != -1 and notices_link.find(kind) != -1:
            split_notices_link = notices_link.split('&')[0]

            if split_notices_link not in news_link and 'idx' in split_notices_link:
                news_link.append(split_notices_link)
    newlink = compareLink(news_link)
    chatBot(newlink, key)

파라미터를 받아 URL과 매핑되는 key를 전달하면서

 

URL을 이용해 request를 보내고 받은 응답에

 

a 태그를 모두 뽑아낸 후 '/media/view/asp'와 'idx'등이 포함된 URL을 제외하고는 모두 저장하지 않았습니다!

 

이유는 

 

a 태그의 href 부분을 모두 가져오게 되면 저런 식으로 쓸모없는 부분도 굉장히 많은데요!

 

물론 처음에 가져올 때 잘 가져올 수도 있지만 저는 간략하게 짜다 보니 이런 식으로 작성을 했고요! 

 

여기서 제가 필요한 부분만 조건문을 이용해 가져왔습니다.

 

제가 필요한 해당 카테고리의 a 태그 안의 주소는 저렇게 /media/view.asp, idx와 page 등이 혼재했고 나머지는 그렇지 않은 부분들이 많이 있었습니다.

 

또한 

split_notices_link = notices_link.split('&')[0]

를 이용해서 제가 가져온 URL

/media/view.asp?idx=81972&page=1&mkind=3&kind=2

위와 같은 부분을 

하기와 같이 절삭해서 추렸고

/media/view.asp?idx=81972

 

절삭 후에! 중복되는 부분들은 

if split_notices_link not in news_link and 'idx' in split_notices_link:
    news_link.append(split_notices_link)

위의 분기문을 통해 절삭 후news_link 리스트에 담았습니다!

 

이제 비교 부분

def compareLink(newslink):
    newlink = []
    if os.path.exists(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt'):
        with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt') as f:
            link_list = f.readlines()
            for printer in newslink:
                if printer + '\n' not in link_list:
                    newlink.append(printer)
    else:
        for printer in newslink:
            newlink.append(printer)

    with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt', 'a') as f:
        for tempwrite in newlink:
            print(tempwrite)
            f.write(tempwrite + '\n')
    return newlink

카테고리를 key(1~4)로 나누어 구분하여 총 4개의 txt 파일로 분리를 했는데요!

중복으로 날라오는 버그로 수정했습니다. compare.txt한개로 비교!

 

newlink 리스트를 만들고!

newlink = []
    if os.path.exists(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt'):
        with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt') as f:
            link_list = f.readlines()
            for printer in newslink:
                if printer + '\n' not in link_list:
                    newlink.append(printer)
    else:
        for printer in newslink:
            newlink.append(printer)

key에 맞는 txt가 있다면 파일을 열어서 파일 안에 주소가 있는지 확인을 하고

 

없으면! newlink에 append 시켜주었고

 

파일이 없다면!(최초 실행 시 혹은 파일을 지운 경우) 모두 newlink에 append 되도록 작성을 했습니다.

 

 

    with open(os.path.dirname(os.path.abspath(__file__)) + '/compare.txt', 'a') as f:
        for tempwrite in newlink:
            print(tempwrite)
            f.write(tempwrite + '\n')
    return newlink

그리고 newlink에 담긴 주소를 compare txt 파일에 추가한 후 리턴을 해줍니다!

그리고 def crawler의 newlink 변수로 리턴 값을 던져 준 후 chaBot 함수의 파라미터로 전달해줍니다!

    newlink = compareLink(news_link)
    chatBot(newlink, key)

 

마지막 chatBot 함수는 전달받은 키를 카테고리명으로 치환해 

메시지를 날려줍니다

def chatBot(newlink, key):
    if key == 1:
        category = '취약점 경고 및 보안 업데이트'
    elif key == 2:
        category = '정책'
    elif key == 3:
        category = 'security'
    else:
        category = 'defense'

    if newlink.__len__() != 0:
        bot = telegram.Bot('토큰!!!!!!!!!!!!')
        for newlisk_list in newlink:
            bot.sendMessage(
                chat_id=쳇_아이디!!!, text='★보안뉴스\n카테고리 : '
                            '{category}\nURL : {URL}'.format(category=category,
                                              URL='\nhttps://www.boannews.com' + newlisk_list)

여기서 챗봇을 생성해 받은 토큰을 제 위치에 넣어주고 

 

chat_id를 넣어주어야 하는데요!

 

저 같은 경우에는 채널을 하나 생성해서 그 채널에만 새로운 뉴스를 전달해주고자 채널의 id를 넣어줬습니다.

 

채널 아이디는 어떻게 알 수 있을까요?

먼저 메뉴의 채널 만들기를 통해서 채널을 만들어 준다음에

 

https://api.telegram.org/botㅁㅁㅁㅁㅁ:ㅠㅠㅠㅠㅠㅠ형태의토큰/getUpdates

봇을 만들고 얻은 토큰을 이용해서 상위 주소에 기입을 하고 chrome이나 IE를 통해 주소창에 넣어줍니다.

 

그러면!

 

빨간색으로 찍찍 그어진 부분처럼 id가 나오는데요 그 부분을

 

    if newlink.__len__() != 0:
        bot = telegram.Bot('TOKEN')
        for newlisk_list in newlink:
            bot.sendMessage(
                chat_id=-여기!!!!여기!!!!, text='★보안뉴스\n카테고리 : '
                                             '{category}\nURL : {URL}'.format(category=category,
                                                                              URL='\nhttps://www.boannews.com' + newlisk_list))

여기라고 써놓은 부분에 기입해줍니다.

 

그리고 돌려보면!

 

python main.py

 

이렇게 잘 동작하는 것을 확인할 수 있습니다!!

 

그런데 계속해서 새로운 뉴스를 원하신다면

 

지속적으로 돌아야겠죠?

 

많은 방법이 있지만 웹 형태로 서비스를 올리기엔 너무 오버스러운 면이 있기 때문에

 

저는 그냥 crontab으로 5분마다 크롤링을 하도록 했는데요!

 

 

이렇게 설정했습니다

 

간단히 설명해드리자면!!


crontab -e 를 누르신 후에

*/5 * * * * /usr/bin/python /path/to/main.py

 

 

왼쪽에서부터 분 시간 주 달 인데요

저는 분 부분에 /5를 해서 5분마다로 설정했습니다.

 

*/3을 하면 3 분마 다고요! 어렵지 않게 설정하실 수 있습니다

crontab 설정마저 끝나면 진짜 끝!

 

소스코드는 여기에!

https://github.com/bourbonkk/BoanNewsCrawler