2020/11/14 - [Python/Community Manager를 위한 Python] - 3. 저장된 txt 파일은 어떻게 정제해야 할까? 1편
위 링크인 이전 글에 이어서.. 같은 날에 발생한 채팅들을 어떻게 정제할 것인지 살펴보고 순서에 맞춰서 정제를 해볼 것이다. 그리고 추가적으로 생각해야 하는 것은 관리자의 행동과 참여자의 인입이다. 자세히 말하자면 오픈 채팅창을 이용하다 보면 광고가 들어오고, 그 광고를 방장이 가리기도 하고.. 사람을 내보내기도 한다. 그리고 사람들은 오픈 채팅장에 입장도 하고 나가기도 한다. 아 그리고 때때로 특정 게시글을 공지로 올릴 수도 있게 된다. 따라서 이 부분도 미리 플래그 값을 추가한다면 추후 채팅방 트렌드를 확인할 때 유용하게 사용될 것이다.
위 설명에 맞춘 정제 포인트는 아래와 같다.
- 기준일 추출
- 작성자, 작성 시간, 메시지 추출
- 공지글 확인하기
- 관리자 행동, 참여자 인입 확인
- 공지글, 삭제 메시지, 샵 검색, 이모티콘, 사진 확인
1. 기준일 추출
따로 정규식은 쓰지 않을 정도로 기준일 추출은 아주 쉽다. 기준일 양옆에 붙은 기호(-)만 제거해주면 된다.
import datetime
def get_date(line):
date_sep = '---------------'
full_date = line.replace(date_sep, '').strip()
talk_date, day_name = full_date[:-4], full_date[-3:]
talk_date = datetime.datetime.strptime(talk_date, '%Y년 %m월 %d일')
talk_date = datetime.datetime.strftime(talk_date, '%Y-%m-%d')
return talk_date, day_name
line = "--------------- 2020년 11월 1일 일요일 ---------------"
get_date(line)
('2020-11-01', '일요일')
2. 작성자, 작성 시간, 메시지 추출
한 메시지에서 작성자, 작성 시간, 메시지 등 3개의 요소를 추출해야 하므로 다소 어려울 수 있다. 밑에 코드를 한 번 살펴보도록 한다.
import datetime
import re
# 작성자, 작성 시간, 메시지 추출
def get_writer_and_wrote_at_and_msg(line):
def convert_time(ko_wrote_at):
import time
if '오전' in ko_wrote_at:
time_str = ko_wrote_at.replace('오전', 'am')
elif '오후' in ko_wrote_at:
time_str = ko_wrote_at.replace('오후', 'pm')
time_str = datetime.datetime.strptime(time_str, '%p %I:%M')
time_str = datetime.datetime.strftime(time_str, '%H:%M')
return time_str
split_line = re.split('\]|\[', line)
split_line = [e for e in split_line if e.strip()]
writer, wrote_at = split_line[0], convert_time(split_line[1])
msg = line.split(split_line[1]+']')[1].strip()
msg = msg if msg else None
return writer, wrote_at, msg
line = "[포도] [오전 11:16] 안녕하세요!"
get_writer_and_wrote_at_and_msg(line)
('포도', '11:16', '안녕하세요!')
한 줄은 작성자와 작성 시간을 대괄호로 감싸고 있고 나머지는 메시지 부분으로 구성되므로 먼저, 대괄호 ([, ])를 기준으로 split()를 이용해 문자열을 분리해준다. 물론 이때 각 요소에 양쪽 공백도 없애준다. (19번째 행)
그러면 리스트에 [작성자, 작성 시간, 메시지]로 담기는데 이때 작성 시간은 오전 11:16의 형태로 오전/오후가 붙고 시간이 붙는다. 오전/오후 00 ~~ 12 표현을 00 ~ 24 표현으로 바꾸기 위해 convert_time() 함수를 만들어 주었다. 오전, 오후를 각각 am, pm으로 바꾸고 datetime 패키지를 활용하여 변경해주었다. 만약 오후였다면 어떻게 나오는지 아래 결과를 살펴보자.
line = "[BB] [오후 08:16] helloworld!"
get_writer_and_wrote_at_and_msg(line)
('BB', '20:16', 'helloworld!')
아무래도 결과가 잘 나온것 같다. :D
3. 관리자 행동, 참여자 인입 확인
이 부분은 생각해봐야 할 것이 꽤 있다. 여기서 정의하는 관리자의 행동은 1) 관리자가 메시지를 가리기 할 때 2) 참여자를 내보낼 때이고 참여자의 인입은 1) 참여자가 나갈 때, 2) 참여자가 들어올 때이다. 이때 카카오톡 메시지에는 다음처럼 나온다.
- 채팅방 관리자가 메시지를 가렸습니다.
- ~ 님을 내보냈습니다.
- ~ 님이 나갔습니다.
- ~ 님이 들어왔습니다.
import re
ACTIONS = ["채팅방 관리자가 메시지를 가렸습니다.",
"님을 내보냈습니다.",
"님이 나갔습니다.",
"님이 들어왔습니다."]
# 관리자, 참여자 행동 확인
def get_actions(line):
global action_msg
writer = '관리자'
if ACTIONS[0] in line:
action_msg = '게시글 공지'
elif ACTIONS[1] in line:
action_msg = '메시지 가리기'
elif ACTIONS[2] in line:
action_msg = '내보내기'
elif ACTIONS[3] in line:
writer, action_msg = line.replace(ACTIONS[3], ''), '나가기'
elif ACTIONS[4] in line:
writer, action_msg = line.replace(ACTIONS[4], ''), '들어오기'
return writer, action_msg
msg_list = ["채팅방 관리자가 메시지를 가렸습니다.",
"방장아니고반장님을 내보냈습니다.",
"위너님이 들어왔습니다.",
"위너님이 나갔습니다."]
for msg in msg_list:
print(get_actions(msg))
('관리자', '메시지 가리기')
('관리자', '내보내기')
('위너', '들어오기')
('위너', '나가기')
4. 공지글 확인하기
이번에는 특정 값의 포함관계를 regex를 활용하여 확인해본다. 특정 게시글을 공지하게 되면 자동적으로 톡게시판 '공지': 라는 머리말이 붙고 이 부분이 있는지 re.match() 함수를 이용해 확인하였다,
def check_notice(text):
start_str = re.match(r"톡게시판 '공지': ", text)
is_notice_action = False
if start_str:
is_notice_action = True
return is_notice_action
text = "톡게시판 '공지': 오늘도 평안하세요!"
check_notice(text)
True
5. 1~4 적용한 결과 도출하기
1,2는 작성시간이 존재하고 3은 작성 시간이 존재하지 않으므로 작성 시간 부분을 나눠 함수를 목적에 맞게 적용할 수 있다. 4번은 2번에서 추출된 메시지을 입력값으로 이용한다. 또한, start_regex에서 [ + (오전이나 오후)가 들어가는지 확인한 후 그다음에 각 목적에 맞춰 결과를 적용한다. 이때, is_talking_activity, is_notice_action을 추가하는데 3번에서 action_msg값이 None이 아니라면 is_talking_activity가 False이고 is_notice_action 또한, 4번 반환 값에 따라 결정될 것이다.
def check_logic(content):
content = content.strip()
start_regex = r'\[.*?\]\s\[[오전|오후].*?\]'
start_str = re.match(start_regex, content)
is_talking_activity, is_notice_action = True, False
wrote_at, action_msg = None, None
if start_str:
writer, wrote_at, msg = get_writer_and_wrote_at_and_msg(content)
is_notice_action = check_notice(msg)
else:
writer, action_msg = get_actions(content)
msg,is_talking_activity = content, False
msg = re.sub(r'\s+', ' ', msg).strip()
print(f"writer: {writer}, wrote_at: {wrote_at}, msg: {msg}")
print(f"action_msg: {action_msg}, is_talking_activity: {is_talking_activity}, is_notice_action: {is_notice_action}")
content1 = '[hk] [오후 11:22] 심심해서 입사했다고 하던데..'
check_logic(content1)
content2 = 'hk님이 나갔습니다.'
check_logic(content2)
writer: hk, wrote_at: 23:22, msg: 심심해서 입사했다고 하던데..
action_msg: None, is_talking_activity: True, is_notice_action: False
writer: 고참참치님이 나갔습니다., wrote_at: None, msg: 고참참치님이 나갔습니다.
action_msg: 나가기, is_talking_activity: False, is_notice_action: False
6. 공지글, 삭제 메시지, 샵 검색, 이모티콘, 사진 확인
이 부분은 사후 처리로 추출된 메시지에서 확인을 한다. 다소 1~5 전개방식과는 다르지만 먼저 예제를 통해 내용을 살펴보자. 사실 1~5번 과정 전에 고려해야 할 부분이 있고, 이 결과를 데이터 프레임에서 람다식의 입력값으로 활용하여 각 목젝에 맞는 플래그 값을 줄 수 있다. 이 대, 데이터 프레임의 행으로 쌓을 것이다. 다음을 같이 확인해보자.
import pandas as pd
df = pd.DataFrame({'msg': ['이모티콘', '사진', '삭제된 메시지입니다.', '샵검색: #']})
df['is_deleted_msg'] = df['msg'].apply(lambda x: True if x.strip() == '삭제된 메시지입니다.' else False)
df['is_emoji'] = df['msg'].apply(lambda x: True if x.strip() == '이모티콘' else False)
df['is_picture'] = df['msg'].apply(lambda x: True if x.strip() == '사진' else False)
df['is_deleted_msg'] = df['msg'].apply(lambda x: True if x.strip() == '삭제된 메시지입니다.' else False)
df['is_search'] = df['msg'].apply(lambda x: True if x.startswith('샵검색: #') else False)
msg is_deleted_msg is_emoji is_picture is_search
0 이모티콘 False True False False
1 사진 False False True False
2 삭제된 메시지입니다. True False False False
3 샵검색: # False False False True
람다식이 아직 어려울 수 있지만 하나만 기억하자. for문보다 간단하게 작성 가능함을!
다음 편에서 전체적으로 이용 가능한 소스를 소개하고 정제는 마무리 짓도록 하겠다. :D