0
1

はじめに

ハッシュタグは、SNS などで広く利用される、話題やカテゴリを示すキーワードです。

本記事では、Unicode Standard で規定されているハッシュタグの仕様 を元に、ハッシュタグ機能の実装します。この仕様はハッシュタグの共通的な処理を促進する一方で、実装の自由度も一定程度確保しています。

ハッシュタグの仕様

ハッシュタグの仕様について簡単に説明します。

ハッシュタグの構文
<Hashtag-Identifier> := <Start> <Continue>* (<Medial> <Continue>+)*


`:=` : 右辺が左辺の構文を定義
`<>` : 定義された構文
`*`  : 直前の構文が 0 回以上繰り返されることを示す
`+`  : 直前の構文が 1 回以上繰り返されることを示す
`()` : グループ化を表す

Start    : ハッシュタグの開始文字
Continue : 続く文字
Medial   : オプションで使用できる文字

ハッシュタグの仕様には、さまざまなバリエーションが書かれていますが、今回は以下のようにいします。

  • Start 文字は #
  • Continue 文字には XID_Continue プロパティに Extended_Pictographic プロパティ、Emoji_Component プロパティ、_-+ を加えたものを含め、# を除外
  • Medial は空

ハッシュタグを解析する際には、Start 文字の前に Continue 文字がない場合にのみハッシュタグとして認識することが推奨されます。例えば、foo#bar ではハッシュタグは認識されませんが、foo #barfoo.#bar ではハッシュタグとして認識されます。

また、比較やマッチングをする際には NFKC_CF 形式へ変換した後に行う必要があります。例えば、#MötleyCrüe#MÖTLEYCRÜE と一致する必要があります。

ハッシュタグの仕様について詳しくは以下の記事で説明しています。

ハッシュタグ機能の実装

Post と Hashtag の 2 つのエンティティが多対多の関係であることを前提に実装します。

# Post
- ID: ユニークな識別子
- Content: 投稿のテキスト内容
- Timestamp: 投稿が作成された日時

# Hashtag
- ID: ユニークな識別子
- Tag: ハッシュタグテキスト(`#` から始まる)

# PostHashtag
- PostID: 関連する投稿のID (外部キー)
- HashtagID: 関連するハッシュタグのID (外部キー)

# Relationships
- Post は複数の Hashtag を持つことができる。
- Hashtag は複数の Post に関連付けられることができる。

+-----------+         +-----------+        +-------+
| Post      |         |PostHashtag|        |Hashtag|
+-----------+         +-----------+        +-------+
| ID        |<-------|| PostID    ||------>| ID    |
|-----------|         | HashtagID |        |-------|
| Content   |         +-----------+        | Tag   |
| Timestamp |                              +-------+
+-----------+

POST の Content から Hashtag の Tag になる部分を抽出し、NFKC_CF 形式に変換する関数を実装します。Tag を NFKC_CF 形式に統一することで検索機能を強化することができます。

フロントエンド側の実装は以下の記事で行なっています。

必要なライブラリのインストール

Pythonの正規表現 regex ライブラリをインストールします。標準ライブラリの reUnicode プロパティ\p{XID_Continue} など)を使うことができないためです。

pip3 install regex

ハッシュタグ抽出と正規化の実装

以下のコードは、POST の Content から Hashtag を抽出し、NFKC_CF 形式に変換する関数を実装します。

  • hashtag.py ファイルを作成
  • NFKC_CF 関数: 文字列を NFKC 形式に正規化し、casefold する。
  • extract_hashtags 関数: テキストをトークンに分割し、ハッシュタグ部分を NFKC_CF 形式に正規化する。
hashtag.py
# 以下の仕様で実装
# - <Hashtag-Identifier> := <Start> <Continue>* 
# - オプションの (<Medial> <Continue>+)* の部分は実装しない
# - Start の前に Continue がない場合にのみハッシュタグとして認識
# - ハッシュタグは NFKC_CF 形式に正規化

import regex as re
import unicodedata

def NFKC_CF(text):
    return unicodedata.normalize('NFKC', text).casefold()

def extract_hashtags(text):
    tokens = []
    current_token = ''
    in_hashtag = False

    def add_token(value):
        tokens.append(value)

    for char in text:
        if re.match(r'#', char):
            if in_hashtag:
                add_token(NFKC_CF(current_token))
                in_hashtag = False
                current_token = char
            else:
                in_hashtag = True
                current_token = char
        elif in_hashtag:
            if re.match(r'\p{XID_Continue}|\p{Extended_Pictographic}|\p{Emoji_Component}|[_+-]', char):
                current_token += char
            else:
                add_token(NFKC_CF(current_token))
                in_hashtag = False
                
    # 最後の部分を処理
    if current_token:
        add_token(NFKC_CF(current_token))

    return tokens
    
    
# 動作確認
content = "#MötleyCrüe #MÖTLEYCRÜE foo #bar#foo #b🦰ar foo.#bar"  
hashtags = extract_hashtags(content)
for hashtag in hashtags:
    print(hashtag)

動作確認

python3 hashtag.py
結果
#mötleycrüe
#mötleycrüe
#bar
#foo
#b🦰ar
#bar

おわりに

本記事では、Unicode Standard で規定されているハッシュタグの仕様を元に、ハッシュタグ機能を実装しました。

フロントエンドからタグ検索を行う場合は、上記で実装した NFKC_CF 関数で検索ワードを正規化した上で、データベースに問い合わせすることで検索機能を強化することができます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1