george's ぶろぐ

Rubyでスクレイピング !

Webサイトをスクレイピングしてみました。いろいろと勉強も兼ねてRubyで作ってみました。
UFJ銀行のネットバンキングのWebページにログインして、表示される金額を取得します。

f:id:whippet_818:20170222231147j:plain:w500

環境

CentOS 6.7
ruby 2.3.1

サーバ側でRubyを実行します。処理内でブラウザからログインし、値を取得します。
少し前までは mechanize というので、Webサイトに対して入力や送信を行うのが普通だったみたいなんですが、Javascript が動くサイトではそれを解釈・実行できないので、最近はCapybara というライブラリを使うのが一般的なようです。


準備

PhantomJS

PhantomJS は画面のない、ヘッドレスなブラウザだそうです。GUI の自動テストに使用されたりするようです。

yum でインストールします。
CentOS6にyumでphantomjs 2.1.1をインストール - Qiita ここに書いてあるとおりです。

$ sudo yum install epel-release
$ sudo rpm -ivh http://repo.okay.com.mx/centos/6/x86_64/release/okay-release-1-1.noarch.rpm
$ sudo yum search all phantomjs

Capybara と Poltergeist を入れる

以下をgem ファイルに追記します。

gem 'capybara'
gem 'poltergeist'

追記後bundle install を実行します。
これで準備が整いました。以降はruby でゴリゴリ作っていく工程です。

Webサイトにアクセス~ログイン

  • モジュールの導入
require 'capybara'
require 'capybara/dsl'
require 'capybara/poltergeist'
  • Capybaraの設定
Capybara.configure do |config|
  config.run_server = false
  config.current_driver = :poltergeist
  config.javascript_driver = :poltergeist
  config.app_host = "http://direct.bk.mufg.jp/"
  config.default_wait_time = 5
end

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, {:js_errors => false, :timeout => 1000 })
end
  • Webサイトにアクセス~ログインページに移動
page.driver.headers = { "User-Agent" => "Mac Safari" }
visit('')
find(:xpath, "//*[@class='mt5']/a/img[contains(./@alt, 'ログイン')]").click
switch_to_window { title == 'ログイン - 三菱東京UFJ銀行' }

ログインページのタブへ移動します。ここ結構詰まりました。UFJのサイトの場合、ログインページには直接アクセスできませんでした。ログインボタンを押してから、新しいタブに生成されるページに移動する必要があります。(このページの最初に貼ってあるイメージです)

※参考 -タブの遷移
Method: Capybara::Session#switch_to_window — Documentation for jnicklas/capybara (master)


  • 契約番号とパスワードの入力
keiyaku_input = find(:xpath, "//input[@type='text']")
keiyaku_input.native.send_key('あなたの契約番号')
password_input = find(:xpath, "//input[@type='password']")
password_input.native.send_key('あなたのパスワード')

契約番号 とパスワード を入力します。 基本的にXPath で入力部分を探していますが、以下の場合でもOKでした。  

fill_in "KEIYAKU_NO", :with => 'あなたの契約番号'

fill を使って入力する方法や、

password_input = find('input#ib_password') 
password_input.native.send_key('あなたのパスワード')

find でもいろいろな検索方法が使えるようです。

XPath についてはこちら
xpathまとめ - Qiita


  • ログインボタン押下後、残高部分を出力
find(:xpath, "//p[@class='acenter admb_m']/a/img[contains(./@alt, 'ログイン')]").click
puts find(:xpath, "//td[@class='number']").text

プログラムを実行し、ターミナルに「~~円」と出力されることを確認します。


全体

require 'capybara'
require 'capybara/dsl'
require 'capybara/poltergeist'

Capybara.configure do |config|
  config.run_server = false
  config.current_driver = :poltergeist
  config.javascript_driver = :poltergeist
  config.app_host = "http://direct.bk.mufg.jp/"
  config.default_wait_time = 5
end

Capybara.register_driver :poltergeist do |app|
  Capybara::Poltergeist::Driver.new(app, {:timeout=>120, js_errors: false})
end

module Scraper
  class ScraperUfj
    include Capybara::DSL

    def initialize(userID, password)
      @userID = userID
      @password = password
    end

    def open_loginpages
      page.driver.headers = { "User-Agent" => "Mac Safari" }
      visit('')
      find(:xpath, "//*[@class='mt5']/a/img[contains(./@alt, 'ログイン')]").click
    end

    def balance_get
      switch_to_window { title == 'ログイン - 三菱東京UFJ銀行' }
  
      keiyaku_input = find(:xpath, "//input[@type='text']")
      keiyaku_input.native.send_key(@userID)
      password_input = find(:xpath, "//input[@type='password']")
      password_input.native.send_key(@password)

      # click_link "ログイン"
      find(:xpath, "//p[@class='acenter admb_m']/a/img[contains(./@alt, 'ログイン')]").click
      puts find(:xpath, "//td[@class='number']").text
    end
  end
end

scraper = Scraper::ScraperUfj.new('あなたの契約番号', 'あなたのパスワード')
scraper.open_loginpages
scraper.balance_get

感想とか

途中何度か詰まりながらも試行錯誤しながら、なんとか動きました。今回ゼロベースの知識からはじめましたが、知らない技術を身につける過程はトキメキます。スクレイピング面白いよ!

保守運用チームのためのカンバン

この1年以上、保守運用チームのリーダー的な立場をやってきたので、重点を置いた取り組みをまとめます。だいたいのことは カンバン: ソフトウェア開発の変革 に記載されいることをそのまま流用しているだけなので、読んだことのない方は是非読んでみて下さい。


作業の仕掛り数に制限を設ける

1人が担当するタスク数を最小限に減らし、1つのタスクをまずは集中して完了させてもらうようにしました。カンバン: ソフトウェア開発の変革 には着手中のタスクを仕掛り(WIP:Work-in-progress)という言葉で定義しています。
今の保守運用チームには顧客からの問い合わせやシステム障害などいろんな方面から続々と連絡が来るので、その度に来たタスクに対して少しずつ手を付けてしまい、結局は何一つ終わらないという状況でしたので、まずはこれからはじめました。

そのためのツールとして、Redmineを利用し、メンバー毎に今着手すべきタスクが何であるか、明確に見えるようにしました。

f:id:whippet_818:20170210200124j:plain:w700

Redmineカスタムクエリ を使用してメンバーごとにグルーピングしました。担当してもらうタスクをリーダーがメンバーに割り振ります。割り振るタスクは2~4個を目処に運用しました。


チームが持つタスクを見える化する

チームメンバー全員がそれぞれの役割と他メンバーの状況を把握するため、現在の作業状況とこの先1ヶ月間の予定を共有できるようにしておく必要があります。
保守運用チームという特性上、インフラ関連の障害やアプリの不具合修正、見積依頼等あらゆるタスクが色々なところから降ってきます。それらをすべてのタスクをRedmineで管理し、加えて1週間単位のざっくりとした見通しを中日程と呼ばれるもので管理しました。


優先順位付け

仕掛りの制限を設定するためにも現時点でどのタスクが優先であるか決める必要があります。この優先順位付けの会議を毎週定例で開催し、マネージャーまたはプロジェクトによっては顧客と実施します。会議の調整コストを下げるため、参加する人数が多ければ多いほど定例化が必要です。
会議の場では、例えば予定外のタスクをお願いされた場合「では、その予定外のタスクを最優先に対応するので、この部分のタスクは着手を遅らせます」などといった会話を行い、チームとマネージャー(顧客)間で合意形成を図ります。その際に共通の認識を持てるよう見える化した資料を使用して報告を行います。

チーム作業ではメンバーと協力しながらプロジェクトの状況を把握し、タイミングごとに現実的な最適解を割り当てる必要があります。また、マネージャーや顧客といった、多くのステークホルダーと折り合いをつけながら双方の意見をまとめながらプロジェクトを進める必要があります。
チームの状況を出来る限り透明にし共有することで、マネージャーや時に顧客が強力な協力者になっていくはずです。


メトリクスとマネジメントレポート

ただ単にタスクを消化させるのでなく、チームが今どのような状態であるかを客観的に確認する必要があります。日常的に仕掛りやリードタイムをトラッキングし、チームが効率的であるか、または改善の余地があるかどうかを判断します。

すべてのタスクをRedmineで管理しているため、そこからログを出力し簡単な表計算で状態を可視化します。

f:id:whippet_818:20170210200056j:plain:w650
発生したタスクと完了したタスクを積み上げたグラフです。8月初旬から完了タスクの割合が増加していることがわかると思います。実は8/5からチームに対して仕掛り制限を明確に実施したことで、どんどんタスクが完了するようになりました。


f:id:whippet_818:20170210200108j:plain:w650
こちらは仕掛り~完了までの平均日数を表したグラフです。これも積み上げのグラフと同時期のもので、仕掛り制限を開始した8月初旬から明らかに日数が短縮できているのがわかるかと思います。
あとタイトルにはリードタイムと記載していますが、正確には、これはプロセスタイムが正しいです。顧客にとっての本当の価値はリードタイムの短縮にあり、保守運用チームにとっても「リードタイムをいかに短縮させるか」にすべての価値を置いています。


ふりかえりの実施

定期的に立ち止まり、メンバーで今の状況を振り返ることで常に改善を図ります。メンバー全員から良かったことや悪かったこと、そして改善したいことをヒアリングし、チームへフィードバックします。その際に客観的に状況を確認するために上記のレポートを見せ、問題があった部分などを掘り下げます。


まとめ

いろいろとチャレンジしていますが、まだまだ改善できることや中途半端になっているところもあったりします。またチームの特性によっても結果にバラつきでると思うので、実態にあわせた様々なパターンをこれからも考える必要がありますね。

LINE Messaging APIを使用したLINE Botの作り方

LINEからMessaging APIが公開されたので、それを利用したLINE BOTを作ってみました。
「ハロー」と送信すると「こんにちは」と返ってくるBotです。とりあえずはじめの一歩ということでシンプルに。
f:id:whippet_818:20170206210029j:plain:w350


f:id:whippet_818:20170206235109p:plain

使ったもの

  • LINE ビジネスアカウント
  • LINE Messaging API
  • Heroku
  • node.js

LINE BOT をつくるにはHTTPS でアクセスできる(SSL が利用できる)サーバーを用意する必要があります。自分で構築するかHerokuを使用するかですが、今回はとりあえず動かしたいだけなのでHeroku を利用しました。
※今後、勉強のためにもAWSのEC2を使用してみる予定です。


作成手順

LINE ビジネスアカウントの登録

https://business.line.me/ja/services/botにアクセスし、自分のLINEのIDでログインします。
ログイン後、画面右下の[Developer Trialを始める]をクリックし、必要な情報を入力します。 f:id:whippet_818:20170206212612j:plain:w600


LINE@ MANAGERの設定

LINE ビジネスアカウントの登録後、LINE@ MANAGER の画面へ遷移すると思うので、
左側のメニューから、アカウント設定 > BOT設定 > 「APIを利用する」ボタンをクリック します。

f:id:whippet_818:20170206213025j:plain:w600

リクエスト設定で Webhook送信を「利用する」にチェックを入れます。


LINE Developersでの設定

先ほどの BOT設定 のページで
ステータス > LINE Developersで設定する のリンクより、LINE Developersのページへ移動します。 そこで表示される値はこの後使用するので、メモしておきましょう。またQRコードから自分が持っているLINEアカウントに友達追加しておきましょう。

f:id:whippet_818:20170206225415j:plain:w600


Webhook URL の設定

LINE Botのイベントをリアルタイムで通知するための仕組みとして、Webhookの設定を行います。

f:id:whippet_818:20170206225427j:plain:w600

[EDIT]にてWebhook URLを設定します。アプリ名は自分で決めたものを入力して下さい。
https://アプリ名.herokuapp.com/webhook


Herokuの設定

今回はHerokuを利用したので、以下にてHeroku上にアプリケーションを作成します。

$ heroku apps:create アプリ名

この時作成されたURLが http://アプリ名.herokuapp.com/という形でWebhook URL と同じURLであることを確認します。
これでLINE側のイベントをサーバ側(Heroku)で受け取ることが可能になります。 あとはリモートのGitリポジトリ等の設定します。Herokuについては、情報はいっぱいあると思うので、ここでは詳細を省きます。


Bot本体のプログラム作成

いろいろ公開されている物を参考にほぼパクらせてもらいました。。 作ったらHerokuへデプロイします。

const LINE_CHANNEL_ACCESS_TOKEN = 'Channl Access Token';

var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
var app = express();

app.post('/webhook', function(req, res, next){
    res.status(200).end();
    for (var event of req.body.events){
        if (event.type == 'message' && event.message.text == 'ハロー'){
            var headers = {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN
            }
            var body = {
                replyToken: event.replyToken,
                messages: [{
                    type: 'text',
                    text: 'こんにちは'
                }]
            }
            var url = 'https://api.line.me/v2/bot/message/reply';
            request({
                url: url,
                method: 'POST',
                headers: headers,
                body: body,
                json: true
            });
        }
    }
});

body-parser モジュールを使用してbody部からデータを取得します。取得したデータを判定して、条件に一致すれば、request モジュールでメッセージを返信するという単純な仕組みです。データの送受信はJSON形式で行っています。

これをHeroku にデプロイ後、「ハロー」と入力して、会話できることを確認してみましょう。


まとめ

プログラムについてはあまり触れませんでしたが、LINE API Referenceには、丁寧なAPIの説明が記載されているので、参照してみて下さい。
この基本的な「会話」さえできれば、あとは形態素解析自然言語処理などを利用して、いろんなパターンの会話ができそうです。
ネタを考えつつ、今後何か作ってみたいと思います。

CentOSにJenkinsをインストール

世間の波から完全に取り残された感がありますが、CI/CDツールとして有名なJenkinsをCentOSにインストールしてみました。

環境

CentOS 6.7
・Jenkins 2.42

Javaのインストール

JenkinsはJava製らしいので、まずはJavaをインストールする必要があります。

$ sudo yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel

Jenkinsのインストール

リポジトリを追加します。

$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
$ sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key

リポジトリを追加したあとはyum installでインストールします。

$ sudo yum install jenkins

Jenkinsの起動

$ sudo /etc/init.d/jenkins start
Starting Jenkins                                           [  OK  ]

自動起動できるようにします。

$ sudo chkconfig jenkins on

Jenkinsが起動できれば、[http://[IPアドレス]:8080]でブラウザからアクセスしましょう。
※Jenkinsでは8080ポートをデフォルトで使用するようです

f:id:whippet_818:20170126205107j:plain:w500

初回起動時は上記画面が起動します。赤字で記載されているパスから管理者用パスワードを取得し、入力しましょう。

$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword

パスワードを入力し、[Continue]ボタンを押すとプラグインのインストール画面が表示されます。
左の[Install suggested plugins]をクリックするとおすすめのがプラグインがインストールされます。インストールしたプラグインが決まっている場合は、右側のボタンをクリックし、それ以外の方はとりあえず、左側をクリックすれば良いでしょう。 f:id:whippet_818:20170126210309j:plain:w500

プラグインのインストールが完了すると、ユーザ登録画面が表示されるので画面に従って入力します。
f:id:whippet_818:20170126210816j:plain:w500

これで初期の設定は完了。[Start using Jenkins]をクリック。 f:id:whippet_818:20170126210924j:plain:w500

ようやくスタートラインです。 f:id:whippet_818:20170126211113j:plain:w500

これからJenkinsおじさんとのお付き合いをはじめたいと思います。

Linux環境(CentOS)をWindows上につくる

1台WindowsPCがあったので、そこにRedmineみたいなWebアプリが稼働する環境を作りたくてLinux環境(CentOS)を構築しました。
今回はその時の手順メモとして、以下の通りWindows上に仮想環境としてLinux環境を構築させたいと思います。

環境

Windows 8.1 (ホストOS)  ※Windows7でも確認済みです
CentOS 6.7 (ゲストOS)
vagrant 1.8.4
VirtualBox 5.0.14

VirtualBoxのインストール

まず仮想環境です。以下からVirtualBoxを落としてきてインストールしましょう。
https://www.virtualbox.org/wiki/Downloads

Vagrantのインストール

次はVagrantです。上記のVirtualBoxなどの仮想マシンの立ち上げや操作を行います。
https://www.vagrantup.com/downloads.html

Vagrant Boxファイルのダウンロード

※注意: 今回は後述の通りChef社のリポジトリからBoxファイルをダウンロードするのでこの手順は不要です。
Boxファイルとは仮想マシンのイメージファイルのことです。
Boxファイルは自分で作ることもできますが、この例では以下の既に用意されているファイルを使用します。
http://www.vagrantbox.es/

以下のコマンドでBoxファイルを追加できます。
vagrant box add ボックス名 URL

URLのところで取得するパスを指定します。
コマンドプロンプトから以下を実行すると、Boxファイルが追加されます。

vagrant box add centos-6.7 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box

Vagrantfileの作成

C:\Users\user> mkdir mycentos
C:\Users\user> cd mycentos
C:\Users\user\mycentos> vagrant init bento/centos-6.7

initコマンドでVagrantfileを作成されます。 bentoでChef社のhttps://atlas.hashicorp.com/bento/ というリポジトリからBoxファイルを指定しています。
また、今回はmycentosというディレクトリを作成し、そこにVagrantfileを作成しました。
ここが作業ディレクトリになるので、今後vagrantコマンドを実行する際はmycentosに移動する必要があります。

Vagrantファイルの編集

initコマンドを実行したディレクトリに移動し、そこにあるVagrantfileを編集します。

IPの設定(ホストオンリーネットワーク)

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
   config.vm.network "private_network", ip: "192.168.33.10"

“config.vm ~” 行のコメントを外します。
ホストとゲスト間のみ通信可能なネットワークになります。今回の例では、ゲスト側にIPアドレス:192.168.33.10を割り当てています。
これでホスト側からこのIPを使用したPingがゲスト側に届くようになっているハズです。

ポートフォワードの設定
会社の環境の制約上、あれこれ面倒なので外部からのアクセスはポートフォワードするようにしました。

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
   config.vm.network "forwarded_port", guest: 80, host: 8080

外部ネットワークからホストIP+ポート番号(8080)でアクセスすると、ホスト側はそのアクセスをゲストに転送します。 それによって、外部ネットワークから実質的なゲストOSへのアクセスを可能とします。

※詳細は以下を参照してください。
vagrantのネットワークについて - Qiita

仮想マシンの起動

C:\Users\user\mycentos> vagrant up

作業ディレクトリに移動後、vagrant upCentOSを起動します。

仮想マシンへのログイン

1.コマンドプロンプトからそのままログインする方法

vagrant sshでログイン可能ですが、Windowsの場合には通常、SSHクライアントがインストールされていないので以下のようなメッセージが出力され、接続できません。

C:\Users\user>mycentos> vagrant ssh
`ssh` executable not found in any directories in the %PATH% variable. Is an SSH client installed?

2.ターミナルからログインする方法

Windowsの場合はTeraTermPuTTY等のSSHクライアントをインストールしましょう。

PuTTYのインストール

今回はPuTTYよりアクセスすることにしましたので、以下からPuTTYごった煮版をダウンロードします。
http://yebisuya.dip.jp/Software/PuTTY/

PuTTYからの接続

f:id:whippet_818:20170116223226j:plain:w450

  • ホスト名:192.168.33.10
  • ポート:22
  • 接続タイプ:SSH

f:id:whippet_818:20170116223228j:plain:w450

上記にて仮想マシンへのログインが可能になります。

仮想マシンのシャットダウン

C:\Users\user\mycentos> vagrant halt

一旦、ここままで仮想マシンの設定~CentOSの起動・シャットダウンまで出来たことになります。