LINEのトーク履歴を加工する
Pythonを使って、LINEのトーク履歴をいじります。
そもそもLINEって?
LINE(ライン)は、24時間、いつでも、どこでも、無料で好きなだけ通話やメールが楽しめる新しいコミュニケーションアプリです。
この記事を読んでいる方は全員LINEを知っている、という前提で話を進めます。
今回の目標
送信した日時、送信した人の名前、送信した内容を上手いことPandasのDateFrameにまとめます。
理想はこんな感じ↓
Datetime | Name | Content |
---|---|---|
2020/7/30 12:00 | わたし | こんにちは |
2020/7/30 12:01 | わたし | [スタンプ] |
... | ... | ... |
フォーマット
テキストファイルのフォーマット
LINEのトーク履歴はtxt形式でダウンロードできます。開くと大体これに近い形になっているはず。
最近頻繁にアップデートされているので、また形式が変わってるかも。
2019.12.23 月曜日
15:16 --がグループに参加しました。
15:16 --がグループに参加しました。
15:16 -- こんにちは
15:16 -- [スタンプ]
15:16 -- よろしくお願いします
15:16 -- [スタンプ]
\nは改行です。
- 日付
yyyy.mm.dd -曜日\n
- 普通のトーク
hh:mm 名前 内容\n
- 改行が含まれるトーク
hh:mm 名前 内容1\n
内容2....\n
これ以外は割愛します。自分で試してみてね。
実装する
txtファイルを読み込む
→改行を含むトーク内容を1つにまとめ直す
→時刻 名前 内容を抽出する
→日付をトークにアペンドする
→DataFrameに加工する
全体のスクリプト
スクリプトはこんな感じ。
import pandas as pd
# txtファイルの読み込み
f = open("line_--.txt", encoding="UTF-8")
line_data = f.readlines()
f.close()
# 連結を定義
def appending(list, row):
row = list[-1] + row
del list[-1]
list.append(row)
data = []
for row in line_data:
# 10文字未満の行
if len(row) < 10:
row = row[:-1]
appending(data, row)
# 15文字未満の行
elif len(row) < 15:
# 時刻+名前+内容
if row[2] == ":" and row[5] == " ":
row = row[:-1]
data.append(row)
# 改行後のトーク内容
else:
row = row[:-1]
appending(data, row)
# 15文字以上の行
else:
# 日付
if row[4] == "." and row[7] == "." and row[-3:-1] == "曜日":
row = row[:10]
data.append(row)
# 時刻+名前+内容
elif row[2] == ":" and row[5] == " ":
row = row[:-1]
data.append(row)
# 改行後のトーク内容
else:
row = row[:-1]
appending(data, row)
data2 = []
for row in data:
# 日付を変数dateに代入
if row[4] == ".":
date = row
# 時刻+名前+内容に日付を連結
else:
row = date + "." + row
row = row.split(" ")
if len(row) == 3:
data2.append(row)
# リストのデータフレーム化
df = pd.DataFrame(data2, columns=["Datetime", "Name", "Content"])
# 時刻をDatetime型に直す
df["Datetime"] = pd.to_datetime(df["Datetime"], format="%Y.%m.%d.%H:%M")
txtファイルの読み込み
先頭から1行ずつ読み込みます。
f = open("line_--.txt", encoding="UTF-8")
line_data = f.readlines()
f.close()
どうやらline_dataはリストのようです。
print(type(line_data))
# <class 'list'>
連結を定義
listの一番最後の要素に、現在の行を連結させます。
def appending(list, row):
row = list[-1] + row
del list[-1]
list.append(row)
遊んでみましょう。
thanks = ["いつも", "ありがとう"]
appending(thanks, "ございます")
# thanks = ['いつも', 'ありがとうございます']
10行未満の行
hh:mm 名前 内容\n
文字数が最小になるのは、名前が1文字、内容が1文字のときです。
つまり、10文字を下回る行はすべて「改行を含むトークの、改行後の部分」になります。
row = row[:-1] # 最後の一文字は改行\nなので削除
appending(data, row)
この処理で、これを↓
01:10 わたし あけましておめでとう!\n
ことしもよろしく!\n
↓こうします。
01:10 わたし あけましておめでとう!ことしもよろしく!\n
もっといい例はなかったのか。
15文字未満の行
yyyy.mm.dd -曜日\n
日時を表す行は文字数が15です。
15文字を下回る行は「普通のトーク」か「改行を含むトークの、改行後の部分」になります。
普通のトーク
01:10 わたし あけましておめでとう!\n
3文字目が「:」かつ6文字目が「 」になるので、その部分を抽出します。
if row[2] == ":" and row[5] == " ":
row = row[:-1]
data.append(row)
改行を含むトークに対しては、先ほどと同じ処理を施します。
15文字以上の行
日付
yyyy.mm.dd -曜日
5文字目と8文字目が「.」で、最後の2文字が「曜日」です。
上手いこと日付の行をピックアップします。
if row[4] == "." and row[7] == "." and row[-3:-1] == "曜日":
row = row[:10]
data.append(row)
「曜日」の部分はなんとなく削除しました。
もちろん残してもいいです。
日付以外
さっきと同じです。
ここまでの作業で、リストdata内に「日付」と「時刻 名前 内容」の要素が入りました。
リストdataが空の人、エラーを吐かれている人はLINEのアップデートを恨んでください。
日付をアペンドする
data内の要素のうち、5文字目が「.」なら日付、そうでなければトーク内容です。
日付を変数dateに代入します。
if row[4] == ".":
date = row
その後「.」で区切り、「時刻 名前 内容」とアペンドすると、
else:
row = date + "." + row
行(要素)rowは↓になります。
yyyy.mm.dd.hh:mm 名前 内容
最後に「 」で区切り、リストを更新します。
データフレーム化
リストdata2は二重リスト構造です。
Pandasでデータフレームに直し、カラム名をつけてあげます。
せっかくなので、日付と時刻をDatetime型に直しました。
これでおしまいです。お疲れさまでした。
to_csvを使って書き出すこともできます。
最後に
LINEのトーク履歴は自然言語のデータセットとして活用できるのでは?と思ったのがきっかけです。
人間の会話を学習したり、会話から感情を読み取ったり…活用方法はたくさんあると思います。
今回は行の抽出に正規表現を用いませんでした。
このコードでは抽出が上手く行かない場合(抽出したくない行まで取り出してしまう等)、Pythonのreパッケージをお試しください。
自分がとりかかったのは3ヶ月以上前だったのですが、それからこの記事を上げるまでの間にフォーマットが変わっていました。
少しずつ書いていた記事が全部パーに。泣きながらコードを新しく書き直しています。
供養のために、以前のコードも書いておきます。
import pandas as pd
# txtファイルを1行ずつ読み込み
f = open("line_--.txt", encoding="UTF-8")
line_data = f.readlines()
f.close()
# 改行データを前の行に連結
def appending(list, row):
row = list[-1] + row
del list[-1]
list.append(row)
data = []
# 4行目から読み込み
for row in line_data[3:]:
# 9文字未満の行は連結
if len(row) < 9:
row = row[:-1]
appending(data, row)
# 13文字未満のとき
elif len(row) < 13:
# 時刻+名前+内容
if row[2] == ":" and row[5] == "\t":
row = row[:-1]
data.append(row)
# 時刻が一桁の場合
elif row[1] == ":" and row[4] == "\t":
row = row[:-1]
data.append(row)
# 連結
else:
row = row[:-1]
appending(data, row)
# 13文字以上のとき
else:
# 日付
if row[4] == "/" and row[-4] == "(" and row[-2] == ")":
row = row[:-4]
data.append(row)
# 時刻+名前+内容
elif row[2] == ":" and row[5] == "\t":
row = row[:-1]
data.append(row)
# 時刻が一桁の場合
elif row[1] == ":" and row[4] == "\t":
row = row[:-1]
data.append(row)
# 連結
else:
row = row[:-1]
appending(data, row)
data2 = []
for row in data:
# 日付をdateに代入
if row[4] == "/":
date = row
# 時刻+名前+内容に日付を連結
else:
row = date + " " + row
row = row.split("\t")
if len(row) == 3:
# 内容に""が付く場合は削除
if row[2][0] == '"' and row[2][-1] == '"':
row[2] = row[2][1:-1]
data2.append(row)
df = pd.DataFrame(data2, columns=["Datetime", "Name", "Content"])
df["Datetime"] = pd.to_datetime(df["Datetime"], format="%Y/%m/%d %H:%M")