MATLAB Answers:日本語の質問に回答してくれているのはどなた?
「MATLAB Answers に回答くださっている皆様、本当にありがとうございます。」
こんにちは、井上です。最近オンラインミーティング用に遅ればせながら女優ライトを導入しました。思いのほかまぶしいので、ここぞ!というときに活用しようと思います。
さて、MATLAB 関連の質問をするなら MATLAB Answers ですが、ここではどんな人が回答してくれているんだろう。MATLAB Answers 全体のランキングは用意されているんですが、日本語の質問に絞ると・・?
ということで日本語の質問に応えてくれている方を探し出し、 Twitter @JPMATLABAnswers からこっそり感謝を伝えてみることにしました。今回はこの全工程をご紹介いたします。見どころは Twitter Bot 化する為の ThingSpeak x GitHub Actions の連携です!コードは GitHub: who-are-the-contributors-on-MATLAB-answers-in-japanese に公開しています。
目次
まずは現状確認
MATLAB Answers の UI を確認します。
貢献度ランキング
まずこれ、コントリビューターリスト。
週間、月間、年間の貢献度ランキングがあります。ちなみにこの “評価” という数字は
- 自分の回答が採用される:4 ポイント
- 自分の回答に投票される:2 ポイント
- 自分の質問に投票される:1 ポイント
という形で数値化されてまして、ポイントがたまると質問、回答、およびコメントの編集や削除などもできるようになります。詳細はこちらにあります。
日本語への回答者は?
残念ながら上のランキングでは日本語の質問に回答してくれているかどうかを判別することはできません。そもそも日本語と英語両方に回答している場合考えると日本語フィルタ―は現実的ではないですね。
質問ページを調査する
質問ページを見てみます。こちらには日本語フィルタがありますね。そして MathWorks Support フィルタ。これは公式サポートチームが FAQ として公開しているものをさします。それ以外のポストはすべて ”コミュニティ” になります。
各ページには〇〇さんによって質問された、◇◇さんが回答したなどの情報があるので、これを吸い上げればいいかもしれません。
それでどうしたか?
- 一定期間内に更新された日本語の質問ページを抽出
- それらに関わるアカウント名を集計
- ランキング上位者を見つける
- Twitter で呟く
- GitHub Actions で定期実行
こんな感じですね。例えば週間ランキングであれば、過去1週間に更新のあった(すなわち回答やコメントが付いた)ページを対象にすればいいですね。
例えば「テキストマイニングによる共起ネットワーク図の作成について」のソースを見ると
href=”/matlabcentral/profile/authors/2229289″>Keisuke Miura</a>
href=”/matlabcentral/profile/authors/14080697″>Misa Taguchi</a>
と Keisuke Miura さんと Misa Taguchi さんの名前が出てきます。これを正規表現を使って抽出できそうです。まずは1から順番に。
1. 一定期間内に更新された日本語の質問ページを抽出
日本語・コミュニティの 2 つのフィルターを適用したページを開きます。「このビューを購読」すると RSS フィードが確認できます。RSS(RDF Site Summary/Rich Site Summary)はデータ形式の一種で、Webサイト内の新着ページや更新ページのタイトルや URL、更新日時、要約などを一覧形式で取得できるので便利。
XML 形式になっていますので、xmlread 関数を使います。
xDoc = xmlread([‘https://jp.mathworks.com/matlabcentral/answers’ …
‘/questions?language=ja&format=atom&sort=updated+desc&status=answered’ …
‘&page=1’]);
MATLAB Answers の場合は 1 ページあたり 50 個しか情報が含まれないので、page=1 とページ番号を振って過去にさかのぼっていくことになります。欲しい情報だけを取り出していきますが、XML 形式は中身の確認が面倒・・欲しい情報がどこに隠れているかを地道に探っていきます。
% まず各投稿は <entry></entry>
allListitems = xDoc.getElementsByTagName(‘entry’);
% アイテム数だけ配列を確保
title = strings(allListitems.getLength,1); % タイトル
url = strings(allListitems.getLength,1); % URL
author = strings(allListitems.getLength,1); % 投稿者
updated = strings(allListitems.getLength,1); % 最終更新日時
% 各アイテムから title, url, author 情報を出します。
for k = 0:allListitems.getLength-1
thisListitem = allListitems.item(k);
% Get the title element
thisList = thisListitem.getElementsByTagName(‘title’);
thisElement = thisList.item(0);
% The text is in the first child node.
title(k+1) = string(thisElement.getFirstChild.getData);
% Get the link element
thisList = thisListitem.getElementsByTagName(‘link’);
thisElement = thisList.item(0);
% The url is one of the attributes
url(k+1) = string(thisElement.getAttributes.item(0));
% Get the author element
thisList = thisListitem.getElementsByTagName(‘author’);
thisElement = thisList.item(0);
childNodes = thisElement.getChildNodes;
author(k+1) = string(childNodes.item(1).getFirstChild.getData);
% Get the <updated>2020-04-18T16:40:12Z</updated>
thisList = thisListitem.getElementsByTagName(‘updated’);
thisElement = thisList.item(0);
updated(k+1) = string(thisElement.getFirstChild.getData);
end
% 日時データは datetime 型に変換しておきます。
updated_at = datetime(updated,‘InputFormat’, “uuuu-MM-dd’T’HH:mm:ss’Z”);
updated_at.Format = ‘uuuu-MM-dd HH:mm:ss’;
% URL は以下の形になっているので、
% href=”https://www.mathworks.com/matlabcentral/answers/477845-bode-simulink-360″
url = extractBetween(url,“href=”””,“”””); % URL 部分だけ取得
entryID = double(extractBetween(url,“answers/”,“-“)); % 投稿IDを別途確保
ここまでの情報をテーブルにまとめると以下の通り。
pagelist = timetable(title, url, author, ‘RowTimes’, updated_at,…
‘VariableNames’,{‘titles’, ‘urls’, ‘authors’})
イイ感じです。
次は各ページでに関連するユーザーアカウントを探してくる作業を行います。
2. それらに関わるアカウント名を集計
例として1つ目の質問を見てみましょう。
名前は
href=“/matlabcentral/profile/authors/14080697”>Misa Taguchi</a>
と出ているはずなので、正規表現は
regexp(txt,‘href=”/matlabcentral/profile/authors/(?:\d+?)”>([^<].+?)</a>’,‘tokens’);
で行けるはず。
url = pagelist.urls(1)
txt = webread(url);
users_on_post = regexp(txt,‘href=”/matlabcentral/profile/authors/(?:\d+?)”>([^<].+?)</a>’,‘tokens’);
string(users_on_post)’
イイ感じ。2 回登場していますが、これは unique 関数を噛ましておけばOKでしょう。
上で抽出したページに対して回すことで、最近アップデートのあった日本語ページに登場するアカウント名を取り出します。
item2check = pagelist;
users = [];
for ii=1:height(item2check)
url = item2check.urls(ii);
txt = webread(url);
% href=”/matlabcentral/profile/authors/6704456″>Atsushi Ueno</a>
users_on_post = regexp(txt,‘href=”/matlabcentral/profile/authors/(?:\d+?)”>([^<].+?)</a>’,‘tokens’);
users = [users, string(users_on_post)];
end
% unique account
nicknames = unique(users);
idx = nicknames == “MathWorks Support Team”; % サポートチームは除いておきます。
nicknames(idx) = [];
nicknames(1:5)’
取れていますね。
3. ランキング上位者を見つける
例えば週間ランキングページの URL は
baseURL = “https://jp.mathworks.com/matlabcentral/answers/contributors/?filter=week”;
こんな様子です。これも各ページ 50 人の表示なので、それ以上の情報をとる場合は page を使います。
試しに上位 250 人を抽出するならこんな感じ。HTML ファイルをいじるなら webread 関数からの htmlTree 関数。これは Text Analytics Toolbox が必要ですが、要素を抽出するのに便利です。がんばれば正規表現で対応できそうですけども。
この辺は HTML のソースとにらめっこしながら欲しい情報がどこにあるか調べながらコードに落とし込みます。
perpage = 50; % ページ当たりの人数
pages = 1; % 5 ページ確認します(合計 250 人)
ranks = zeros(perpage*pages,1);
names = strings(perpage*pages,1);
for ii=1:pages
url = baseURL + “&page=” + ii;
a = webread(url);
b = htmlTree(a);
c = findElement(b,“td:first-child”);
for jj=1:perpage
index = (ii-1)*perpage+jj;
% 順位
tmp = findElement(c(jj),“div:first-child”);
ranks(index) = extractHTMLText(tmp);
% アカウント名
tmp = findElement(c(jj),“H4 > A > SPAN”);
names(index) = extractHTMLText(tmp);
end
end
names
錚々たる面子です。
この中から、上で探し出してきた日本語の質問に関連するユーザーアカウントを ismember 関数で抽出します。
idx = ismember(names,nicknames);
dataset = table(ranks(idx),names(idx),‘VariableNames’,{‘rank’,‘nickname’})
これは実行時の結果ですので、これを読まれている時点ではまた違う結果になっていると思います。
あとは Twitter への投稿文を構成して、後は呟くだけ!改行は newline が使えますので、こんな感じで文章を作ってみます。
status = “MATLAB の Q&A サイト:MATLAB Answers” + newline;
status = status + “日本語質問に回答する Top アカウント (weekly)” + newline + newline;
for ii=1:min(height(dataset),5)
status = status + “- ” + dataset.nickname(ii) + “さん” + newline;
end
status = status + newline + “ありがとうございます!” + newline;
status = status + “詳細:” + baseURL;
disp(status);
できあがり。
4. Twitter で呟く
Twitter に投稿するには Twitter API の取得が必要でその審査がちょっとハードル高い・・と心配された方。ご安心ください。ThingSpeak では ThingTweet という機能を提供していて、Twitter アカウントとリンクすれば、ThingSpeak 経由で Tweet することができます。Twitterアカウントをリンクして API Key を取得します。
もちろんできることは文字列を呟くだけ・・なので Twitter API と比較するとかなり機能は限定されますが、今回の用途には十分です。
コードはこんな感じ。
tturl=‘https://api.thingspeak.com/apps/thingtweet/1/statuses/update’;
api_key = getenv(‘THINGTWEETAPIKEY’); % 隠しておきます(適宜変更してください)
options = weboptions(‘MediaType’,‘application/x-www-form-urlencoded’);
options.Timeout = 10;
webwrite(tturl, ‘api_key’, api_key, ‘status’, status, options);
ThingSpeak を使った Twitter Bot の作り方については Qiita の MATLAB Answers の新着質問をお知らせする Twitter bot (Powered by ThingSpeak) も参考にしてください。
5. GitHub Actions で定期実行
さて、以上の処理(コード全体は tweetRanking_JP.m を見てください)を定期実行させるのに、今回は GitHub Action を使ってみます。ThingSpeak 上で MATLAB コードを実行できますし、TimeControl 機能があるので一定時間毎に実行させる設定は可能です。
ただ、ここはコードも GitHub に公開していることですし、せっかくなので(?)GitHub Action を使ってみます。MATLAB Actions が用意されているので MATLAB ライセンスは不要です。API_Key も Secrets 機能で隠ぺい化できますし、コードも公開できるメリットは大きいですね。
GitHub Action の設定はレポジトリ内の .github/workflows に yml ファイルを設定するだけ。
name: Run MATLAB Script on GitHub-Hosted Runner
on:
schedule:
– cron: ‘0 23 * * 0’
jobs:
my-job:
name: Run MATLAB Script
runs-on: ubuntu-latest
steps:
– name: Check out repository
uses: actions/checkout@v2
– name: Set up MATLAB
uses: matlab-actions/setup-matlab@v1
– name: Run script
uses: matlab-actions/run-command@v1
env:
THINGTWEETAPIKEY: ${{ secrets.THINGTWEETAPIKEY }}
with:
command: tweetRanking_JP
こんな感じです。cron: ‘0 23 * * 0’ のおまじないは UTC 23:00(日曜日)に実行してねという設定です。これは日本時間(JST)だと月曜日の朝 8 時ですね。
ポイントとしては API Key は実行時に環境変数として設定することで、MATLAB コードからも参照できるようになっています。
まとめ
毎週月曜日の朝8時に、ここで紹介したコードが実行され @MATLABAnswers から感謝の Tweet ができるようになりました。MATLAB Answers に回答くださっている皆様、本当にありがとうございます。
また、GitHub Actions は MATLAB のライセンス不要で使える=ややこしい設定不要なのでかなりお手軽ですので遊んでみてください。こんなことに使っているよ!という方、どんな GitHub x MATLAB 芸を構築されているのか、ぜひ教えてください。
コメント
コメントを残すには、ここ をクリックして MathWorks アカウントにサインインするか新しい MathWorks アカウントを作成します。