読者です 読者をやめる 読者になる 読者になる

(O+P)ut

習うより慣れろ、Practice makes perfect。この言葉をモットーに、Slerで働く若手インフラエンジニアが、学んだ知識を【 (O+P)ut = OutPut 】していく場です。

その日、「最も」話題となったキーワードを調べてみる

テクノロジー

Twitterからの情報を取得しながら何かをしようとすると、Twitterと連携するためのOAuth認証を行わなければなりません。
Twitter4J - A Java library for the Twitter APIなどを使えば、もちろん認証周りをスムーズにはしてくれるのですが、やはり認証用アカウントの取得は必須です。

一方で、このようなアカウントが不要でTwitterの情報が使用できるサービスに
Yahoo!検索があり、ここでは最新の話題ワードTop20が表示されています。*1

ここのhtmlを取得すれば簡単に世間の関心等をフォローすることができますので、紹介ついでに少し解析して遊んでみたいと思います。

過去の記事同様 【Linux】wgetで桂離宮の参観可能日を取得する - (O+P)ut
wgetで取得してもいいのですが、今回は jsoup( Java で HTML の解析・編集を行うためのライブラリ)を使ってhtmlを取得しました。

ライブラリを取得し、org.jsoup...をインポートさえ行えば

String url = "https://search.yahoo.co.jp/realtime"
Document document = Jsoup.connect(url).get();
String str = document.html();

たったこれだけの記述でstrにhtmlが入っています。

あとは、htmlの構造を見ながら必要な箇所のみを出力して以下のようにTop20のキーワードを吐くようにしました。*2

java -jar getHotTag.jar 
パラド,グラファイト,ジュリオ,17号,永夢,エグゼイド,飛彩,マーダッコ,リオ,パラドクス,リプログラミング,アンラッキー,界王様,いちか,スティンガー,サンデーモーニング,プリキュア,ポッピーピポパポ,キュウレンジャー,リュウテイオー

これを10分毎に起動して`date +%Y%m%d`.csvファイルに出力し、「その日、最も話題となったキーワード」を調べてみます。
2017/4/23 0:00~24:00 での計144回の検索結果を以下のワンライナーでソートしました。

cat 20170423.csv | awk 'BEGIN{RS=",";ORS="\n"}{print $0}' | awk '{count[$0]++}END{for(i in count)print i","count[i]}' > result.txt

RS=",";ORS="\n"によってカンマ区切りを全て改行にし、count[$0]とすることで連想配列としてカウントしています。

ちなみに、上の出力結果を wc で確認すると

$ wc result.txt 
     265     305    4577 result.txt

つまり、4/23 に「最新の話題ワードTop20」に出てきたワードは265種類だということになります。

この265種類のワードをcountの値でソートして出力した結果はこちらです。

f:id:mtiit:20170423235920p:plain

ぶっちぎりの一位が確認できます。
さて、上位3位はこのようになっています。

1. ナオトインティライミ (count:93)
2. エキシビション (count:43)
2. 不審物 (count:43)

一位に関しては、
f:id:mtiit:20170424000129p:plain
とのことでTwitter上で賑わっているようです(笑)

以上です。

*1:非公開に設定されていない日本語のツイートを主な対象として表示している模様

*2:2017/4/23/10:00あたり

【Java】簡単モザイク生成

Java テクノロジー

これはなんのキャラクターでしょうか。
f:id:mtiit:20170314232445j:plain
どことなく、国民的アニメの主人公に色合いが似てる気がしませんか?

少し前に、モザイクをかけても人間はアニメキャラクターを見破ってしまう というのが話題になりました。
私も同じように、画像を配列と見た時に各行の平均の色を出力することで簡易モザイクを作成する遊びをしてみたのでここに共有します。

Javaでの画像取得は以下のように行います。BurreredImageのメソッドを用いて縦横のサイズも取得可能。

File f = new File("[入力画像のフルパス]");
BufferedImage read=ImageIO.read(f);
int w = read.getWidth();
int h = read.getHeight();

さて、画像の各画素を取り出して平均をとりたいんですが、
BufferedImageでは画素を取得するメソッドgetRGBを用いると、 R G B を8bitずつで表現する以下のような形が返り値となっています。
00000000 00000000 00000000 00000000
なので、R G B をそれぞれ取り出すために

public class Img{
 public static int r(int c){
  return c>>16&0xff;
 }
 public static int g(int c){
  return c>>8&0xff;
 }
 public static int b(int c){
  return c&0xff;
 }
 public static int rgb(int r,int g,int b){
  return 0xff000000 | r <<16 | g <<8 | b;
 }
}

といったクラスを用意しておくと便利ですね。
rgbは、各r,g,bを基に元の値を構成するメソッドです。

画像の各列の画素の平均値の画像を出力するコードは以下になります。

public class Main {
 public static void main(String[] args) throws IOException {
  File f = new File("[入力画像のフルパス]");
  BufferedImage read=ImageIO.read(f);
  int w = read.getWidth();
  int h = read.getHeight();
  BufferedImage write = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
  for(int y=0;y<h;y++){
   int r = 0;
   int g = 0;
   int b = 0;
   for(int x=0;x<w;x++){
    int c = read.getRGB(x,y);
    r = r + Img.r(c);
    g = g + Img.g(c);
    b = b + Img.b(c);
   }
   int n_r = r/h;
   int n_g = g/h;
   int n_b = b/h;
   for(int xx=0;xx<w;xx++){
    int rgb = Img.rgb(n_r, n_g, n_b);
    write.setRGB(xx, y, rgb);
   }
  }
  File f2 = new File("[出力先のフルパス]");
  ImageIO.write(write, "jpg", f2); //jpgで出力
 }
}

最初に見せたモザイク画像の元画像はこちらです。
f:id:mtiit:20170314234708p:plain
両側が黒くなっているのは、透明化している部分の画素が 0/0/0 で 黒と同じだからみたいです。
この辺は、改良の余地ありですね。以上です。

【Linux】wgetで桂離宮の参観可能日を取得する

テクノロジー Cygwin Linux

f:id:mtiit:20170213141405j:plain
この写真は、約1年前に桂離宮で私が撮ったものです。

日本には、桂離宮修学院離宮の二つの現存する離宮があります。*1
これらの参観は無料ですが、事前に予約を行うのが一般的です。詳しくは宮内庁のHP参照。
予約はWebからもでき、例えば桂離宮について見てみますと以下のようにWebで空き人数付参観可能日が確認できます。
f:id:mtiit:20170213142210p:plain
これらの情報を、Webサーバーからファイルをダウンロードするためのコマンドwgetを用いて取得してみたいと思います。

2017年2月現在、上のページのurlは

"https://sankan.kunaicho.go.jp/register/frame/4201?ym=[西暦4桁][月二桁]"

という命名規則となっています。

標準出力に出すためのオプション -O - を使いまして来月(201703)でwgetをしますと以下のようにエラーとなります。※環境によっては証明書の発行者が不明というエラーが先に出る場合があるかとは思いますが。*2

wget -O - https://sankan.kunaicho.go.jp/register/frame/4201?ym=201703<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.<br><br>Your support ID is

The requested URL was rejected に関してですが、調べてみますと
"ユーザエージェントWgetを排除している可能性がある"
ということが分かりました。宮内庁のHPに関しては、他のページも同じエラーが起きました。悪戯対策なんですかね?
オプション -U "" を用いて明示的にユーザエージェントを空欄に変更してみると、

wget -O - -U "" https://sankan.kunaicho.go.jp/register/frame/4201?ym=201703<html>
  <head>
    <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <META http-equiv="Content-Style-Type" content="text/css">
...以下略

といった形で正規のhtmlを取得できました。

あとは、
日付情報↓

<TD width="20" align="center"><B>01</B></td>

参観可能人数情報↓

<img src="/images/false.gif">10:00(0人)

といったような構造になっておりましたので、以下のワンライナーで結果を取り出してみました。
読み方は、3月1日の15:30から2人空いている、といった感じです。

$ wget -O - -U "" https://sankan.kunaicho.go.jp/register/frame/4201?ym=201703 2>/dev/null | grep -B 10 '[1-9]人' | grep -e '[1-9]人' -e '<B>[0-9][0-9]</B>' | awk 'BEGIN{FS="[><]"}{print $5}'01
15:30(2)
02
10:00(1)
15:30(2)
03
09:00(4)
13:30(1)

ワンライナーに関して簡単に説明します。
大部分は 0人 ですので 0人以外のところを grep -B 10 '[1-9]人' で拾います。-Bオプションをつけることで、一致した行から前10行を同時に表示し、日付情報を拾い出しています。htmlを見ますと、日付~参加可能人数が10行毎に繰り返していたので今回はこのようにしています。そして、改めて日付と参加可能人数の情報をgrepで拾い出し、awkで欲しい箇所を抽出しています。今回は、< または > で区切るとどちらも5列目でしたので上のようにしました。

ちなみに、5月だと1日も空いてませんでした。。。
京都に立ち寄る予定がある方は、ぜひ事前に調べてみてください。

*1:修学院離宮にも行きました。どちらもぜひ一度は行ってみる価値ありです!

*2:接続先が信用できる場合では --no-check-certificate を追記ですれば簡易的に対応可能です。

【Linux】とりあえず2つだけ覚える、変数のパターンマッチ

テクノロジー Linux Cygwin

bashでは「%」や「#」を用いた変数のパターンマッチがありますが、最近使うことが増えてきたのでメモとして書いておきます。

紹介するのは、とりあえず2種類です。

${変数#パターン}
${変数%パターン}

[ファイル名][数字].[拡張子]のようなファイルを一気に処理したい場合等に使えたりして便利です。

紹介のために、以下のような4つのファイルを用意しました。

$ ls
file1.txt  file2.txt  file3.txt  file4.txt

4つのファイルをワンライナーでいじろうとすると以下の書き方が使えます。
fileで始まるファイルを変数xに格納して、echoで画面に表示しています。

$ for x in file*; do echo $x; done ⏎
file1.txt
file2.txt
file3.txt
file4.txt

さて、まずは ${変数#パターン} です。
ざっくり言えば "パターン部分を除いたファイル名の後半を取り出したい時" に使います。
正確に書けば、"変数の内容について、最初の部分とパターンがマッチした場合に、もっとも短く一致する部分を取り除いた残りの部分を返す" 役割が ${変数#パターン} にはあります。

以下を見ていただければイメージが掴めるかと思います。

$ for x in file*; do echo ${x#file}; done ⏎
1.txt
2.txt
3.txt
4.txt

これを応用すれば、file[数字].txt ではなく text[数字].txt としたい時などに使えます。

$ for x in file*; do mv $x text${x#file}; done ⏎
$ ls ⏎
text1.txt  text2.txt  text3.txt  text4.txt

次に、 ${変数%パターン} です。

こちらもざっくり言えば "パターン部分を除いたファイル名の前半を取り出したい時" に使います。
正確に書けば、"変数の内容について、最後の部分とパターンがマッチした場合に、もっとも短く一致する部分を取り除いた残りの部分を返す" 役割が ${変数%パターン} にはあります。

こちらも以下を見てください。※上の実行後に行っているので、ファイル名はtextから始まっています。

$ for x in text*; do echo ${x%.txt}; done ⏎
text1
text2
text3
text4

これを応用すれば、拡張子を一気に変えたい場合なんかに使えそうですね。

他にも、「##」や「%%」などもあるんですが、とりあえずは上の2個を覚えて必要あらば詳しく調べるのが良いかなーと思います。

【Cygwin】作業ログを自動で残す

テクノロジー Cygwin Linux

明けましておめでとうございます。今年は、少なくとも月1ペースで更新したいと考えています!

さて、Cygwinでちょこちょこと作業をすることが多いのですが script コマンドなるものを使えば、簡単に作業ログを残せることを知りました。
それを自動で行えるようにした際の備忘録です。

scriptコマンドの使い方は、ターミナルで script と打てば以下のようになるかと思います。

$ script
スクリプトを開始しました。ファイルは typescript です

ここから記録が ./typescript に始まり、終了する際は exit と打てば以下のように終了します。

$ exit
exit
スクリプトを終了しました。ファイルは typescript です

オプションや引数をつけることで、ファイルを上書きしたりファイル名を指定もできます。

問題は、これを起動時に自動で実行しようとする際です。
単純に .bashrc に

script

と書くとすごいことになります。以下が私の環境で実際に上記の記述を.bashrcに行い、起動した際の出力です...

スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です
スクリプトを開始しました。ファイルは typescript です

実はscriptコマンドは新たにシェルを立ち上げる挙動をするようですので、新たなシェルが立ち上がる→scriptコマンド実行→新たなシェルが... の無限ループになるみたいですね。

$ ps
      PID    PPID    TTY       STIME COMMAND
    13052       1    ?         01:08:49 /usr/bin/mintty
     6288    1936    pty0      01:08:56 /usr/bin/ps
     1936   13052    pty0      01:08:49 /usr/bin/bash

$ script
スクリプトを開始しました。ファイルは typescript です

$ ps
      PID    PPID   TTY      STIME COMMAND
     1288    5712  pty0      01:09:01 /usr/bin/script
    11180    4176  pty1      01:09:04 /usr/bin/ps
     5712    1936  pty0      01:09:01 /usr/bin/script
     4176    1288  pty1      01:09:02 /usr/bin/bash
    13052       1  ?         01:08:49 /usr/bin/mintty
     1936   13052  pty0      01:08:49 /usr/bin/bash

上がすごい分かりやすいと思います。pty1が立ち上がってカレントシェルが変更になります。

なので、今回は簡易的に現在のシェルの親プロセスが /usr/bin/mintty な場合にのみ script を動かすようにします。

autoscript.shというスクリプトを記述しました。*1

#!/bin/sh

ppcheck=`ps | grep $PPID | grep -v pty | awk '{print $8}'`
ttynum=`echo \`tty\` | awk '{print substr($0,(length($0)+1)-1,1)}'`
nowtime=`date +"%Y%m%d%H%M%S"`
filename="${nowtime}_${ttynum}"

if [[ ${ppcheck} = "/usr/bin/mintty" ]]; then
        script -f script_log/$filename
fi

簡単に説明しますと、ppcheckで現在のシェルの親プロセスのIDでプロセスを絞り込み、ttyが?となっている/usr/bin/minttyをppcheckとして取り込んでいます。
それを、現在のttyの最後の数字と現在の時刻とするファイル名でログをとる、という感じですね。こうしておけば、scriptで立ち上がったシェルの親プロセスは/usr/bin/minttyではなくなるので、ループは起きないといった感じです。ファイル名は、時刻だけだと同時にCygwinの窓を複数立ち上げるとやっかいかなーと思ったのでttyから値を持ってきてます。特に深い意味はありません...

これを起動時に実行するために .bashrc に以下のように記載します。

. ./autoscript.sh

カレントシェルで実行するよう記述しているのは、親プロセスが変わってしまうのを防ぐためです。

これでCygwinを立ち上げると

スクリプトを開始しました。ファイルは script_log/20170129011831_0 です

$ ls script_log/
20170129011831_0

とまぁ、こんな感じで機能しています。

とりあえずこれで運用してみて、挙動が怪しい場合にはまたその都度修正していこうと思います。
Cygwinをお使いの方で、同じようなことに興味がある方はご参考ください。

*1:直接.bashrcに記述してもいいですが、テストしやすかったのでそのままshファイルを起動するという構成にしています

マージソートの可視化

テクノロジー R アルゴリズム

ちょうと1年前くらいに
mti.hatenablog.com
といった記事を書いたんですが、そこでは選択ソートとバブルソートを可視化しました。

今回はマージソートの可視化を行います。使用するのも前回同様 R言語で。

マージソートとは、並べ替えたい配列を再帰的に分割していき、再び併合(マージ)していくことで並び替えを実現しようとするソートアルゴリズムです。

今回は配列をx、マージソート関数をf、基準値をpivotとし、基準値は配列xの平均値とします。

pivot=ave(x)[[1]]

そして、配列xの各値が基準(pivot)より大きいか小さいかで配列をleft,rightに分割

for(k in 1:length(x))
    {
      if(x[k] <= pivot)
      {    
        left <- append(left,x[k])
        count <- count + 1
      }
      else
      {
        right <- append(right,x[k])
      }
    }

分割したleft,rightを再起的にまたfにつっこむ流れですね。*1

分割の回数で上記のマージソートを可視化したものが以下になります。
f:id:mtiit:20161103103618g:plain
一秒毎に8回目までのソートの経過を可視化しました。選択ソートやバブルソートとは全く異なっていることが分かります。


おまけとして、pivotを

pivot_t=ave(x[1:2])[[1]]

とすればどうなるでしょう。直観的には、基準が前の二つの平均値となるので少し偏ったpivotで配列を分割していくことになりそうです。
同じく8回目までのソートの経過を可視化したものが以下になります。
f:id:mtiit:20161103104419g:plain
8回だけでは並べ替えきれていない感じがします。

以上、マージソートの可視化でした。*2

*1:countは配列の結合で用いる配列の引数としてfに渡してました

*2:分割と併合を繰り返したものの経過の可視化はマージソートWikipediaにも掲載されていましたのでご参考まで

【Java】APIリクエストでファイルを取得する

テクノロジー Java

APIリクエストってのはWeb APIのことですね.
HTTPをベースにしてデータをやり取りするので,一般的にはWebサイトへのアクセスを行うようにブラウザを用いて操作を行います.
例えば,今回の場合ではURLを入力してGETメソッドでアクセスする*1ことで,csvファイルをアクセスできるという状況が既にあります.

そんな中で例えば「日付でURLが分かれているファイルを一気に取得したい」であったり「csvファイルの特定の項を編集しながら保存したい」といったようなことをする必要もありますよね.自分はそのような状況だったので,Javaを使ってcsvファイルを取得する方法について記述します.*2

簡単なサンプルコードは以下になります.

URL url;
try {
	String url_s = "[アクセスするURL]";
	url = new URL(url_s);
	URLConnection conn;
	conn = url.openConnection();
	InputStream in = conn.getInputStream();
	BufferedReader br = new BufferedReader(new InputStreamReader(in));
	String line;
	while ((line = br.readLine()) != null) {
		System.out.println(line);
	}
	br.close();
} catch (MalformedURLException e) {
	e.printStackTrace();
}
catch (IOException e) {
	e.printStackTrace();
}

今回は一行ずつlineを標準出力していますが,必要に応じてファイルに保存します.
また,csvファイルの各項を取り出したいのなら

token = new StringTokenizer(line, ",");
while (token.hasMoreTokens())
{
   //処理を書く
         //token.nextToken()で項を順に取得
}

をline毎に行います.

備忘録でした!

*1:要は普通にWebブラウザを使ってアクセスする

*2:おそらく他の言語ならもっと簡単?