스터디용 디스코드 봇 (3) - BOJ DB화 + Embed 정리

Jeuk Oh·2021년 9월 20일
0

디스코드 서버 내 스터디 관리용으로 개발하는 간단한 토이 프로젝트입니다.

추가 된 기능

Solvedac을 활용한 검색 기능이 자주 요청이 막혀
BOJ 문제들을 DB화 (현재 개발 중)

문제 공유시 Embed활용으로 이쁘게 정리해주는 기능

아이디어

  1. 이전에 solved.ac을 활용하여 BOJ 문제 검색기능을 쓰려하였지만, 요청이 잦으면 답이 안오는 문제가 잦았기에 직접 BOJ 문제들을 DB화하여 보다 편안한 검색기능을 직접 만들어 보고자 합니다.


  1. 스터디 멤버분들이 문제 추천 게시판에 BOJ url를 올리는데, 보기가 매우 지저분합니다.

Embed를 활용하여 이쁘게 정리해줍시다.


구현

1. BOJ DB화

BOJ DB화는 진행중이기에 간단하게 아이디어만 쓰고 넘어가겠습니다.

  1. BOJ는 problem/<problem_num> url로 모든 문제를 쉽게 접근할 수 있습니다.

  2. 한 문제의 정보에는 이름, 넘버링, 난이도, 알고리즘 분류가 있습니다.

  3. 다만 난이도와 알고리즘 분류는 백준 사이트를 로그인 한 뒤 해당 정보들을 보겠다는 옵션을 적용해야만 볼 수 있습니다. 따라서 일반 크롤러 방법으로는 난이도와 알고리즘 정보를 받아오기가 어렵습니다.

  4. selenium은 실제 브라우저와 동일하게 작동하도록, 그리고 세션 정보를 저장하고 로드하도록 하여 로그인 된 브라우저를 사용할 수 있습니다. 이-글 이 도움이 되었습니다.

  5. 4번의 방법을 사용하여 문제의 이름, 넘버링, 난이도, 알고리즘 모든 데이터를 크롤링해올 수 있었습니다.
    (Xpath 짱...)

현재 저장중인 문제 정보들..

BOJ에 현재 문제 수는 2만개가 조금 넘습니다.

이렇게 만든 DB에 이제 난이도와 알고리즘 종류별로 select 쿼리를 보내면 2만개의 데이터를 일일이 확인해야합니다.
보다 효율적인 서칭을 위해서 DB를 파티셔닝합시다.

난이도 별로 테이블을 새로 만들어서 정보를 분리해놓습니다.
자주 호출될만한 알고리즘들은 index로 관리하면 좋을 것 같습니다.


2. Url Embed 정리기

추천 문제 게시판에 boj url을 툭 올리면, 해당 링크와 solved.ac을 확인하여 이름과 문제번호, 난이도를 이쁘게 정리해줍니다.

1. crawler.py -> BOJcralwer(url)

먼저 백준 url을 받으면 이름, 문제번호, 난이도를 받아오는 함수를 짰습니다.

requests와 BeautifulSoup을 활용해서

def BOJcralwer(url):
	...
    return {'url':url,'number':number,'name':name,'rank':rank}

다음과 같이 문제 정보를 반환합니다.

크롤링 부분은 이전 글에서도 언급한 듯 하여 넘어가겠습니다.

2. main.py

이번 기능에서는 지금 까지의 다른 명령어들과는 다르게, 따로 명령어 커멘드나 접두사가 필요 없고, url만 툭 던져도 작동 가능하게 해야합니다.

@client.event
on_message 함수를 써서 메세지가 감지되었을 때 실행되는 함수를 작성할 수 있습니다.
코드를 한줄한줄 따라가봅시다.


@client.event
async def on_message(message):
    if message.author == client.user:
        return

client.event 데코레이터를 단 on_message 함수는 봇이 메세지를 감지했을 시 자동으로 실행됩니다. 이 때 인수 message는 봇이 감지한 메세지이며, 작성자 정보 작성된 채널 등등 여러가지 속성을 가집니다. 자기 자신의 메세지에도 실행되므로, 메세지의 author가 자기 자신일 시 바로 함수를 종료하도록 합니다.


# 추천문제 채널 문제 정리기
    if message.channel.id == 883333465720889353:
        a = re.findall("https://www.acmicpc.net/problem/[0-9]+",message.content)
        if a:
            ret = []
            for url in a:
                ret.append(BOJcralwer(url))
            await message.delete()
            await message.channel.send(embed=embed_print_BOJ(ret,message.author))


    await client.process_commands(message)
    

메시지가 올라온 채널이 추천문제를 공유하는 채널일 때만 문제 정리를 고려하도록 합니다. 정규식을 활용하여 message에서 BOJ 문제 url 형식을 가진 것들만 뽑아옵니다.

앞서 만들었던 BOJcralwer 함수를 활용하여 문제 정보를 받아온 뒤 사용자가 작성한 메세지를 지우고 대신 이쁘게 정리된 embed로 프린트해줍시다.

마지막 await client.process_commands(message) 구문이 중요한데, 다른 커멘드로 사용된 함수를 실행시켜주는 구문입니다. 해당 구문이 없을 시, 다른 명령어들이 모두 on_message 함수를 돌고 죽어버려서 항상 마지막에 써주어야합니다.

3. embed.py

def embed_print_BOJ(ret,author):
    title = f'{author.nick or author.name} 님의 추천 문제입니다.'
    embed = Embed(title=title)
    rank_text = '\n'.join(f'{x["rank"]}' for x in ret)
    number_text = '\n'.join(f'[{x["number"]}]({x["url"]})' for x in ret)
    name_text = '\n'.join(f'[{x["name"]}]({x["url"]})' for x in ret)
    embed.add_field(name="Rank", value=rank_text, inline=True)
    embed.add_field(name="Numbers", value=number_text, inline=True)
    embed.add_field(name="Name", value=name_text, inline=True)
    return embed

embed는 역시나 data에 담은 걸 잘 나열해주기만 하면 됩니다. author가 nick이 따로 없는 경우도 있으니 name을 쓰게합시다.


시연

추천 문제 게시판에 BOJ 문제 url을 여러개 올리면 알아서 문제를 잘 정리해서 올려줍니다. 굳


느낀 점

크롤링이 생각보다 오래 걸리네요. 스터디 시간보다 스터디 봇 짜는 시간이 더 긴 것 같습니다. 그래도 재밌기도하고 유용하니 OK입니다.


추가

BOJ 크롤링은 중단합니다 ㅜ

profile
개발을 재밌게 하고싶습니다.

0개의 댓글