Parsing Whatsapp Messages


Export Chat

WhatsApp export format might change in future. The code presented in this post is tested on 2.21.14.24 version of whatsapp client. To export the chat open the chat window and select more from the option menu. There you will see the Export Chat option. Select it to initiate the export without including any media object. The initialization will take some time. Finally you’ll get the option to save or share the exported txt file.

Reading Lines

The exported file is just a text file where some lines are the start of a message and some are the continuation of one. First step is to read the whole file and keep everything in memory line by line.

f = open('exported.txt', encoding='utf-8')
all_lines = f.readlines()
f.close()

To identify the lines that start a message I’m using a hit and trial method. I assume that every line is the start of a message and try to parse the date from the start of the message. If it fails I skip that line otherwise mark that index as the start of a message. Not an ideal solution but works in most cases and for small files.

# index of message starting
msg_indices = []

# Get message indices
for i in range(len(all_lines)):
    l = all_lines[i]
    d = l.split('-')
    if len(d) >= 2:
        # MM/DD/YY, HH:MM AM/PM
        possible_dt = d[0].strip()
        try:
            datetime_object = datetime.strptime(possible_dt, '%m/%d/%y, %I:%M %p')
            msg_indices.append(i)
        except ValueError:
            pass

Senders

To group all messages by sender I use message indices and get the sender name, then with the name as key I save the message information which is nothing but a tuple containing (start_index, end_index, timestamp) of the message.

# messages from sender
msg_sender_indices = {}

# Filter indices from selected sender
for i in range(len(msg_indices)):
    l = all_lines[msg_indices[i]]
    msg_date = datetime.strptime(l.split('-')[0].strip(), '%m/%d/%y, %I:%M %p')
    msg_sender = (l.split('-')[1]).split(':')[0].strip()
    if i < len(msg_indices)-1:
        r = (msg_indices[i], msg_indices[i+1]-1, msg_date)
    else:
        r = (msg_indices[i], len(all_lines)-1, msg_date)
    if msg_sender in msg_sender_indices:
        msg_sender_indices[msg_sender].append(r)
    else:
        msg_sender_indices[msg_sender] = [r]

Messages

With this information I can easily look for a sender’s message and reconstruct it with the information available in the tuple.

msgs = []
for msg_info in msg_sender_indices['sender_name']:
    start_index = msg_info[0]
    end_index = msg_info[1]
    msg = msg_info[2].strftime('%m/%d/%y') + " \n"
    for i in range(start_index, end_index + 1):
        if i == start_index:
            msg += ''.join(''.join(all_lines[i].split('-')[1:]).split(':')[1:])
        else:
            msg += all_lines[i]
    msgs.append(msg)

Application

Getting structured time series data out of exported chat can be used to do a lot of interesting NLP analysis. Like word clouds, sentiment analysis or chat frequency based on time. I did it to make a collage of quotes I received in a group chat.