kivantium活動日記

プログラムを使っていろいろやります

Twitterのハッシュタグを正しく抽出する

ここ一年くらいかけて暇な時間にTwitterに投稿されたイラストを収集するにじさーちというサイトを開発しています。
にじさーちにはハッシュタグを使って画像を検索する機能があるのですが、ハッシュタグに中黒(・)が含まれる場合にうまく抽出できないバグがありました。

例えば次のツイートには「リネット・ビショップ生誕祭2020」というハッシュタグが含まれています。しかし、今まで使っていた正規表現では中黒でハッシュタグが終わると判定されてしまい「リネット」というタグがついていると認識されていました。

ハッシュタグ正規表現で抽出するのはかなり難しい

Twitterでは#で始まる文字列がハッシュタグとして利用されていますが、多言語対応を考えるとハッシュタグがどこで終わるのかを正確に判定する正規表現を書くのはかなり難しいです。例えば、hashtag regex in python · GitHubでは

hashtag_re = re.compile("(?:^|\s)[##]{1}(\w+)", re.UNICODE)

のようにハッシュタグを抽出しています。にじさーちでは昨日までこの正規表現をコピペして使っていたのですが、中黒(・)がハッシュタグの一部として認識されていませんでした(おそらく「・」がUnicode word charactersに含まれず\wにマッチしなかったのでしょう)

また、Twitterのソースコードから生成した正規表現だと主張するStack Oveflowのコードも中黒(・)をうまく認識できませんでした。

ハッシュタグ正規表現の歴史

Twitterの中の人ではないので詳しい歴史的経緯は分かりませんが、日本語ハッシュタグ導入時のtogetterを読むと、導入当時は中黒(・)がハッシュタグ区切りとして導入されているようでした。


(現在は・がハッシュタグの一部として認識されているのでこれだけ見てもよく分かりませんが、togetter上では次の写真のように表示されているので、当時は・がハッシュタグの区切りになっていたことが推測できます)
f:id:kivantium:20210829154405p:plain

しかし、2017年12月16日のInitial commit of twitter-text 2.0 · twitter/twitter-text@34dc1dd · GitHubでは中黒に対応するU+30FBがhashtagSpecialCharsとして追加されています。そのため、2011年から2017年のどこかのタイミングで中黒の処理に変更が入ったことが推測できます。2015年のStack Overflowの回答にある正規表現が現在の仕様と合致しないのはTwitter側の仕様変更が理由である可能性が高いです。

解決策その1: 公式ライブラリtwitter-textを使う

多言語に対応するハッシュタグ正規表現を自分で実装するのは非常に手間がかかりそうです。しかし、Twitter社が公式でテキストをパースするためのライブラリを提供しているので対応言語の場合はライブラリを使うことが出来ます。
github.com

JavaScriptの場合はこのように使うことができます。

var parser = require('twitter-text')
var tweet = "#リネット・ビショップ生誕祭2020\n間に合った!!リーネちゃん誕生日おめでとう!";
var result = parser.extractHashtags(tweet);
console.log(result);  // [ 'リネット・ビショップ生誕祭2020' ]

しかし、現在ライブラリが正式に提供されているのはJava, Ruby, JavaScript, Objective-Cのみで、Pythonは含まれていません。移植すればいいのかもしれませんが、Unicode周りを正しく書ける知識は私にはありません。(Python移植であると主張するレポジトリはあるのですが、日本語ではうまく動きませんでした)

解決策その2: Twitter APIに含まれるハッシュタグ情報を使う

Pythonによる文字列処理でハッシュタグを抽出するのは難しそうなのですが、Twitter APIを使ってツイートを読み込む際にハッシュタグ情報を取得することができます。

Twitter APIGET statuses/show/:idのドキュメントを読むと、Example Responseの"entities"の中に"hashtags"という項目があります。ここにハッシュタグの情報が含まれています。

Tweepyを使う場合は次のようにハッシュタグを取得できます。

status = api.get_status(1271018594273333248)  # 冒頭のツイート
for tag in status.entities['hashtags']:
    print(tag['text'])  # リネット・ビショップ生誕祭2020

Twitter APIを使うことで、文字列処理に頼らずにTwitterが認識する通りのハッシュタグを取得することができました。

(今回例に使ったツイートは最初にバグに気がついた例として記録しておいたものです。バグに気づいてから1年以上放置していたようです……)

広告コーナー