Parser and analysis for WhatsApp chats exported as .txt files
Project description
whatsapp-chat-analyzer
Robust parser for WhatsApp chats exported as .txt files, with automatic format
detection, clean data models and export to JSON, CSV and pandas.
The library is meant to be used in two ways: as a solid foundation to build custom analyses on top of (it hands you tidy Python objects), and as a ready-to-use tool thanks to the exporters and a small CLI.
Features
- Automatic format detection: iOS and Android,
DD/MMorMM/DDdates, 2- or 4-digit years, 24h or 12h time with AM/PM. The format can also be forced. - Typed messages: every message is classified as
TEXT,MEDIA,SYSTEMorDELETED, with media subtype detection (image, video, audio, ...). The patterns are multilingual (Italian and English). - Multi-line messages handled correctly.
- Parse-time transformations: date/author/system filters and name anonymization.
- Analysis: per-user statistics, temporal activity (by hour/weekday/date) and content analysis (word frequencies and emoji usage).
- Export: JSON (with re-import for round-tripping), CSV and
pandas.DataFrame. - CLI
wa-analyzerto convert a chat without writing code. - Zero required dependencies (standard library only);
pandasis optional.
Installation
pip install whatsapp-chat-analyzer
For pandas.DataFrame export:
pip install whatsapp-chat-analyzer[pandas]
Requirements: Python 3.10+.
Quick start
from whatsapp_analyzer import parse_file
chat = parse_file("export.txt")
print(len(chat), "messages")
print("Participants:", chat.participants)
for msg in chat:
print(msg.timestamp, msg.sender, "->", msg.text)
If you already have the content in memory (for example loaded from an upload):
from whatsapp_analyzer import parse_string
chat = parse_string(chat_text)
How to export a chat
Export it from your phone without media:
Chat settings → More → Export chat → Without media. You will get a .txt file.
The library automatically handles the utf-8 BOM that WhatsApp adds to some exports.
The data models
Chat
It behaves like a sequence of Message objects (supports len(), indexing and
iteration) and offers a few conveniences:
chat.participants # set of authors (system messages excluded)
chat.filter(predicate) # new Chat with only the messages matching the predicate
chat.to_json() # -> str (see the Export section)
chat.to_csv() # -> str
chat.to_dataframe() # -> pandas.DataFrame (requires the pandas extra)
Chat.from_json(data) # rebuild a Chat from a JSON export
Message
It is an immutable object (frozen dataclass) with these fields:
| Field | Type | Notes |
|---|---|---|
timestamp |
datetime |
message date and time |
sender |
str | None |
None for system messages (no author) |
text |
str |
message body (multi-line ones include the \n) |
type |
MessageType |
TEXT, MEDIA, SYSTEM, DELETED |
media_kind |
MediaKind | None |
set only when type == MEDIA |
MediaKind can be IMAGE, VIDEO, AUDIO, DOCUMENT, STICKER, GIF.
from whatsapp_analyzer import MessageType
text = [m for m in chat if m.type == MessageType.TEXT]
media = [m for m in chat if m.type == MessageType.MEDIA]
Configuration
All options go through ParserConfig, which also acts as a dependency injection
point: if a field is left as None, the parser uses the default implementation.
from whatsapp_analyzer import parse_file, ParserConfig
config = ParserConfig(
locale="it", # tie-break for ambiguous dates (DD/MM vs MM/DD)
max_lines_per_message=1000, # safeguard against corrupted files
transformers=[], # filters/anonymization (see below)
)
chat = parse_file("export.txt", config=config)
Available fields:
chat_format: force a specific format and skip automatic detection.detectors: list of customFormatDetectors (default: iOS + Android).locale: used to disambiguate dates when the day is ≤ 12 in both positions. With"us"/"en_us"month-first is assumed, otherwise day-first.classifier: an alternativeMessageClassifier.transformers: list of transformations applied in order (see below).max_lines_per_message: beyond this threshold a message is truncated and a warning is emitted (protects against corrupted exports where the header is no longer recognized).
Forcing the format
If you have an export with a format the automatic detection does not catch, you can describe it by hand:
import re
from whatsapp_analyzer import parse_file, ParserConfig
from whatsapp_analyzer.detection.base import ChatFormat
fmt = ChatFormat(
header_regex=re.compile(
r"^(?P<date>\d{2}/\d{2}/\d{4}), (?P<time>\d{2}:\d{2}) - "
r"(?:(?P<sender>[^:]+): )?(?P<text>.*)$"
),
datetime_format="%d/%m/%Y, %H:%M",
)
chat = parse_file("export.txt", ParserConfig(chat_format=fmt))
Filters and anonymization
transformers are applied to every message in the order you put them in the list. Each
one can modify the message or drop it.
from datetime import datetime
from whatsapp_analyzer import parse_file, ParserConfig
from whatsapp_analyzer.transform.filters import (
DateRangeFilter, AuthorFilter, SystemMessageFilter,
)
from whatsapp_analyzer.transform.anonymizer import Anonymizer
config = ParserConfig(transformers=[
SystemMessageFilter(), # drop system messages
DateRangeFilter(start=datetime(2024, 1, 1)), # only from 2024 onwards
AuthorFilter(["Mario"], mode="include"), # only Mario's messages
Anonymizer(), # Mario -> User1, Luigi -> User2, ...
])
chat = parse_file("export.txt", config=config)
Notes:
- Order matters. As a rule of thumb, filter first and anonymize last.
AuthorFilteracceptsmode="include"ormode="exclude".Anonymizerreplaces only thesenderfield with a stable alias for the whole chat; it does not touch the message text.
Analysis
The library ships three analyzers. Each one takes a Chat and returns an immutable
report; the Chat shortcuts are the easiest way in.
chat = parse_file("export.txt")
# Per-user and overall statistics
stats = chat.statistics()
print(stats.message_count, "messages,", stats.system_count, "system")
for sender, user in stats.per_user.items():
print(sender, user.message_count, user.word_count, user.media_count)
# Temporal activity
activity = chat.activity()
print("Most active hour:", activity.most_active_hour)
print("Messages on Mondays:", activity.by_weekday.get(0, 0)) # Monday == 0
# Content: word frequencies and emoji
content = chat.content(stopwords={"the", "a", "and"})
print(content.top_words(10)) # [("hello", 42), ...]
print(content.top_emojis(5)) # [("😂", 17), ...]
statistics() exposes ChatStatistics (message_count, system_count,
first_timestamp, last_timestamp and a per_user map of UserStats).
activity() returns an ActivityReport with by_hour, by_weekday, by_date maps
plus most_active_hour/most_active_weekday. content() returns a ContentReport
whose top_words(n) and top_emojis(n) give the most frequent items; word matching is
Unicode-aware, lowercased, and ignores numbers, punctuation and non-text messages.
Each analyzer can also be used directly (for example with a custom configuration):
from whatsapp_analyzer import ContentAnalyzer
report = ContentAnalyzer(stopwords={"the", "a"}).analyze(chat)
Export
chat = parse_file("export.txt")
# JSON (timestamps in ISO 8601, enums as strings)
json_text = chat.to_json()
# Round-trip: reload without re-parsing the .txt
from whatsapp_analyzer import Chat
chat2 = Chat.from_json(json_text)
# CSV (one row per message)
csv_text = chat.to_csv()
# pandas (requires the [pandas] extra)
df = chat.to_dataframe()
The CSV and DataFrame columns are: timestamp, sender, text, type, media_kind.
CLI
After installation the wa-analyzer command is available:
# JSON to stdout
wa-analyzer export.txt --to json
# CSV to a file, dropping system messages and anonymizing authors
wa-analyzer export.txt --to csv --out chat.csv --no-system --anonymize
Options:
| Flag | Description |
|---|---|
--to {json,csv} |
export format (default: json) |
--out PATH |
output file (default: stdout) |
--anonymize |
replace author names |
--no-system |
drop system messages |
Extending the library
The architecture is meant to be extended without touching the existing code:
- new format → implement a
FormatDetectorand pass it inParserConfig(detectors=...); - new transformation → extend
MessageTransformer; - new export → extend
Exporter; - new analysis → extend
Analyzer.
Example of a custom transformer:
from dataclasses import replace
from whatsapp_analyzer.models import Message
from whatsapp_analyzer.transform.base import MessageTransformer
class UppercaseTransformer(MessageTransformer):
def apply(self, msg: Message) -> Message | None:
return replace(msg, text=msg.text.upper())
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file whatsapp_chat_analyzer-0.2.0.tar.gz.
File metadata
- Download URL: whatsapp_chat_analyzer-0.2.0.tar.gz
- Upload date:
- Size: 22.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dea57f1f25e39a1b625596f3a2d6b016273bf7d34faa47ea3b873b8dd95fd117
|
|
| MD5 |
0ffe94f43c28bd225b832c00d6877277
|
|
| BLAKE2b-256 |
3fced6fa6d68ee359f04d2dc292e4affe0456a5f3141d57826d4986ce44f9c8e
|
Provenance
The following attestation bundles were made for whatsapp_chat_analyzer-0.2.0.tar.gz:
Publisher:
publish.yml on mtmxo/whatsapp-chat-analyzer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whatsapp_chat_analyzer-0.2.0.tar.gz -
Subject digest:
dea57f1f25e39a1b625596f3a2d6b016273bf7d34faa47ea3b873b8dd95fd117 - Sigstore transparency entry: 1887785381
- Sigstore integration time:
-
Permalink:
mtmxo/whatsapp-chat-analyzer@3bbe9c4e5f1b8de5b7f95dd9f6b4c12784ee9635 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mtmxo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3bbe9c4e5f1b8de5b7f95dd9f6b4c12784ee9635 -
Trigger Event:
release
-
Statement type:
File details
Details for the file whatsapp_chat_analyzer-0.2.0-py3-none-any.whl.
File metadata
- Download URL: whatsapp_chat_analyzer-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f8ec7ed08b099a9d88663a15ea5ae7cbd37d91a4080b9355006e60390fc169b2
|
|
| MD5 |
eb4bf1e084106e4a9c3cefab204edcc3
|
|
| BLAKE2b-256 |
4c068ab81c15841b61b6561b85ce91bdc9ec54c868f0a5a4a3d3f01c7f7ce9cb
|
Provenance
The following attestation bundles were made for whatsapp_chat_analyzer-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on mtmxo/whatsapp-chat-analyzer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
whatsapp_chat_analyzer-0.2.0-py3-none-any.whl -
Subject digest:
f8ec7ed08b099a9d88663a15ea5ae7cbd37d91a4080b9355006e60390fc169b2 - Sigstore transparency entry: 1887785993
- Sigstore integration time:
-
Permalink:
mtmxo/whatsapp-chat-analyzer@3bbe9c4e5f1b8de5b7f95dd9f6b4c12784ee9635 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mtmxo
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3bbe9c4e5f1b8de5b7f95dd9f6b4c12784ee9635 -
Trigger Event:
release
-
Statement type: