プログラミングを上達させたい

情報学専攻の大学院→放送局でCMの営業など@大阪→舞台俳優&IT営業@東京

Twitterのbotを作った(竹原ピストルの親戚bot)

仕事でエンジニアの方々と接していると、普段の作業の自動化ツールやwebページなど、"実際に使えるもの"を作っているのを見る場面が多く、「自分ももっとそういうのができたら」と思ってしまいます。これまでそういうのにトライしたことはあるのですが、取り組んでみては途中で挫折、という感じでした。
でも、なんか急に思い立ったので、やってみます。

作るもの

俳優・歌手である竹原ピストルさんに似た名前を生成して呟くbotを作ります。
竹原ピストルさんの曲「よー、そこの若いの」は、みなさんもCMなどで一度は聞いたことがあるのではないでしょうか。僕はこの曲が大好きで、よくカラオケで歌います。1番のサビ前の歌詞が特に好きです。
www.youtube.com

プロダクトとしては、例えば「田中フェリー」「山田ディスカウント」みたいな文字列を一定間隔で自動で呟くbotということになります。素敵ですね。

使う言語など

今回はpythonで書くことにしました。使っているPCはMacbookAir(2013年くらいに買った)です。バージョンなどは下記をご参照ください。tweepyというライブラリをpip installでインストールし、使います。

$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.7
BuildVersion:	19H524
$ python --version
Python 2.7.16
$ pip --version
pip 20.2.3 from /Library/Python/2.7/site-packages/pip (python 2.7)
$ pip install tweepy
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support pip 21.0 will remove support for this functionality.
Defaulting to user installation because normal site-packages is not writeable
Collecting tweepy
  Downloading tweepy-3.10.0-py2.py3-none-any.whl (30 kB)
Collecting requests[socks]>=2.11.1
  Downloading requests-2.25.1-py2.py3-none-any.whl (61 kB)
     |████████████████████████████████| 61 kB 1.6 MB/s 
〜〜〜一部省略〜〜〜
WARNING: You are using pip version 20.2.3; however, version 20.3.4 is available.
You should consider upgrading via the '/System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python -m pip install --upgrade pip' command.
$ pip --version
pip 20.2.3 from /Library/Python/2.7/site-packages/pip (python 2.7)

[追記]進める過程で「python2はよくない、時代はpython3」みたいな記事をたくさん見たのですが、挫けずにそのままでやり切りました。

Twitterアカウント、および認証用のデータ取得

アカウントは普通に作りました(是非フォローしてください!1日に最大でも3ツイートしかしないので!)。
twitter.com
Twitterアカウントに電話番号を登録できていないと、Developerサイトでの認証に進めないのでご注意ください。

次に、DeveloperサイトからAppsを作る申請&アクセスキーの取得などを行います。
こちらのサイトを参考にさせていただきました。
www.itti.jp
他のサイトを見ていると、過去は認証までに数日かかっていたようですが、今はすぐに認証通るようですね。設定内容によって違うかもしれませんが、僕はメール認証のみですぐにAppsを作れるようになりました。

早速呟いてみる

tweepy使って呟いてみます。tweepyのドキュメントはこちら。
どうやってツイートするかわかりにくいですが、api.update_status()で呟けるようです。
まず、下記の内容(bot.py)を作り、(keyなどは***で伏せています)

# coding: UTF-8
import tweepy

auth = tweepy.OAuthHandler('***************', '***************')
auth.set_access_token('***************', '***************')
api = tweepy.API(auth)

api.update_status('よー、そこの若いの')

実行してみます。

$ python bot.py 

無事ツイートできていました。

次に、ツイート内容について考えていきます。

ツイート内容

一旦まずは、
日本人の名字 + カタカナ語
をランダムでツイートするようにしたいです。できれば、カタカナ語は4文字に設定したい、母音が近いものにしたい、など欲を言えばキリがありませんが、まず最低限のことができるようにしたいです。
ダサダサですが、まずはプログラム内に「名字の候補」「カタカナ語の候補」を配列として持たせ、ランダムな組み合わせで呟くようにしてみます。

名字は、日本で多い名字TOP10にしました。配列として持ちます。

family_name_list = ['佐藤','鈴木','高橋','田中','山本','伊藤','中村','小林','渡辺','加藤']

カタカナ語は、Wikipediaにある、無限に時間が溶けてしまう機能でおなじみ「おまかせ表示」を使って出てきた記事で、カタカナのみで構成されるタイトル(記号などもNG)であったものを、出てきた順に10個入れました。「・」や「(」が入っているものもNGとしたので、10個出すのにかなり苦労しました。

結果として、かなり個性的な面々が揃いました。

katakana_list = ['タケダケン','プライムニュース','コヴェントリーステークス','ノーブランド',
'フィオレンティーノ','ニコニコプレミアム','モンスターコンプリワールド',
'ジャイプールインビテーショナルステークス','リバースモーゲッジ','ウィークエンドハスラー']

なんだこれ、みたいに思った単語は是非ともWikipediaでお調べください。

そして、ここからランダムに選ぶようにしたいです。今回は、randomモジュールをimportして、random.choiceを使います。

$ python
WARNING: Python 2.7 is not recommended. 
This version is included in macOS for compatibility with legacy software. 
Future versions of macOS will not include Python 2.7. 
Instead, it is recommended that you transition to using 'python3' from within Terminal.

Python 2.7.16 (default, Jun  5 2020, 22:59:21) 
[GCC 4.2.1 Compatible Apple LLVM 11.0.3 (clang-1103.0.29.20) (-macos10.15-objc- on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> list = [0,1,2,3]
>>> random.choice(list)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'random' is not defined
>>> import random
>>> random.choice(list)
0
>>> random.choice(list)
1
>>> random.choice(list)
1

これを入れ、bot.pyは下記のように変更されました。

# coding: UTF-8
import tweepy
import random

auth = tweepy.OAuthHandler('***************', '***************')
auth.set_access_token('***************', '***************')
api = tweepy.API(auth)

family_name_list = ['佐藤','鈴木','高橋','田中','山本','伊藤','中村','小林','渡辺','加藤']
katakana_list = ['タケダケン','プライムニュース','コヴェントリーステークス','ノーブランド',
'フィオレンティーノ','ニコニコプレミアム','モンスターコンプリワールド',
'ジャイプールインビテーショナルステークス','リバースモーゲッジ','ウィークエンドハスラー']

text = random.choice(family_name_list) + random.choice(katakana_list)

api.update_status(text)

実行してみます。

無事、「佐藤タケダケン」が誕生しました。


タケダケンさんは、日本の芸人・Tiktokerです。

もっと色んな文字列が生成されるようにしたい

上記のコードだと、名字10種類 * カタカナ語10種類 = 100種類 しか候補がないということになります。もっと増やしたい。できることなら、候補を配列として持たせるのではなく、webのどこかの情報から取ってくるようにしたい。

カタカナ語

とりあえずカタカナ語に関しては、上記でやったようにWikipediaの「おまかせ表示」を使って取得できないだろうか?と考えました。
つまり、
①おまかせ表示
②飛んだ先の記事タイトル(URLでもよさそう)を見る
③タイトルの全文字がカタカナならOK、違ったら①に戻る
という流れです。

webスクレイピングだ!やってみたかったやつ!!!ただ、色々調べてみると、Wikipediaの情報をスクレイピングすることは禁止されているようです。
ただ、全記事のダンプデータが提供されていて、それを手元に落とすことが可能とのことなので、そちらから攻めてみることにします。
下記の記事を参考にさせていただきました。
qiita.com

まずは、どんなデータか知るために、wikiのダンプデータのページから「jawiki-latest-all-titles.gz」をダウンロードしてみました。

page_namespace	page_title
0	!
0	!!
0	!!!
0	!!!!!!!!/君という仮説
0	!!!Fuck_You!!!
0	!=
0	!?
0	!BYE_BYE
0	!LOUD!
0	!SHOUT!
0	!_-attention-
0	!wagero!
0	![ai-ou]
0	"
0	"3人のゴースト"のテーマ
0	"3人のゴースト"のテーマ〜恋をあなたに
0	"74ers"_LIVE_IN_OSAKA-JO_HALL_2003

序盤の記号のみの行はともかく、途中からは使えるものもありそうでした。
ちなみに373万行あります。テキストのみで100MBです。すごっ

f:id:frfrfrfr:20210410163627p:plain
タケダケンさんも無事ありました

ここからカタカナ語を取るのはできそうですね。

これはタブで区切られているファイルで、各行の2つめの要素にページタイトルが入っている模様です。ヘッダー行をnextで飛ばして、あとは各行の2つめの要素のみを取り出していきます。
※ダウンロードしたファイルの名前の末尾に".tsv"を追加しています。必要ないかもですが、後に振り返った時に分かりやすくするため。

import csv

with open('jawiki-latest-all-titles.tsv') as f:
    h = next(csv.reader(f))
    with open('output.txt', 'a') as f2:
        for cols in csv.reader(f, delimiter='\t'):
            f2.write(cols[1])
            f2.write('\n')

一部変になっている行もありますが、大事なところ(カタカナ語のところ)はちゃんとできているようでした。

!
!!
!!!
!!!!!!!!/君という仮説
!!!Fuck_You!!!
!=
〜〜〜一部省略〜〜〜
タケダアヤノ
タケダアワー
タケダケン
タケダサナ
タケダスポーツ
〜〜〜残り省略〜〜〜

あとは、「カタカナのみで構成された言葉かどうか」が判定できればいいということになります。対話モードでカタカナの判定を探ります。

>>> 'ア' < 'イ'
True
>>> 'ウ' < 'イ'
False
>>> 'ア' < 'a'
False
>>> 'a' < 'ア'
True
>>> 'ア' < 'あ'
False
>>> 'あ' < 'ア'
True

ということで、文字列内の全文字が'ア' <= ● <= 'ン' を満たせば、その文字列はカタカナのみで構成されている、と言えそうです。

結構オールカタカナ語の判定に苦労したのですが、最終的にはこういうコードになりました(苦労の部分は、一回書いてみたいと思っていたQiitaの方に書いてみようと思います)。

# coding: UTF-8
import csv
import random
import time

title_list = []
with open('output.txt') as f:
    for line in f:
        title_list.append(line.replace('\n',''))

def katakana(str):
    if len(str) == 0:
        return True
    elif 'ア' <= str[0:3] and str[0:3] <= 'ン':
        return katakana(str[3:len(str)])
    else:
        return False

fixtitle = ''
while True:
    title = random.choice(title_list)
    if katakana(title):
        fixtitle = title
        break

print(fixtitle)

できているか確認。

$ python get_katakana.py
レモンチェロ
$ python get_katakana.py
グレッグヒデ
$ python get_katakana.py
マルバルコウソウ

無事に動きました。

名字

名字に関しては、ネットにランキング記事やデータベースなどもありましたが、充実したデータベースは有料商品であり、また名字検索サイトもスクレイピングNGの模様でした。

でもできたらここも大きなデータベースから拾ってくるようにしたい・・・

そんなとき、たくさんWikipediaの記事を見てきた中で、ふと気付きました。
f:id:frfrfrfr:20210411124444p:plainf:id:frfrfrfr:20210411124447p:plainf:id:frfrfrfr:20210411124455p:plain
※それぞれWIkipediaの、藤野観如枚田菜々子霧賀ユキのページより。

このように、日本人名がタイトルの記事は
タイトル = 名字+名前
概要の冒頭 = 名字 + " " + 名前
で始まっていそうです。つまり、記事タイトルと概要を見て上記条件を満たすようにピックアップすれば名字がひっぱってこられるのでは・・・?

さっそく、この方針でやってみます。
https://dumps.wikimedia.org/jawiki/latest/:wikiのダンプデータのページから、「jawiki-latest-abstract.xml.gz」をダウンロードして、中身を見てみます。

# coding: UTF-8
import csv
import random
import time

count = 0
with open('jawiki-latest-abstract.xml') as f:
    for line in f:
        print(line.replace('\n',''))
        count = count + 1
        if count == 20:
            break

先頭20行を見るコードです。実行結果はこうなりました。

$ python get_family_name.py
<feed>
<doc>
<title>Wikipedia: アンパサンド</title>
<url>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89</url>
<abstract>right|thumb|100px|[[Trebuchet MS フォント]]</abstract>
<links>
<sublink linktype="nav"><anchor>歴史</anchor><link>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89#歴史</link></sublink>
<sublink linktype="nav"><anchor>手書き</anchor><link>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89#手書き</link></sublink>
<sublink linktype="nav"><anchor>プログラミング言語</anchor><link>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89#プログラミング言語</link></sublink>
<sublink linktype="nav"><anchor>符号位置</anchor><link>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89#符号位置</link></sublink>
<sublink linktype="nav"><anchor>外部リンク</anchor><link>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89#外部リンク</link></sublink>
<sublink linktype="nav"><anchor>脚注</anchor><link>https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89#脚注</link></sublink>
</links>
</doc>
<doc>
<title>Wikipedia: 言語</title>
<url>https://ja.wikipedia.org/wiki/%E8%A8%80%E8%AA%9E</url>
<abstract>この記事では言語(げんご)、特に自然言語について述べる。</abstract>
<links>
<sublink linktype="nav"><anchor>概要</anchor><link>https://ja.wikipedia.org/wiki/%E8%A8%80%E8%AA%9E#概要</link></sublink>

思い他、ややこしい構造をしている・・・
これのうち、必要なのは"title"と"abstract"だけなので、一旦それだけを抜き出して別のファイルに移すコードを書いてみます。
xmlファイルですが、単純にテキストとして扱い、下記のコードで変換をしました。

with open('jawiki-latest-abstract.xml') as f:
    with open('title_and_abst.xml', 'a') as f2:
        for line in f:
            if '<title' in line or '<abstract' in line:
                f2.write(line.replace('Wikipedia: ', ''))

できたものを見てみると・・・目論見が少し外れ、タイトルが人名の記事の概要が人名で始まっているものはあまりない・・・

<title>村上もとか</title>
<abstract>|生地=東京都世田谷区</abstract>
<title>青木光恵</title>
<abstract>まんがseek・日外アソシエーツ共著『漫画家人名事典』日外アソシエーツ、2003年2月25日初版発行、ISBN 4-8169-1760-8、6頁</abstract>
<title>赤塚不二夫</title>
<abstract>| 生年 = 1935年9月14日</abstract>
<title>一条ゆかり</title>
<abstract>岡山県玉野市</abstract>

ただ、なっているものもありました。

<title>六道神士</title>
<abstract>六道 神士(りくどう こうし、1970年11月16日 - )は、日本の漫画家、同人作家。男性六道神士、瀬口たかひろのトークも!</abstract>
<title>高橋葉介</title>
<abstract>高橋 葉介(たかはし ようすけ、1956年3月15日『Fusion Product 創刊号』ふゅーじょんぷろだくと、1981年7月。PP174-175。 -  )は、日本の漫画家、漫画原作者。本名高橋庸介。代表作に怪奇幻想マンガ『夢幻紳士』シリーズや『学校怪談』がある。</abstract>

全部を網羅できないのはくやしいですが、一旦はこの方針で最後まで書いてます。

まず、「abstractが、記事タイトルの間に半角スペースが挟まったもので表現されているか」をどうチェックすればよいか、pythonの対話モードで探ります。

$ python

WARNING: Python 2.7 is not recommended. 
This version is included in macOS for compatibility with legacy software. 
Future versions of macOS will not include Python 2.7. 
Instead, it is recommended that you transition to using 'python3' from within Terminal.

Python 2.7.16 (default, Jun  5 2020, 22:59:21) 
[GCC 4.2.1 Compatible Apple LLVM 11.0.3 (clang-1103.0.29.20) (-macos10.15-objc- on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> len('高橋葉介')
12
>>> len('高橋 葉介')
13
>>> a = '高橋葉介'
>>> b = '高橋 葉介'
>>> a == b
False
>>> a == b.replace(' ', '')
True
>>> a = '<title>高橋葉介</title>'
>>> b = '<abstract>高橋 葉介(たかはし ようすけ、1956年3月15日『Fusion Product  創刊号』ふゅーじょんぷろだくと、1981年7月。PP174-175。 -  )は、日本の漫画家、漫画原作者。本名高橋庸介。代表作に怪奇幻想マンガ『夢幻紳士』シリーズや『学校怪談』がある。</abstract>'
>>> a == b
False
>>> a = a.replace('<title>', '')
>>> a
'\xe9\xab\x98\xe6\xa9\x8b\xe8\x91\x89\xe4\xbb\x8b</title>'
>>> a = a.replace('</title>', '')
>>> a
'\xe9\xab\x98\xe6\xa9\x8b\xe8\x91\x89\xe4\xbb\x8b'
>>> title_length = len(a)
>>> title_length
12
>>> b = b.replace('<abstract>', '')[0:title_length+1].replace(' ', '')
>>> a == b
True

よって、
・記事のタイトルの長さをtitle_lenとしたとき
・abstractの頭から(title_len + 1)文字を取得したものをabst_strとして
・タイトルの文字列と abst_str.replace(' ', '') が一致すればよい
というステップで記述できそうです。

こちらのページを参考に、指定行の読み取りを使って書きます。
www.delftstack.com

文字コードとの戦いなどもありましたが、

import linecache
import random

line_count = 0
with open('title_and_abst.xml') as f:
	for line in f:
		line_count = line_count + 1

def title_abst_check(title_str, abst_str):
	# print(title_str)
	# print(abst_str)
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	if(title_name == abst_head.replace(' ', '')):
		return True
	else:
		return False

def get_family_only(title_str, abst_str):
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	for i in range(title_length):
		if title_name[0:i] != abst_head[0:i]:
			return title_name[0:i-1]
			break


family_list = []

for i in range(line_count):
	if i * 2 >= line_count:
		break
	taitoru = linecache.getline('title_and_abst.xml', i * 2 + 1)
	abusuto = linecache.getline('title_and_abst.xml', i * 2 + 2)
	# print(taitoru)
	# print(abusuto)
	# if i >= 10:
	# 	break
	if title_abst_check(taitoru, abusuto):
		# print('OK!')
		family_list.append(get_family_only(taitoru, abusuto))

family_list = filter(None, family_list)
print(random.choice(family_list))
$ python get_family_name.py
六道
$ python get_family_name.py
奥山
$ python get_family_name.py
大橋
$ python get_family_name.py
大島
$ python get_family_name.py
きくち

いけた!!!!と思った矢先、「きくち」が出てきました。きくち、この野郎・・・
ということで、平仮名およびカタカナが入っていない、というのも条件につけようと思います。ダレノガレとかが入っちゃうかもしれないですしね。
あと、竹原ピストルさんに寄せるため、2文字であることも条件に付け加えるようにしてみます。
さらにさらに、重複もかなりあると想像されるので、set関数を噛ませて重複を削除してみます。
ということで、最終的な名字生成用のコードはこちら。候補となる名字の数と、実際に8個表示させてみるようにしてみました。

# coding: utf-8
import linecache
import random

line_count = 0
with open('title_and_abst.xml') as f:
	for line in f:
		line_count = line_count + 1

def title_abst_check(title_str, abst_str):
	# print(title_str)
	# print(abst_str)
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	if(title_name == abst_head.replace(' ', '')):
		return True
	else:
		return False

def get_family_only(title_str, abst_str):
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	for i in range(title_length):
		if title_name[0:i] != abst_head[0:i]:
			return title_name[0:i-1]
			break

def not_katakana(str):
    if len(str) == 0:
        return True
    elif 'ア' > str[0:3] or str[0:3] > 'ン':
        return not_katakana(str[3:len(str)])
    else:
        return False

def not_hiragana(str):
    if len(str) == 0:
        return True
    elif 'あ' > str[0:3] or str[0:3] > 'ん':
        return not_hiragana(str[3:len(str)])
    else:
        return False

def nimoji(str):
    if len(str) == 6:
        return True
    else:
        return False


family_list = []

for i in range(line_count):
	if i * 2 >= line_count:
		break
	taitoru = linecache.getline('title_and_abst.xml', i * 2 + 1)
	abusuto = linecache.getline('title_and_abst.xml', i * 2 + 2)
	# print(taitoru)
	# print(abusuto)
	# if i >= 10:
	# 	break
	if title_abst_check(taitoru, abusuto):
		# print('OK!')
		family_list.append(get_family_only(taitoru, abusuto))

family_list = list(set(filter(nimoji, filter(not_hiragana, (filter(not_katakana, filter(None, family_list)))))))
print(len(family_list))
print(random.choice(family_list))
print(random.choice(family_list))
print(random.choice(family_list))
print(random.choice(family_list))
print(random.choice(family_list))
print(random.choice(family_list))
print(random.choice(family_list))
print(random.choice(family_list))

実行!

$ python get_family_name.py
12510
屋敷
甲斐
夢巌
令丈
藤口
大越
廣谷
神浦

今回ダウンロードしたタイトルや概要が入っているファイルは全部で6つあるようですが、そのうちの一つでも1万を超える名字が得られることが分かりました。厳密に言うと名字でないものも入っているかもですが・・・ただ、漢字のみ&漢字で2文字という条件を付けたことでより名字が得られる可能性は上がっているように思います。

さて、あとは以前作ったカタカナ語を出力するコードと合体させることで、一旦"竹原ピストルさんの親戚の名前を呟く"ことはできそうです。
いちいち探索する形のコードとなっているため速度は遅いですが、一旦完成させたいのでそのまま移植します。
こんな感じになりました。長いですが、全体を省略せずに載せます。

# coding: utf-8
import random
import linecache
import csv
import tweepy

def title_abst_check(title_str, abst_str):
	# print(title_str)
	# print(abst_str)
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	if(title_name == abst_head.replace(' ', '')):
		return True
	else:
		return False

def get_family_only(title_str, abst_str):
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	for i in range(title_length):
		if title_name[0:i] != abst_head[0:i]:
			return title_name[0:i-1]
			break

def not_katakana(str):
    if len(str) == 0:
        return True
    elif 'ア' > str[0:3] or str[0:3] > 'ン':
        return not_katakana(str[3:len(str)])
    else:
        return False

def not_hiragana(str):
    if len(str) == 0:
        return True
    elif 'あ' > str[0:3] or str[0:3] > 'ん':
        return not_hiragana(str[3:len(str)])
    else:
        return False

def nimoji(str):
    if len(str) == 6:
        return True
    else:
        return False

def katakana(str):
    if len(str) == 0:
        return True
    elif 'ア' <= str[0:3] and str[0:3] <= 'ン':
        return katakana(str[3:len(str)])
    else:
        return False


title_list = []
with open('output.txt') as f:
    for line in f:
        title_list.append(line.replace('\n',''))

fixtitle = ''
while True:
    title = random.choice(title_list)
    if katakana(title):
        fixtitle = title
        break

line_count = 0
with open('title_and_abst.xml') as f:
	for line in f:
		line_count = line_count + 1

family_list = []

for i in range(line_count):
	if i * 2 >= line_count:
		break
	taitoru = linecache.getline('title_and_abst.xml', i * 2 + 1)
	abusuto = linecache.getline('title_and_abst.xml', i * 2 + 2)
	# print(taitoru)
	# print(abusuto)
	# if i >= 10:
	# 	break
	if title_abst_check(taitoru, abusuto):
		# print('OK!')
		family_list.append(get_family_only(taitoru, abusuto))

family_list = list(set(filter(nimoji, filter(not_hiragana, (filter(not_katakana, filter(None, family_list)))))))

text = random.choice(family_list) + fixtitle

auth = tweepy.OAuthHandler('***************', '***************')
auth.set_access_token('***************', '***************')api = tweepy.API(auth)
api.update_status(text)

そしてドキドキの実行・・・

f:id:frfrfrfr:20210413171153p:plain
「佐藤タケダケン」に続く親戚の名前:「川村シカンディン」

無事に親戚の名前がツイートされました!やったね!

全自動botにする

あとは、このコードを定時ごとに行なってくれるようになるとオッケーということになります。

ただ、現在はいちいち名字&カタカナ語を探し出して→ツイートとなっているため、実行に少し時間がかかります。
高速化するため、名字、およびカタカナ語の候補のみを保存したテキストファイルをそれぞれ作ります。
また、このタイミングでカタカナ語も4文字のもののみを使うようにしました(len(str) == 12がその判定です)。
カタカナ語

# coding: UTF-8
import csv
import random
import time

title_list = []
with open('output.txt') as f:
    for line in f:
        title_list.append(line.replace('\n',''))

def katakana(str):
    if len(str) == 0:
        return True
    elif 'ア' <= str[0:3] and str[0:3] <= 'ン':
        return katakana(str[3:len(str)])
    else:
        return False

fixtitles = []
for ele in title_list:
    if katakana(ele) and len(ele) ==12:
        fixtitles.append(ele)

with open('titles.txt', 'a') as f2:
    for ele in fixtitles:
        f2.write(ele + '\n') 

名字

# coding: utf-8
import linecache
import random

line_count = 0
with open('title_and_abst.xml') as f:
	for line in f:
		line_count = line_count + 1

def title_abst_check(title_str, abst_str):
	# print(title_str)
	# print(abst_str)
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	if(title_name == abst_head.replace(' ', '')):
		return True
	else:
		return False

def get_family_only(title_str, abst_str):
	title_name = title_str.replace('<title>', '').replace('</title>', '').replace('\n', '')
	title_length = len(title_name)
	abst_head = abst_str.replace('<abstract>', '')[0:title_length+1]
	for i in range(title_length):
		if title_name[0:i] != abst_head[0:i]:
			return title_name[0:i-1]
			break

def not_katakana(str):
    if len(str) == 0:
        return True
    elif 'ア' > str[0:3] or str[0:3] > 'ン':
        return not_katakana(str[3:len(str)])
    else:
        return False

def not_hiragana(str):
    if len(str) == 0:
        return True
    elif 'あ' > str[0:3] or str[0:3] > 'ん':
        return not_hiragana(str[3:len(str)])
    else:
        return False

def nimoji(str):
    if len(str) == 6:
        return True
    else:
        return False


family_list = []

for i in range(line_count):
	if i * 2 >= line_count:
		break
	taitoru = linecache.getline('title_and_abst.xml', i * 2 + 1)
	abusuto = linecache.getline('title_and_abst.xml', i * 2 + 2)
	# print(taitoru)
	# print(abusuto)
	# if i >= 10:
	# 	break
	if title_abst_check(taitoru, abusuto):
		# print('OK!')
		family_list.append(get_family_only(taitoru, abusuto))

family_list = list(set(filter(nimoji, filter(not_hiragana, (filter(not_katakana, filter(None, family_list)))))))

with open ('family_names.txt', 'a') as f2:
	for ele in family_list:
		f2.write(ele + '\n')

これで、名字候補は88KB、カタカナ語は330KBのテキストファイルに圧縮できました。
名字は1万以上、カタカナ語は2万以上、よって竹原ピストル さんの親戚が2億種類以上生成できる計算です。
また、名字やカタカナ語の候補を追加したい場合はこのテキストファイルに追記すればよいということになります。

あとは全自動で実行するように設定するのみです。
こちらの記事を参考に、cronを使って自動化します。
fremilli.com
また、実際にツイートするpythonファイルでは
・上記のテキストファイルから名字、カタカナ語を取ってくるように変更
・テキストファイルはファイル名だけでなく、パスで場所を指定
と変更しました。cronから実行する際に、パスの指定がないとうまくいかなかったので要注意です。

これで、無事に自動で実行されるようになりました。

1日に1〜3回くらいのペースで呟く設定にしています。是非フォローしてください!
twitter.com




最後に、今回たくさんWikipedeiaの記事を見て、タイトルが一番面白かったものを発表します。

魚雷一覧

です。
最後まで読んでくださりありがとうございました。