仮想と現実の真ん中あたり

主に舞台探訪とか聖地巡礼と呼ばれる記録をつづるブログ

『咲-Saki-』の登場人物リストを作ってみる

 突然ですが、『咲-Saki-』の登場人物リストのデータファイルを作りたくなりました。
 データの参照元としては、もちろん作者の小林立先生の公式サイトの『咲-Saki- キャラクター一覧』が筆頭に挙げられます。そして比較のためにWikipedia咲-Saki-の登場人物からもデータを取る方針にします。
 お手軽に作りたかったので、サクッとプログラムを組んでみました。

咲-Saki-』の登場人物のリストをWebから取得するプログラム

 プログラムにはPythonを使いました。あらかじめ、下記のモジュールをインポートしておきます。

  • URLを解釈するための、urllib.parse
  • URLからHTMLを取得するための、requests
  • 取得したHTMLを解釈するための、Beautiful Soup
  • データを整理するための、pandas
     後は、サックリと。
#######################################################
#               SakiNameCollector.py                  #
#  機能:『咲-Saki-』の登場人物のリストを作る         #
#  履歴:Rev.1.00: 2020/05/24 新規作成                #
#    2020 USO9000 All rights reserved.                #
#######################################################
import re
import urllib.parse
import requests, bs4
import pandas as pd

#小林立先生のサイトからキャラクター名を取得
def GetSciasta():
    #DFを作成
    lst_coloumn = ['姓', '名', '読み1', '読み2', '所属', '学年', '誕生日', '身長']
    df = pd.DataFrame(columns = lst_coloumn)

    #立先生のサイトからページデータをダウンロード
    url = 'http://sciasta.com/characters.html'
    res = requests.get(url)

    #解析&キャラクター名取得
    if (res.status_code == requests.codes.ok):  
        #ページデータをBeautiful Soupに入れて解析準備
        page = bs4.BeautifulSoup(res.content, 'lxml')

        #キャラクターデータを抽出
        for tr in page.select('tr'):    #<tr>の要素を抽出
            offs = 0
            td_list = tr.select('td')    #<td>の要素を抽出
            if len(td_list) != 0:
                for td in td_list:         #<td>の要素を抽出
                    name = td.getText()    #名前のテキスト部分を抽出
                    if offs == 0:
                        m = re.search(r'.*?\s', name)    #姓を正規表現で検索
                        if bool(m):            #姓名が分かれている場合
                            sei = name[m.start():m.end()-1]
                            m = re.search(r'\s.+', name)    #名を正規表現で検索
                            mei = name[m.start()+1:m.end()]
                        else:                #姓名が分かれていない場合
                            sei = name
                            mei = ''
                    elif offs == 1:
                        m = re.search(r'.*?\s', name)    #姓の読みを正規表現で検索
                        if bool(m):            #姓名が分かれている場合
                            yomi1 = name[m.start():m.end()-1]
                            m = re.search(r'\s.+', name)    #名の読みを正規表現で検索
                            yomi2 = name[m.start()+1:m.end()]
                        elif '・' in name:    #外国人の場合
                            m = re.search(r'.*?・', name)   #姓の読みを正規表現で検索
                            yomi1 = name[m.start():m.end()-1]
                            m = re.search(r'・.+', name)    #名の読みを正規表現で検索
                            yomi2 = name[m.start()+1:m.end()]
                        else:                #姓名が分かれていない場合
                            yomi1 = name
                            yomi2 = ''
                    elif offs == 2:
                        m = re.search(r'.年', name)            #所属を正規表現で検索
                        if bool(m):            #学年がある場合
                            group = name[:m.start()]        #職業
                            school_year = name[m.start():m.end()]    #学年
                        elif '阿知賀女子中' in name:          #例外処理
                             m = re.search(r'阿知賀女子中', name)
                             group = name[m.start():m.end()]
                             school_year = name[m.end():] + '年'    #学年
                        else:                #学年がない場合
                            group = name    #職業
                            school_year = ''
                    elif offs == 3:
                        birthday = name        #誕生日
                    elif offs == 4:
                        height = name          #身長
                    offs = offs + 1
    else:
        print('fail')

    return df

#Wikipediaからキャラクター名を取得
def GetWikipedia():
    #DFを作成
    lst_coloumn = ['姓', '名', '読み1', '読み2', '所属']
    df = pd.DataFrame(columns = lst_coloumn)

    #Wikipediaからページデータをダウンロード    
    url_post = 'https://ja.wikipedia.org/wiki/'
    url = url_post + urllib.parse.quote('咲-Saki-の登場人物')
    res = requests.get(url)

    #解析&キャラクター名取得
    if (res.status_code == requests.codes.ok):
        #ページデータをBeautiful Soupに入れて解析準備
        page = bs4.BeautifulSoup(res.content, 'lxml')

        #キャラクター名,団体名を抽出
        tag_list = page.find_all(class_=['anchor','mw-headline'])  #class="anchor"と"mw-headline"の要素を抽出

        for tag in tag_list:
            if 'mw-headline' in str(tag):   #所属部分抽出
                group = tag.getText()  #所属のテキスト部分を抽出
                if ('校' not in group) and ('院' not in group) and ('園' not in group) and ('女子' not in group) or ('その他' in group):    #例外処理1
                    group = ''
                if '・' in group:    #例外処理2
                    group = group[group.find('・')+1:]                    

            if 'anchor' in str(tag):    #名前部分抽出
                name = tag.getText()    #名前のテキスト部分を抽出
                if ' ' in name:      #姓名が分かれている場合
                    m = re.search(r'.*?\s', name)        #姓を正規表現で検索
                    sei = name[m.start():m.end()-1]
                    m = re.search(r'\s.+?(', name)      #名を正規表現で検索
                    mei = name[m.start()+1:m.end()-1]
                    m = re.search(r'(.+?\s', name)      #姓の読みを正規表現で検索
                    yomi1 = name[m.start()+1:m.end()-1]
                    m = re.search(r'\s\S+?)', name)     #名の読みを正規表現で検索
                    yomi2 = name[m.start()+1:m.end()-1]
                elif '・' in name:   #外国人の場合
                    m = re.search(r'.*?・', name)        #姓を正規表現で検索
                    sei = name[m.start():m.end()-1]
                    m = re.search(r'・.+', name)      #名を正規表現で検索
                    mei = name[m.start()+1:m.end()]
                    yomi1 = ''
                    yomi2 = ''
                else:   #姓名が別れが分かれていない場合
                    sei = name
                    mei = ''
                    yomi1 = ''
                    yomi2 = ''
                new_coloumn = pd.DataFrame(data=[[sei, mei, yomi1, yomi2, group]], columns = lst_coloumn)
                df = df.append(new_coloumn, ignore_index=True)    
    else:
        print('fail')

    return df
            
#キャラクター名リストを比較チェック
def CheckNameList(df1, df2):
    #DFを作成
    lst_coloumn = ['姓', '名', '読み1', '読み2', '所属']
    df_add = pd.DataFrame(columns = lst_coloumn)

    print('小林立先生の収録:{}名'.format(len(df1)))
    print(' Wikipediaの収録:{}名'.format(len(df2)))
    print('')

    #立先生のサイトをWikipediaと比較
    print('立先生のサイトにあってWikipediaにないキャラ')
    sciasta_count = 0
    for row1 in df1.itertuples():
        wiki_flag = 0
        for row2 in df2.itertuples():
            if (((row1.姓 == row2.姓) and (row1.名 == row2.名)) or ((row1.読み1 == row2.姓) and (row1.読み2 == row2.名))):
                wiki_flag = 1
                break

        if (wiki_flag != 1):
            print(' ' + row1.姓 + ' ' + row1.名)
            sciasta_count = sciasta_count + 1

    print('合計:{}名'.format(sciasta_count))
    print('')

    #Wikipediaを小林立先生のサイトと比較
    print('Wikipediaにあって立先生のサイトにないキャラ')
    wiki_count = 0
    for row2 in df2.itertuples():
        sciasta_flag = 0
        for row1 in df1.itertuples():
            if (((row1.姓 == row2.姓) and (row1.名 == row2.名)) or ((row1.読み1 == row2.姓) and (row1.読み2 == row2.名))):
                sciasta_flag = 1
                break

        if (sciasta_flag != 1):
            print(' ' + row2.姓 + ' ' + row2.名)
            wiki_count = wiki_count + 1
            new_coloumn = pd.DataFrame(data=[[row2.姓, row2.名, row2.読み1, row2.読み2, row2.所属]], columns = lst_coloumn)
            df_add = df_add.append(new_coloumn, ignore_index=True)         

    print('合計:{}名'.format(wiki_count))

    df1 = pd.concat([df1, df_add])
    df1 = df1.reset_index(drop=True)

    return df1

if __name__ == '__main__':
    #小林立先生のサイトからキャラクター名を取得
    df1 = GetSciasta()
    #Wikipediaからキャラクター名を取得
    df2 = GetWikipedia()
    #結合してCSVファイルに出力
    df1 = CheckNameList(df1, df2)
    df1.to_csv('namelist.csv', encoding='utf_8_sig')

 ※このプログラムはフリーウェアです。個人的利用の限りにおいて利用,複製,改変は自由です。

出来た登場人物のリストを見てみる

 上記のプログラムを実行して出力されたCSVファイルを表にすると、2020年5月30日現在こんな感じ↓になります。

  読み1 読み2 所属 学年 誕生日 身長
0 宮永 みやなが さき 清澄高校 1年 10/27 155
1 原村 はらむら のどか 清澄高校 1年 10/04 154
154 宮永 みやなが かい 宮永家の父 06/10 175
155 原村 はらむら けい 原村家の父 08/29 183
156 宮永 みやなが あい
157 原村 嘉帆 はらむら かほ
158 白築 しらつき しの
159 石飛 閑無 いしとび かんな
201 瑞原 美月 みずはら みつき
202 杏果の母
203 ニーマン

 No.156以降がWikipediaからのデータになります。Wikipediaからの誕生日の取得は面倒だったので出来ていませんが、立先生のサイトに載ってない登場人物で誕生日が判明しているのは白築慕,白築耕介,石飛閑無,稲村杏果の4人だけなので、必要な方は手で追記してもらっても大した手間ではありません。なぜこの4人だけ誕生日が判明しているのかというと、『シノハユ』勢としては珍しく立先生の公式サイトで発表されている(発表時点では瑞原はやり含む5人分)からなのですね。当然ですが、創造主からの発表が無いと分からない世界なのであります。

 そして、プログラムの実行画面の表示結果は、↓のような感じになりました。

小林立先生の収録:156名
 Wikipediaの収録:197名

立先生のサイトにあってWikipediaにないキャラ
 石戸 明星
 十曽 湧
 Alexandra Windheim
 能口 彩花
 伏屋 那都
 多治比 真佑子
 南浦 聡
合計:7名

Wikipediaにあって立先生のサイトにないキャラ
 宮永 愛
 原村 嘉帆
 白築 慕
 石飛 閑無
 (-中略-)
 杏果の母 
 ニーマン 
合計:48名

 公式サイトに156名も収録されていることに、あらためて驚かされます。しかも全員が誕生日と身長のデータ付き! 「設定大魔神」の異名で知られる(?)小林立先生ですが、そのご尽力には頭が下がります。
 Wikipediaと比べてみると、先に書いたように『シノハユ』のみに登場するキャラクターが、公式サイトでは収録されていません。これは『シノハユ』以降スピンアウトが増えて手が回らないのか、あるいは成長期の物語のため身長が載せられないからなのか、作者のみぞ知るですね。
 まだ公式サイトに収録されていない重要人物は、宮永愛(旧姓:愛・アークタンデ)と 原村嘉帆というママさんコンビですが、これは登場時期が新しいため、まだ収録されていないということなのでしょう。
 また、一方のWikipediaですが、臨海女子監督のアレクサンドラ・ヴィントハイムが収録されてないのが意外な感じでした。(気づいたら自分で書き加えろというツッコミは置いといて)

 以上、「『咲-Saki-』の登場人物リスト出来るかな?」コーナーでした。