以前書いた記事でTwitterの対話データを集める方法を紹介しました。
www.pytry3g.com
紹介した方法を使えば膨大な数の対話データが簡単に手に入るというメリットがありますが、一方でTwitter特有の単語が多く含まれていてデータとして使えないなどのデメリットもあります。
今回はその収集したデータに前処理をかけて、データとして使えるものとそうでないものに分けたいと思います。
関連リンク
6.2. re — 正規表現操作 — Python 3.6.5 ドキュメント
pythonに標準で入っているreを使って対話データのフィルタリングをします。
まずはインポートします。
import re
URL
手に入れたツイートにはかなりの確率でURLが含まれています。URLを含んでいるツイートはそのURLに関することについての会話がされていると考えられるので、対話データとしては扱いません。
正規表現を使ってフィルタリングします。
url_regexp = re.compile(r"https?://")
正規表現パターンが文字列を正しくフィルタリングしているか確認します。
フィルタリングにはreモジュールのsearch()
を使います。
search()
は文字列を走査し正規表現がマッチする最初の場所を探して、対応するmatchオブジェクトを返します。文字列が正規表現にマッチしない場合はNone
を返します。
URLを含まないテキスト
sample = "わしはこれもはじめてみたよ"
print(url_regexp.search(sample))
正規表現にマッチする文字列がないのでNoneを返しています。
URL(https)を含むテキスト
sample = "わしはこれもはじめてみたよ https://t.co/lhlr3851cT"
print(url_regexp.search(sample))
正規表現にマッチしたのでmatchオブジェクトが返ってきました。
URL(http)を含むテキスト
sample = "わしはこれもはじめてみたよ http://t.co/lhlr3851cT"
print(url_regexp.search(sample))
httpsから始まるURLだけでなくhttpから始まるURLにもマッチしています。
使用例
使用例として下のような感じ。
def filtering(string):
if url_regexp.search(string):
return False
return True
for sentence in corpus:
if filtering(sentence):
おまけ
ちなみに以下の方法①と方法②は等価です。
sample = "わしはこれもはじめてみたよ https://t.co/lhlr3851cT"
url_regexp = re.compile(r"https?://")
result = url_regexp.search(sample)
result = re.search(r"https?://", sample)
ハッシュタグは普段我々が会話をしているときには含まれることはないので、フィルタリングします。
tag_regexp = re.compile(r"#(\w+)")
URLのフィルタリングと同じようにsearch()
を使います。
サンプル1
sample = "#RTした人に絵のアドバイスする 画像をリプで……送っ(′ω'((⊂(`ω´∩)アチョイ"
print(tag_regexp.search(sample))
ハッシュタグを見つけています。
サンプル2
sample = "【報告】結婚します💍 #いやそんなまさか #企業研究です #頑張る #インスタではお馴染みの最近の私 #いかにも幸せそうな女の子っぽくレジに向かいました #虚しい #ひじがだざん https://t.co/7abyBJn6i0"
print(tag_regexp.search(sample))
ハッシュタグを見つけています。
サンプル3 - 顔文字
sample = "おは(#^.^#)"
print(tag_regexp.search(sample))
この顔文字は正規表現にマッチしませんでした。
ユーザ名
Twitterで返信(リプライ)するときはツイートの先頭にユーザ名を含んでいます。このユーザ名を正規表現を使って空文字列に変換します。
name_regexp = re.compile(r"@([A-Za-z0-9_]+) ")
正規表現にマッチした文字列を変換するにはsub()
を使います。
re.sub(pattern, repl, string, count=0, flags=0)
文字列(string)を走査し正規表現(pattern)にマッチすると、マッチした文字列をreplに置き換えます。
サンプル1
sample = "いっぱいついーとします"
print(name_regexp.sub('', sample))
マッチした文字列はないので変化なし。
サンプル2
sample = "@Yamada2ndSeason おいおいまだ俺とマッチングしてないぞ"
print(name_regexp.sub('', sample))
正規表現がユーザ名とマッチしたので空文字に変換してます。
サンプル3 - 顔文字
sample = "おは(@-.-@)"
print(name_regexp.sub("", sample))
正規表現にマッチせず。
サンプル4 - 顔文字
sample = "おは(@_@)"
print(name_regexp.sub("", sample))
正規表現にマッチせず。
サンプル5 - 顔文字
sample = "おは@(・●・)@"
print(name_regexp.sub("", sample))
正規表現にマッチせず。
replaceを使う。
手に入れたツイートにはHTMLで使われる特殊文字が含まれています。主に顔文字など。pythonに組み込まれているreplace
を使って文字列を痴漢置換します。
sample = "おは(>v<)&"
print(sample.replace(">", ">").replace("<", "<").replace("&", "&"))
textify.py
をこの記事で紹介した前処理などを加えて以下のように書き換えてみた。
import MeCab
import argparse
import re
import sqlite3
import sys
class Dumper:
def __init__(self, db):
self.db = db
self.url_regexp = re.compile(r"https?://")
self.name_regexp = re.compile(r"@([A-Za-z0-9_]+) ")
self.tag_regexp = re.compile(r"#(\w+)")
self.tagger = MeCab.Tagger("-Owakati")
def dump(self):
conn = sqlite3.connect(self.db)
cursor = conn.cursor()
cursor.execute(
"select sid1, sid2, sid3 from conversation")
for row in cursor:
sid1, sid2, sid3 = row
text1 = self.tweet_text(conn, sid1)
text2 = self.tweet_text(conn, sid2)
text3 = self.tweet_text(conn, sid3)
if self.is_valid_conversation(text1, text2, text3):
text1, text2, text3 = self.mapper(text1, text2, text3)
print("{}\n{}\n{}".format(text1, text2, text3))
conn.close()
def is_valid_conversation(self, text1, text2, text3):
if text1 is None or text2 is None or text3 is None:
return False
for text in [text1, text2, text3]:
if re.search(self.url_regexp, text):
return False
for text in [text1, text2, text3]:
if re.search(self.tag_regexp, text):
return False
return True
def mapper(self, text1, text2, text3):
text1 = text1.replace(">", ">").replace("<", "<").replace("&", "&")
text2 = text2.replace(">", ">").replace("<", "<").replace("&", "&")
text3 = text3.replace(">", ">").replace("<", "<").replace("&", "&")
text1 = re.sub(self.name_regexp, '', text1)
text2 = re.sub(self.name_regexp, '', text2)
text3 = re.sub(self.name_regexp, '', text3)
return text1, text2, text3
@staticmethod
def tweet_text(conn, sid):
text = None
cursor = conn.cursor()
cursor.execute("select text from status where id = ?", [sid])
for row in cursor:
text = row[0]
return text
def tokenizer(self, text):
return self.tagger.parse(text).strip()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--db', type=str, required=True,
help='path to sqlite3 db')
args = parser.parse_args()
dumper = Dumper(args.db)
dumper.dump()
if __name__ == '__main__':
sys.exit(main())
おわりに
今回行った前処理だけではまだ不十分で使えない対話データはまだ残っています。(例えば、ユーザとボットとの会話とか)今後データをさらに考察し新しい前処理の方法を考えていきたいと考えています。