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

【Rails】関連(アソシエーション)をもったモデルを構造化してElasticsearchに格納する

Ruby Rails

RailsでElasticsearchを使ってみたときのメモ.
Articles <-> Authorships <-> Authors という関連を持ったモデル群があるときに,例えば記事(Article)を全文検索したら著者名も検索対象に入れたい.

gem ‘elasticsearch-rails
gem ‘elasticsearch-model’
を使った場合について書きます.

mappings

mappings dynamic: 'false' do
    indexes :title, analyzer: 'kuromoji_analyzer', index: 'analyzed'
    indexes :authors do
        indexes :name, analyzer: 'kuromoji_analyzer', index: 'analyzed'
    end
end

例えば上のような形で,構造化されたマッピングを定義可能です.

as_indexed_json

def as_indexed_json(options={})
    self.as_json(
        include: {
            authors: {only: :name}
        }
    )
end

モデル側で関連を貼れていればincludeするだけで関連先ごと格納可能です.
これで,以下のようなデータが入るようになるので,無事著者名も検索対象となりました.

{
    "title": "hoge",
    "authors": [
        {
            "name": "piyo"
        },
        {
            "name": "huga"
        }
    ]
}

RubyでHashをfreezeして定数にしたけど要素にアクセスできなかった

Ruby

Hashを定数として扱おうと思ったときのメモ.
Rubyの定数はミュータブルなので,freezeメソッドを使ってイミュータブルにしておく必要があります.
また,ArrayやHashなどを定数にするときには要素もfreezeしなければ,参照先オブジェクトの変化の影響を受けてしまいます.

Rubyで定数を扱う場合はfreezeするべき - Qiita
Ruby の定数やfreeze の扱い方が難しい :: by and for engineers

とかで詳しく説明されています.

コレクションを要素ごとfreezeする場合には,以下のようにmapを使うのが良さそうです.

hoge = {name: 'piyo', age: 30}.map(&:freeze).freeze

が,この定数に対して直感的に以下のようなアクセスをしようとしたらエラーになりました.

puts hoge[:name]

=> in `[]': no implicit conversion of Symbol into Integer (TypeError)

数値型を渡せと言われて気が付きましたが,そういえばmapの返り値はArrayなので,どうやらこのhogeはArrayのようです.
ということで,Hashをmapでfreezeした後もHashとして扱いたい場合には,to_hでHashに戻してあげると良さそうです.

hoge = {name: 'piyo', age: 30}.map(&:freeze).to_h.freeze
puts hoge[:name] 

=> "piyo"

AmazonLinux(EC2) + Selenium + Ruby でスクレイピング

Ruby AWS

Seleniumを使ってAmazonLinux上でスクレイピングをやってみたときのメモ.
nokogiri単体とかだとJavascriptで動的にHTMLが書き換わるようなサイトのスクレイピングはできませんが,SeleniumというWebブラウザ操作の自動化ツールを使えばそのようなスクレイピングも可能になります.
今回は,Xvfbを使ってAmazonLinuxのCUI環境でFirefoxを動かしてスクレイピングしてみます. プログラムにはRubyを使用しますがインストールは割愛. (Seleniumスクレイピングツールというわけではないので,ブラウザテストなどにも使用可能)

使うもの

Firefoxselenium-webdriberの組み合わせは最近のものだと上手く動作しなかったので,少し古いものを使用しています.

AmazonLinux(Amazon Linux AMI release 2016.09)
Firefox(45.3.0)
Ruby(2.2.4)
selenium-webdriver(2.53.4)
Xvfb(1.15.0)


Firefox 準備

まずはAmazonLinux上でFirefoxを動かすためのパッケージを準備します.

curl -X GET -o RPM-GPG-KEY-lambda-epll https://lambda-linux.io/RPM-GPG-KEY-lambda-epll  
sudo rpm --import RPM-GPG-KEY-lambda-epll  
curl -X GET -o epll-release-2016.09-1.2.ll1.noarch.rpm https://lambda-linux.io/epll-release-2016.09-1.2.ll1.noarch.rpm  
sudo yum -y install epll-release-2016.09-1.2.ll1.noarch.rpm
sudo yum --enablerepo=epll install firefox-compat


次にブラウザを準備します.

wget -O firefox-esr.tar.bz2 "https://download.mozilla.org/?product=firefox-45.3.0esr-SSL&os=linux64&lang=en-US"
bzcat firefox-esr.tar.bz2 | tar xvf -
mv firefox /usr/local/   #適当に/usr/localとかに移動


Xvfb 準備

Xvfbは仮想ディスプレイを作成するためのソフトで,これを使えばGUI環境が無くてもGUIが必要なソフトを動かせます.(ヘッドレス)
今回はGUI環境のないAmazonLinuxを使うので,Xvfbを利用してGUI無しでFirefoxを動作させます.

yum -y install xorg-x11-server-Xvfb # インストール
export DISPLAY=:99  # ディスプレイ番号の設定
Xvfb :99 -screen 0 1024x768x24 &  # バッググラウンドで起動
  
jobs  # これでステータスを確認可能. 停止にはkillコマンドを使用


Seleniumの準備

Selenium WebDriver というクライアントライブラリを用意します.
Rubyではgemで提供されています.

gem install selenium-webdriver -v 2.53.4
gem install nokogiri  # スクレイピングライブラリもインストールしておきます


プログラムの準備

スクレイピングプログラムを書いて終わりです.

require 'nokogiri'
require 'selenium-webdriver'

Selenium::WebDriver::Firefox::Binary.path = '/usr/local/firefox/firefox' # Firefoxを配置した場所を指定

driver = Selenium::WebDriver.for :firefox # :firefoxを指定
driver.navigate.to "スクレイピングしたいサイトのURLを指定"
html = Nokogiri::HTML(driver.page_source) # nokogiriと連携して解析可能
puts html
driver.quit # ドライバを閉じる


おわり

あとはシェルスクリプトでも書いてcronで動かせば,定期的にデータを収集できます.

RubyでHTTPクライアント→HTTPプロキシ→サーバの流れを実装してみる

Ruby

● HTTPクライアント (httpclient)
● HTTPプロキシ (webrick
● サーバ (CGI

を書いて、HTTP クライアントからプロキシ経由でリクエストを投げてサーバで受け取る、という一連の流れを実装してみようと思った時のメモ。
受け取り側のサーバだけ CGI を使っていて、 HTTP クライアントから POST で CGI サーバにファイルを飛ばしてみます。

HTTP クライアント

require 'httpclient'

proxy = 'http://127.0.0.1:8080'
client = HTTPClient.new(proxy)
boundary = 'boundary'
open('postfile.txt') do |file|
    postdata = {'file' => file}
    client.post_content(
        'http://192.168.99.99/cgi-bin/file.cgi', #ここに受け取り側サーバを指定
        postdata,
        'content-type' => 'multipart/form-data, boundary=#{boundary}'
    )
end

HTTP クライアントには、httpclient を使います。
このあたりの記事に Ruby の HTTP クライアントの比較について書かれています。

Ruby HTTPクライアントの比較表 - Qiita
Ruby の HTTP リクエストを送る方法の性能比較 - Qiita


HTTP プロキシ

require 'webrick'
require 'webrick/httpproxy'

class TestProxyServer < WEBrick::HTTPProxyServer
    def proxy_service(req, res)
        req.body =~ %r{filename=\"(.+?)\"} #受け取ったファイル名抽出
        puts $~[1]
        super #プロキシのメイン処理
    end
end

server = TestProxyServer.new(
    :BindAddress => '127.0.0.1',
    :Port => 8080,
    :Logger => nil,
)
Signal.trap('INT') do
    server.shutdown
end
server.start

プロキシには、webrick を使います。
今回はプロキシで処理したいことが特に思い浮かばなかったので、受け取ったファイル名を正規表現で抽出して出力しています。
ここには様々な処理を書くことが可能で、バナー広告等を除去するコンテンツフィルタを作ったり、開発用に特定の通信の接続先を書き換えたりする例を見つけたので載せておきます。

Rubyist Magazine - WEBrickでプロキシサーバを作って遊ぶ
開発用HTTPプロキシを簡単に立てる裏技 - Qiita


受け取り側サーバ

#! /usr/local/bin/ruby ここに ruby へのパス
require 'cgi'
cgi = CGI.new
cgi.out("type"=>"text/plain") {
  cgi["file"].read
}

こいつだけ CGI で書いています。 今回は Apache で動かしました。
ApacheCGI の連携は下の記事に簡潔に書いてあります。

Apache:CGIの利用設定をする - Qiita


おわり

あとは、HTTP プロキシと受け取り側サーバを起動して、HTTP クライアントを実行すれば、受け取り側サーバが送信ファイルの内容を出力するはずです。

【Twitter4J】特定のキーワードを呟いた人をフォローする的なやつ【Bot】

Java

※ 追記

こんな記事を書いておいてアレですが、どうやら Twitter では自動フォローは規約で禁止されているみたいです。
ここに書いてあるようなことを実行してバレると、アカウントを凍結されます。注意。

support.twitter.com

=========================

Twitter 上で行えることを自動化してみようと思ったときのメモ。
例として、標題の機能だけここに書いていきます。

TwitterAPI をそのまま使用するのはなにかと面倒なので、今回は Java でその辺をチョロチョロっと書けるようにしてくれる Twitter4J というライブラリを使います。

Twitter アカウントの用意

当然動作させるアカウントが必要です。 作ります。

アプリケーション登録

以下の記事に分かりやすく書いてあるので引用。

Twitter APIの使い方まとめ

一通り読み進めれば下の値が得られているはずなので、メモしておきます。(アプリケーション管理ページの [Keys and Access Tokens] というページに記載)

これらは TwitterAPI を活用する上で必要な OAuth という認証方式で使用する値ですが、説明すると長くなるので割愛します。

OAuthプロトコルの中身をざっくり解説してみるよ - ( ꒪⌓꒪) ゆるよろ日記

OAuth 2.0 の仕組みと認証方法 | murashun.jp

Twitter4J 用意

Twitter4J - A Java library for the Twitter API ここから jar ファイル群をダウンロードできるので、適当に配置した後プログラムから使用できるようにパスを通しておきます。

コード書く

package twitter;

import twitter4j.*;
import twitter4j.auth.*;
import java.util.*;

public class Test {
    final static Twitter twitter = TwitterFactory.getSingleton();

    public static void main(String[] args) {
        // OAuth 認証
        twitter.setOAuthConsumer("ここにConsumer Key (API Key)", "ここにConsumer Secret (API Secret)");
        AccessToken token = new AccessToken("ここにAccess Token", "ここにAccess Token Secret");
        twitter.setOAuthAccessToken(token);

        List<Status> tweets = search("キーワードです");
        for (Status tweet : tweets) {
            try{
                twitter.createFriendship(tweet.getUser().getId()); // フォロー
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    public static List<Status> search(String keyword) {
        Query query = new Query();
        query.setQuery(keyword);
        query.setCount(100); // 100 -> 1度のリクエストで取得するツイート数
        List<Status> tweets = new ArrayList<>();
        try {
            for (int i = 0 ; i < 15 ; i++) { // 15 -> 検索するページ数 , この場合だと100件×15ページで最大1500件取得
                QueryResult result = twitter.search(query);
                tweets.addAll(result.getTweets());
                if (result.hasNext()) {
                    query = result.nextQuery();
                } else {
                    break;
                }
            }
        } catch (TwitterException e) {
            e.printStackTrace();
        }

        return tweets;
    }
}

おわり

今回は特定のキーワードを呟いた人をフォローするだけでしたが、Twitter上で手動で出来ることならほぼTwitter4Jからでも可能かと思います。

DebianにNVIDIAのドライバを入れたかったけど躓いた


いくつか躓くポイントがあったのでメモ。
以下、手順を書いていきます。
 

ドライバのダウンロード

これが無ければ始まりません。
以下から自分のビデオカードに対応するドライバを適当な場所にダウンロードします。

NVIDIAドライバダウンロード

 

ドライバのインストール

sh NVIDIA-Linux-xx-xxx.xx.run (ここはダウンロードしてきたファイル名)

するとエラーが。

ERROR: You appear to be running an X server; please exit X before            
         installing.  For further details, please see the section INSTALLING   
         THE NVIDIA DRIVER in the README available on the Linux driver         
         download page at www.nvidia.com.

Xサーバーが動いているからインストールできない的なメッセージが表示されます。

まずは必要なものをインストール。

sudo aptitude install build-essential linux-source linux-headers-`uname -r` xserver-xorg-dev


そしてgdmを停止します。

sudo /etc/init.d/gdm3 stop

ここで画面が真っ暗になります。
その状態で、[Control]+[Alt]+[F2] でコンソール画面に切り替えます。

適当なユーザでログインして、先程のインストールコマンドを再度入力します。

sh NVIDIA-Linux-xx-xxx.xx.run

するとまたしてもエラーが。

ERROR: The Nouveau kernel driver is currently in use by your system.  This
         driver is incompatible with the NVIDIA driver, and must be disabled
         before proceeding.  Please consult the NVIDIA driver README and your
         Linux distribution's documentation for details on how to correctly
         disable the Nouveau kernel driver.

NVIDIAドライバと競合するような既存のドライバが入っているようです。
恐らくデフォでオープンソースのドライバが入ってしまっているので、削除します。
/etc/modprobe.d/nvidia-installer-disable-nouveau.conf を開いて、以下を記載します。

blacklist nouveau
options nouveau modeset=0


保存したら再起動します。

sudo reboot


三度目の正直。 まずは gdm を停止します。

sudo /etc/init.d/gdm3 stop


インストール。

sh NVIDIA-Linux-xx-xxx.xx.run


やっとエラーなくインストーラーに入ることが出来ました。
あとは適当にOKとか選択していけば完了です。

おわり。

AWSのAutoScalingの設定をしてみる

AWS

AWS の AutoScaling 機能を使う機会があったので、忘れないうちに設定方法を書いておきます。

AutoScaling とは

負荷に合わせて EC2 インスタンスの数を自動的に増減してくれる機能です。
増減はさせずに、インスタンスの数を常に一定にしておくためだけに利用したりもできます。
AutoScaling の利用には追加費用は発生しません。(当然ですが、AutoScaling によって自動的に追加された分のインスタンスの利用料はかかります。)
負荷に合わせて、と書きましたが、トリガーには CloudWatch のメトリクスなどが使用できます。

以下の記事に分かりやすく概要が書いてあります。
【AWS】Auto Scalingまとめ - Qiita

設定

今回はマネジメントコンソールを使ってGUIで設定をしていきます。
AutoScaling を利用するためには、[起動設定] と [AutoScalingGroup] を作成する必要がありあす。

[AutoScalingGroup] では、インスタンスを起動するAZやトリガーなどの設定します。
[起動設定] では、インスタンスサイズやセキュリティグループ等の、起動するインスタンスの情報を設定します。

起動設定の作成

AutoScalingGroup を作成する前に、あらかじめ起動設定を作成しておく必要があります。

これで起動設定の作成は終わりです。

AutoScalingGroup の作成

  • [EC2 Management Console] のサイドバーにある [AutoScalingグループ] → [AutoScalingグループの作成] をクリックします。

  • 先程作成した起動設定を選択します。

  • [AutoScalingグループの詳細設定] の [高度な詳細] をクリックすると色々出てくるので設定していきます。
    ELB 配下のインスタンスを AutoScaling 対象にする場合には [ロードバランシング] にチェックを入れて、使用する ELB を選択します。
    [ヘルスチェックのタイプ] を [ELB] に設定すると、「EC2 インスタンス自体は生きているけど ELB のヘルスチェックが通らない」状況の時などに異常ありと判断され、インスタンスの置き換えが行われるようになります。
    [ヘルスチェックの猶予期間] には、インスタンスの起動後、サービス開始できるようになるまでの時間(ビルドやデプロイ等に掛かる時間)以上の値を入力します。
    ELB ヘルスチェックを正しく返せない状態でこの AutoScaling ヘルスチェックが開始されてしまうと、起動と削除が繰り返されていつまでたってもサービスが提供できない状態に陥るので注意が必要です。(確か EC2 インスタンスは起動した時点で1時間分の課金が行われるので、起動と削除が繰り返されると厄介です。)

  • [スケーリングポリシーの設定] では、トリガーの設定等を行います。
    負荷に合わせてインスタンスの増減をする場合は、[スケーリングポリシーを使用して、このグループのキャパシティを調整する] を選択し、インスタンス数の範囲と、インスタンス増減のタイミングを指定します。
    [グループサイズの増加] と [グループサイズの減少] のそれぞれで [新しいアラームの追加] を行い、閾値を指定します。
    [ステップを追加] で、負荷の度合いに応じて増減させるインスタンス数に変化をつけることも可能です。
    [ウォームアップ] の秒数を指定すると、インスタンス増加のイベントが発生した後、指定した時間が経つまで次の増加イベントが発生しなくなります。 (デフォルトでは5分)

  • [通知の設定] では、AutoScaling で発生したイベントを指定したメールアドレス等に通知する設定ができます。
    通知が必要な場合は [通知の追加] をクリックします。

  • 最後に起動するインスタンスへのタグ付けをして終わりです。

おわり

他にも色々な設定が可能ですが、一応これだけで AutoScaling グループが起動しインスタンスが作成されます。