2020/11/21 - [Python/Community Manager를 위한 Python] - 4. 저장된 txt 파일은 어떻게 정제해야 할까? 2편

위 링크인 이전 글에 이어서 이번 정제를 마무리를 짓도록 한다. 일단 전체 소스를 이야기 하기 전에 중요 로직을 확인해본다.

카카오톡 채팅 데이터는 line by line으로 한 메시지에 여러 문장을 작성할 경우 짤리기 때문에 같은 메시지라고 인식하고 이 메시지를 붙여주는 것이 중요하다. 먼저 1편에서 확인한 내용은 아래와 같다.

fig1.txt 파일 읽은 후 리스트로 변환한 결과 

 

이제 아래처럼 바꿔 줘야 한다. 물론 아래는 각 채팅날짜별로 돌기 때문에 for loop를 이용하여 모든 날짜에 적용할 것이다. 그리고 아래에서 작성하진 않았지만 각 리스트로 구성된 메시지를 문자열로 변환 작업도 필요하다. 

fig2. 특정 날짜에 발생한 채팅 메시지 리스트를 문자열로 변환한 결과

fig2 결과를 위해 사용한 함수는 아래와 같다. 채팅 날짜에 해당하는 전체 메시지를 작성자+작성시간에 맞춰 문자열로 변환 후 다시 리스트 객체에 담아준다. 크게 신경써야 하는 부분은 1) 메시지가 시작된 부분인가? 2) 이전 편에서 확인했던 관리자 행동이나 참여자의 인입인가? 이다.

def split_talk_by_user(context):
    whole_txt, merge_txt = [], []
    for index, element in enumerate(context):
        start_str = re.match(r'\[.*?\]\s\[[오전|오후].*?\]', element)
        if start_str:
            whole_txt.append(' '.join(merge_txt))
            merge_txt = []
            merge_txt.append(element)
            if index == len(context)-1:
                whole_txt.append(' '.join(merge_txt))
        else:
            is_contain = False
            for action in ACTIONS:
                if action in element:
                    is_contain = True
            if is_contain:
                if merge_txt:
                    whole_txt.append(' '.join(merge_txt))
                whole_txt.append(element)
                merge_txt = []
            else:
                merge_txt.append(element)
    return [e for e in whole_txt if e.strip()]

 

그래서 전체 소스 코드는 아래를 참고하길 바란다.

github.com/hyunkyungboo/kakaotalk_chat_analysis/blob/master/01_read_txt_and_data_preprocessing.py

 

hyunkyungboo/kakaotalk_chat_analysis

카카오톡 채팅방 분석하기. Contribute to hyunkyungboo/kakaotalk_chat_analysis development by creating an account on GitHub.

github.com

 

다음 편에서는 데이터를 탐색해볼 것이다.

먼저 저장된 txt 파일은 아래처럼 구성된다.

내보내기 된 카카오톡 오픈 채팅방.txt 내용 1
내보내기 된 카카오톡 오픈 채팅방.txt 내용 2

위 설명에 맞춘 정제 포인트는 아래와 같다.

  • 기본 헤더 제거
  • 일별 구분
  • 첫 입장 메시지 제거
  • 작성자/기준시간/작성 메시지 분리하기
  • 입장, 퇴장 확인하기

 

1. 정제 전 채팅 내용 데이터(.txt) 읽기

input_file_name = 'kakao_chat.txt'  # 텍스트 데이터 경로/이름 입력
with open(input_file_name, "r", encoding="utf-8-sig") as input_file:
    for line in input_file:
        line = line.strip()
        print(line)
데이터분석 QnA&네트워킹 님과 카카오톡 대화
저장한 날짜 : 2020-11-14 14:56:45

--------------- 2020년 11월 1일 일요일 ---------------
hk님이 들어왔습니다.운영정책을 위반한 메시지로 신고 접수 시 카카오톡 이용에 제한이 있을 수 있습니다.
[팬다 Jr.] [오전 10:27] 안녕하세요, 반갑습니다. 데분방입니다. 데분데분
포도님이 들어왔습니다.
[팬다 Jr.] [오전 11:16] 안녕하세요, 반갑습니다. 데분방입니다. 데분데분
[포도] [오전 11:16] 안녕하세요!
모모님이 들어왔습니다.

위 블록은 코드와 결과 일부 담고 있다. 그다음에 우선적으로 해야 하는 것은 기본 헤더 제거와 일자별 내용을 묶는 것이다.

2. 기본 헤더 제외한 일자별 내용 리스트로 담기

date_sep = '일 ---------------'
context_list, tmp_context_list = [], []
with open(input_file_name, "r", encoding="utf-8-sig") as input_file:
    index = 0
    for line in input_file:
        line = line.strip()
        start_index = 4
        if index == start_index:
            tmp_context_list.append(line)
        else:
            if line.endswith(date_sep):
                context_list.append(tmp_context_list)
                tmp_context_list = []
            tmp_context_list.append(line)
        index += 1
print("주어진 텍스트 파일 {}의 일자별 context는 {}건입니다.".format(input_file_name, len(context_list)))
주어진 텍스트 파일 kakao_chat.txt의 일자별 context는 11건입니다.

context_list의 내용은 아래와 같다. (일부)

[['데이터분석 QnA&네트워킹 님과 카카오톡 대화', '저장한 날짜 : 2020-11-14 14:56:45', ''],
 ['--------------- 2020년 11월 1일 일요일 ---------------',
  'hk님이 들어왔습니다.운영정책을 위반한 메시지로 신고 접수 시 카카오톡 이용에 제한이 있을 수 있습니다.',
  '[팬다 Jr.] [오전 10:27] 안녕하세요, 반갑습니다. 데분방입니다. 데분데분',
  '포도님이 들어왔습니다.',
  '[팬다 Jr.] [오전 11:16] 안녕하세요, 반갑습니다. 데분방입니다. 데분데분',
  '[포도] [오전 11:16] 안녕하세요!', (중략)

1번에 비해 다소 어려워진 것 같지만 때에 따라 어려워졌을 수도 있다... ㄴㅇㄱ

전반적인 코드 아래처럼 구성되어 있다.

  • 전체 내용을 담을 리스트, 일자별 내용을 담을 리스트 (각 context_list, tmp_context_list)
  • 일자별 구분을 위해 사용되는 date_sep과 .endwith()
  • 파일을 한 줄씩 읽는 for문, 그 한 줄에 대한 index

기본 헤더는 index 값이 0 ~ 3에 해당하므로 기준인을 index가 4로 시작된다. 그런데 기준일이 계속 늘어나기 때문에 기준일의 패턴인 [일 ---------------]을 활용하여 기준일 별 발생한 채팅 내용을 묶어줘야 한다. 그런데 첫 입장 메시지가 거슬리니 다음에서 첫 입장 메시지를 제거해본다. (단, 텍스트에 없는 경우가 있을 수 있다. 따라서 이 경우 다음을 넘어가도 된다.)

 3. 첫 입장 메시지 제거하기

if ~ continue를 활용하여 첫 입장 메시지, 즉 본문에 pass_msg에 해당하면 넘어가도록 한다.
(pass와는 달리 continue는 if ~ continue 이후 코드를 실행하지 않게 해 준다.)

 

date_sep = '일 ---------------'
pass_msg = '운영정책을 위반한 메시지로 신고 접수 시 카카오톡 이용에 제한이 있을 수 있습니다'
context_list, tmp_contenxt_list = [], []
with open(input_file_name, "r", encoding="utf-8-sig") as input_file:
    index = 0
    for line in input_file:
        line = line.strip()
        # 새로 추가한 부분 시작
        if pass_msg in line:
          continue 
        # 새로 추가한 부분 끝
        start_index = 4
        if index == start_index:
            tmp_contenxt_list.append(line)
        else:
            if line.endswith(date_sep):
                context_list.append(tmp_contenxt_list)
                tmp_contenxt_list = []
            tmp_contenxt_list.append(line)
        index += 1
print("주어진 텍스트 파일 {}의 일자별 context는 {}건입니다.".format(input_file_name, len(context_list)))
[['데이터분석 QnA&네트워킹 님과 카카오톡 대화', '저장한 날짜 : 2020-11-14 14:56:45', ''],
 ['--------------- 2020년 11월 1일 일요일 ---------------',
  '[팬다 Jr.] [오전 10:27] 안녕하세요, 반갑습니다. 데분방입니다. 데분데분',
  '포도님이 들어왔습니다.',
  '[팬다 Jr.] [오전 11:16] 안녕하세요, 반갑습니다. 데분방입니다. 데분데분',
  '[포도] [오전 11:16] 안녕하세요!', (중략)

 

어느 정도 구조화 가능한 데이터를 확보했으므로 이제 본격적으로 다음 편에서 pandas를 활용해 데이터 정제 과정을 공유하고자 한다.

+ Recent posts