20190203のRubyに関する記事は8件です。

Google colab で RubyからGPU を使って行列計算する

Google Colab に Cumo/NArray をインストールすることで、Google Colab上でRubyでGPUを用いた高速計算ができることがわかったのでメモを残します。

Google Colaboratory とは

Google Colaboratory はクラウドで実行できる無料のJupyter環境です。カスタムでインストールすることでJupyterカーネルがあるさまざまな言語を動かすことができると思われる。少なくともRubyは動く。

Cumo とは

PythonだとCupyに相当するものです。RubyにはNumo/NArrayという、Numpyとほぼ同等の機能をもつ数値計算ライブラリがあります。CumoはこのNArray互換のインターフェースをCUDAで動かせるライブラリです。

Google Colab のランタイムのGPUを有効にする

一番最初にGPUを有効にします。あとからGPUを有効にするとランタイムの環境がリセットされてしまうので、注意してください。GPUが有効でないランタイムにいくらRubyやライブラリを正常にインストールしても、GPU有効のランタイムでは動きませんし、その逆もしかりです。google colabはランタイム再起動や、時間切れで再起動した際に知らずに設定が切り替わってることがあり、ハマりやすいです。動かなくなったらまずこれを疑ってもいいかも知れません。

Google ColabでRubyを動かす

Google Colab で Rubyを動かしてみた を参考にGoogle ColabでRubyを動かせるようにします。

Cumo/NArray のインストール

Google Colab は2019年1月の時点で、CUDA9.2を使用しており、Cumo/NArray の公式READMEの指示通りでインストールされます。ただし、環境変数は1行ごとにクリアされているっぽいので、マジックコマンドは長々と続けて書く必要があるようです。もっといいやり方があったら教えてください。また、IRuby上ではマジックコマンドは動きませんのでRubyとは別にPythonノートブックを立ち上げて下記のコマンドは実行しています。コマンドが実行されたら、IRubyのランタイムを起動し直すとcumoがrequrieできるようになっているはずです。

(cumoのインストールには合計10分ぐらい時間がかかります)

!export CUDA_PATH="/usr/local/cuda" && export CPATH="$CUDA_PATH/include:$CPATH" && export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH" && export PATH="$CUDA_PATH/bin:$PATH" && export LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LIBRARY_PATH" && gem install cumo

上記でインストールされますが、ちゃんとCumoの機能が動くか確認したい場合、コンパイルに失敗してエラーが見たい場合は、下記のような感じにしてメッセージを表示させながらのコンパイルや、テストを実行することもできるようです。

!gem install bundler
!git clone https://github.com/sonots/cumo
!export CUDA_PATH="/usr/local/cuda" && export CPATH="$CUDA_PATH/include:$CPATH" && export LD_LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LD_LIBRARY_PATH" && export PATH="$CUDA_PATH/bin:$PATH" && export LIBRARY_PATH="$CUDA_PATH/lib64:$CUDA_PATH/lib:$LIBRARY_PATH" && cd cumo && rake compile && rake test

補足

これでどこからでも無料でRubyからGPUで使うことができて個人的には満足です。ただし手元のコンピュータでは実行できたスクリプトもGoogle Colab上ではエラーが出る場合もあり、もう少し使ってみないと色々わからない面もあります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

面白いセルの合計

1行ごとに、前の行の面白いセル一覧を取得してから、自分の上にあるセルの中で面白いものを合計。
セルの右端が欠けるので余裕を見ないといけない。この意味で負けな可能性がある。
ちなみに、Rubyで11分、Crystalで2分、Crystal build --releaseで13秒でした。実行時間的にはyhpgですから許容範囲でしょう。

tyama_henae30.rb
#!/usr/bin/ruby
#http://nabetani.sakura.ne.jp/hena/orde30sumt/
#https://qiita.com/Nabetani/items/725e09cc5913a8569c04

while s=gets
    a_,b_=s.chomp.split("/")
    a=a_.split(",").map(&:to_i)
    b=b_.to_i
    width=a[-1]*b*4 ### fixme magic number 4
    #cell id, number
    storage=width.times.map{|i|[i/a[0],i/a[0]+1]}
    (1...a.size).each{|wi|
        interesting={}
        storage.uniq.tap{|z|
            interesting[z[0]]=1
            (1..z.size-2).each{|i|
                interesting[z[i]]=1 if z[i][1]!=z[i-1][1] || z[i][1]!=z[i+1][1]
            }
            interesting[z[-1]]=1
        }
        z0=[]
        w0=a[wi]
        0.step(width-1,w0).with_index{|k,ki|
            su=(k...[k+w0,width].min).select{|i|
                interesting.has_key?(storage[i])
            }.map{|i|
                storage[i]
            }.uniq.map(&:last).reduce(0,:+)%1000
            (k...[k+w0,width].min).each{z0<<[ki,su]}
        }
        storage=z0
    }
    p storage[(b-1)*a[-1]][1]
    STDOUT.flush
end
tyama_henae30.cr
#!/usr/bin/env crystal
#http://nabetani.sakura.ne.jp/hena/orde30sumt/
#https://qiita.com/Nabetani/items/725e09cc5913a8569c04

while s=gets
    a_,b_=s.chomp.split("/")
    a=a_.split(",").map(&.to_i)
    b=b_.to_i
    width=a[-1]*b*4 ### fixme magic number 4
    #cell id, number
    storage=width.times.map{|i|{i/a[0],i/a[0]+1}}.to_a
    (1...a.size).each{|wi|
        interesting=Hash(Tuple(Int32,Int32),Int32).new
        storage.uniq.tap{|z|
            interesting[z[0]]=1
            (1..z.size-2).each{|i|
                interesting[z[i]]=1 if z[i][1]!=z[i-1][1] || z[i][1]!=z[i+1][1]
            }
            interesting[z[-1]]=1
        }
        z0=Array(Tuple(Int32,Int32)).new
        w0=a[wi]
        0.step(to: width-1,by: w0).with_index{|k,ki|
            su=(k...[k+w0,width].min).select{|i|
                interesting.has_key?(storage[i])
            }.map{|i|
                storage[i]
            }.uniq.map(&.last).sum%1000
            (k...[k+w0,width].min).each{z0<<{ki,su}}
        }
        storage=z0
    }
    p storage[(b-1)*a[-1]][1]
    STDOUT.flush
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windowsでrubyをつかう。WEBrickが立ち上がるまで

WindosでRubyの開発を行いたいと思ったことがきっかけ
Ruby導入からRailsプロジェクトの作成、WEBrickが起動するまでのまとめ
Qiitaとか投稿したこがないけど、やってみようかな
アウトプットすることを覚える
見苦しいところが多々ありますが、ご指摘していただけるとありがだいです。m@@m

開発環境

  • Windows7(x64)
  • Ruby2.5.3p105 (2018-10-18 revision 65156)
  • Rails5.2.2

rubyインストール

Windows用のRubyがここで公開されてる。development kit がついている以下のものをダウンロードする

  • Ruby+Devkit 2.5.3-1 (x64)

インストーラを実行すればrubyが導入される
今回はC直下にインストールv@@v

Railsのインストール

コマンドプロンプトで以下を実行する

gem install rails

SQLite3の実行ファイルをダウンロード

ここから以下のものをダウンロードする

  • Source Code

    • sqlite-amalgamation-3260000.zip・・・C直下に展開
  • recompiled Binaries for Windows

    • sqlite-dll-win32-x86-3260000.zip・・・C:\Ruby25-x64\binにsqlite3.dllを投入
    • sqlite-tools-win32-x86-3260000.zip・・・C:\Ruby25-x64\binにsqlite3.exeを投入

SQLite3のライブラリをインストール

SQLite3のソースコードと実行ファイルのディレクトリを指定しコマンドプロンプトで以下を実行

gem install sqlite3 --platform=ruby -- --with-sqlite3-include=C:\sqlite-amalgamation-3260000 --with-sqlite3-lib=C:\Ruby25-x64\bin

※SQLite3のインストールでハマりました、以下のページを参照して脱出
 WindowsでRailsTutorialするときに気をつけること

Railsプロジェクトを作成

コマンドプロンプトでRailsのプロジェクトを作成したいフォルダを指定(今回はd:\app\rails)し以下を実行

rails new プロジェクト名

d:\app\railsにプロジェクト名のフォルダが作成されている
これだけでrailsのプロジェクトができちゃうのね

WEBrickを起動する

d:\app\rails\プロジェクト名 を指定しコマンドプロンプトで以下を実行

rails server WEBrick

http://localhost:3000/にアクセス
WEBrickが正常に起動していれば、地球の上に立っている人々が表示される

コマンドでWEBrickを指定せず、サーバを起動させるとPumaが起動した
バージョンによってデフォルトで起動するサーバが違うのか...?

参考

Ruby on Rails入門
ここで紹介されている内容を実行しました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オフラインリアルタイムどう書く E29 の実装例( ruby )

問題の名前 : 面白いセルの合計
問題 : http://nabetani.sakura.ne.jp/hena/orde30sumt/
実装リンク集 : https://qiita.com/Nabetani/items/725e09cc5913a8569c04

次回のイベントは 3/9

で。

ruby2.6
require "json"

class Cells
  def initialize(ws)
    @ws = ws.dup
    @cache={}
  end

  def at(y,x)
    key = (x<<32)+y
    @cache[key] ||= _at(y,x)
  end

  def interesting?(y,x)
    !( 0<y && at(y,x)==at(y,x-1) && at(y,x)==at(y,x+1) )
  end

  def _at(y,x)
    return x+1 if y<=0
    left0 = x*@ws[y]
    right0 = left0 + @ws[y]

    left = left0 / @ws[y-1]
    right = (right0-1) / @ws[y-1]

    (left..right).select{ |xx|
      interesting?(y-1,xx)
    }.sum{ |xx| 
      at(y-1,xx)
    } % 1000
  end
end

def solve( src )
  wss, ms = src.split("/")
  ws = wss.split(",").map(&:to_i)
  Cells.new(ws).at(ws.size-1, ms.to_i-1).to_s
end

if $0==__FILE__
  data = JSON.parse(DATA.read, symbolize_names: true)
  data[:test_data].map{ | number:, src:, expected: |
    actual = solve( src )
    okay = actual == expected
    puts [ number, (okay ? "ok" : "**NG**"), actual, expected ].join(" ")
    okay
  }.all?.yield_self{ |x| puts( x ? "okay" : "SOMETHING WRONG" ) }
end

__END__
{"event_id":"E30","event_url":"https://yhpg.doorkeeper.jp/events/84247","test_data":[
  {"number":0,"src":"4,6,1,5/3","expected":"14"},
  {"number":1,"src":"1/1","expected":"1"},
  {"number":2,"src":"6/1","expected":"1"},
  {"number":51,"src":"408,412,30,930,372,822,632,948,855,503,8,618,138,695,897,852/377","expected":"212"}
]}

毎度のことながらテストデータの大半は省略。

普通にメモ化つき再帰なんだけど、それ以外にいい作戦は思いついていない。

つい yield_self のままにしたけど、 ruby 2.6 なので itself にすべきだったね。

過去問に 上と左の合計 という類題がある。過去問のほうが難しそうな感じ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オフラインリアルタイムどう書く E30 の実装例( ruby )

問題の名前 : 面白いセルの合計
問題 : http://nabetani.sakura.ne.jp/hena/orde30sumt/
実装リンク集 : https://qiita.com/Nabetani/items/725e09cc5913a8569c04

次回のイベントは 3/9
see https://yhpg.doorkeeper.jp/events/86766

で。

ruby2.6
require "json"

class Cells
  def initialize(ws)
    @ws = ws.dup
    @cache={}
  end

  def at(y,x)
    key = (x<<32)+y
    @cache[key] ||= _at(y,x)
  end

  def interesting?(y,x)
    !( 0<y && at(y,x)==at(y,x-1) && at(y,x)==at(y,x+1) )
  end

  def _at(y,x)
    return x+1 if y<=0
    left0 = x*@ws[y]
    right0 = left0 + @ws[y]

    left = left0 / @ws[y-1]
    right = (right0-1) / @ws[y-1]

    (left..right).select{ |xx|
      interesting?(y-1,xx)
    }.sum{ |xx| 
      at(y-1,xx)
    } % 1000
  end
end

def solve( src )
  wss, ms = src.split("/")
  ws = wss.split(",").map(&:to_i)
  Cells.new(ws).at(ws.size-1, ms.to_i-1).to_s
end

if $0==__FILE__
  data = JSON.parse(DATA.read, symbolize_names: true)
  data[:test_data].map{ | number:, src:, expected: |
    actual = solve( src )
    okay = actual == expected
    puts [ number, (okay ? "ok" : "**NG**"), actual, expected ].join(" ")
    okay
  }.all?.yield_self{ |x| puts( x ? "okay" : "SOMETHING WRONG" ) }
end

__END__
{"event_id":"E30","event_url":"https://yhpg.doorkeeper.jp/events/84247","test_data":[
  {"number":0,"src":"4,6,1,5/3","expected":"14"},
  {"number":1,"src":"1/1","expected":"1"},
  {"number":2,"src":"6/1","expected":"1"},
  {"number":51,"src":"408,412,30,930,372,822,632,948,855,503,8,618,138,695,897,852/377","expected":"212"}
]}

毎度のことながらテストデータの大半は省略。

普通にメモ化つき再帰なんだけど、それ以外にいい作戦は思いついていない。

つい yield_self のままにしたけど、 ruby 2.6 なので itself then にすべきだったね。

過去問に 上と左の合計 という類題がある。過去問のほうが難しそうな感じ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

brew doctorでWarningが出たので解決方法まとめました。(for Mac)

環境

Macbook pro(2017年モデル)
macOS Mojave

状況

Homebrewを使って、rbenvをインストールしようと試みたところエラーが発生し、

terminal
$ brew doctor

で確認したところ、以下のようなWarningが出現したので、その解決方法を備忘録として残しておきます。

Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: Unbrewed dylibs were found in /usr/local/lib.
If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected dylibs:
  /usr/local/lib/libgvplugin_core.6.dylib
  /usr/local/lib/libgvplugin_dot_layout.6.dylib
  /usr/local/lib/libgvplugin_gd.6.dylib
  /usr/local/lib/libgvplugin_neato_layout.6.dylib
  /usr/local/lib/libgvplugin_quartz.6.dylib

Warning: Unbrewed header files were found in /usr/local/include.
If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected header files:
  /usr/local/include/arith.h
  /usr/local/include/binary_log_types.h
  /usr/local/include/cdt.h
  /usr/local/include/cgraph.h
  /usr/local/include/color.h
  /usr/local/include/decode.h
  /usr/local/include/demux.h
  /usr/local/include/encode.h
  /usr/local/include/errmsg.h
  /usr/local/include/fcfreetype.h
  /usr/local/include/fcprivate.h
  /usr/local/include/fontconfig.h
  /usr/local/include/freetype/config/ftconfig.h
  /usr/local/include/freetype/config/ftheader.h
  /usr/local/include/freetype/config/ftmodule.h
  /usr/local/include/freetype/config/ftoption.h
  /usr/local/include/freetype/config/ftstdlib.h
  /usr/local/include/freetype/freetype.h
  /usr/local/include/freetype/ftadvanc.h
  /usr/local/include/freetype/ftbbox.h
  /usr/local/include/freetype/ftbdf.h
  /usr/local/include/freetype/ftbitmap.h
  /usr/local/include/freetype/ftbzip2.h
  /usr/local/include/freetype/ftcache.h
  /usr/local/include/freetype/ftchapters.h
  /usr/local/include/freetype/ftcid.h
  /usr/local/include/freetype/ftdriver.h
  /usr/local/include/freetype/fterrdef.h
  /usr/local/include/freetype/fterrors.h
  /usr/local/include/freetype/ftfntfmt.h
  /usr/local/include/freetype/ftgasp.h
  /usr/local/include/freetype/ftglyph.h
  /usr/local/include/freetype/ftgxval.h
  /usr/local/include/freetype/ftgzip.h
  /usr/local/include/freetype/ftimage.h
  /usr/local/include/freetype/ftincrem.h
  /usr/local/include/freetype/ftlcdfil.h
  /usr/local/include/freetype/ftlist.h
  /usr/local/include/freetype/ftlzw.h
  /usr/local/include/freetype/ftmac.h
  /usr/local/include/freetype/ftmm.h
  /usr/local/include/freetype/ftmodapi.h
  /usr/local/include/freetype/ftmoderr.h
  /usr/local/include/freetype/ftotval.h
  /usr/local/include/freetype/ftoutln.h
  /usr/local/include/freetype/ftparams.h
  /usr/local/include/freetype/ftpfr.h
  /usr/local/include/freetype/ftrender.h
  /usr/local/include/freetype/ftsizes.h
  /usr/local/include/freetype/ftsnames.h
  /usr/local/include/freetype/ftstroke.h
  /usr/local/include/freetype/ftsynth.h
  /usr/local/include/freetype/ftsystem.h
  /usr/local/include/freetype/fttrigon.h
  /usr/local/include/freetype/fttypes.h
  /usr/local/include/freetype/ftwinfnt.h
  /usr/local/include/freetype/t1tables.h
  /usr/local/include/freetype/ttnameid.h
  /usr/local/include/freetype/tttables.h
  /usr/local/include/freetype/tttags.h
  /usr/local/include/ft2build.h
  /usr/local/include/geom.h
  /usr/local/include/graphviz_version.h
  /usr/local/include/gvc.h
  /usr/local/include/gvcext.h
  /usr/local/include/gvcjob.h
  /usr/local/include/gvcommon.h
  /usr/local/include/gvconfig.h
  /usr/local/include/gvplugin.h
  /usr/local/include/gvplugin_device.h
  /usr/local/include/gvplugin_layout.h
  /usr/local/include/gvplugin_loadimage.h
  /usr/local/include/gvplugin_render.h
  /usr/local/include/gvplugin_textlayout.h
  /usr/local/include/gvpr.h
  /usr/local/include/lt_dlloader.h
  /usr/local/include/lt_error.h
  /usr/local/include/lt_system.h
  /usr/local/include/mux.h
  /usr/local/include/mux_types.h
  /usr/local/include/my_command.h
  /usr/local/include/my_list.h
  /usr/local/include/mysql.h
  /usr/local/include/mysql/client_plugin.h
  /usr/local/include/mysql/plugin_auth_common.h
  /usr/local/include/mysql/udf_registration_types.h
  /usr/local/include/mysql_com.h
  /usr/local/include/mysql_time.h
  /usr/local/include/mysql_version.h
  /usr/local/include/mysqld_error.h
  /usr/local/include/mysqlx_ername.h
  /usr/local/include/mysqlx_error.h
  /usr/local/include/mysqlx_version.h
  /usr/local/include/pack.h
  /usr/local/include/pathgeom.h
  /usr/local/include/pathplan.h
  /usr/local/include/png.h
  /usr/local/include/pngconf.h
  /usr/local/include/textspan.h
  /usr/local/include/types.h
  /usr/local/include/usershape.h
  /usr/local/include/xdot.h

解決法

まずは上のWarningから。(下のWarningも解決方法同じでした!!)

Warning: Unbrewed dylibs were found in /usr/local/lib.
If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected dylibs:
  /usr/local/lib/libgvplugin_core.6.dylib
  /usr/local/lib/libgvplugin_dot_layout.6.dylib
  /usr/local/lib/libgvplugin_gd.6.dylib
  /usr/local/lib/libgvplugin_neato_layout.6.dylib
  /usr/local/lib/libgvplugin_quartz.6.dylib

①ターミナルに以下を入力します。(ファイルの作成)

terminal
$ vim ~/.del_files.sh

が並んだページが出現するので、1行目に#!/bin/bashを貼り付け、2行目以降にUnexpected dylibs:に記載されているファイルをコピペします。(以下、参考)

terminal
#!/bin/bash
  /usr/local/lib/libgvplugin_core.6.dylib
  /usr/local/lib/libgvplugin_dot_layout.6.dylib
  /usr/local/lib/libgvplugin_gd.6.dylib
  /usr/local/lib/libgvplugin_neato_layout.6.dylib
  /usr/local/lib/libgvplugin_quartz.6.dylib

③カーソルを一番上のファイル(2行目)の頭におき、ctrl + vで矩形選択のビジュアルモード(VISUAL BLOCK)に移行。カーソルで一番下のファイルの頭まで選択し、:を押します。

すると、下のラインに既に :'<,'>が入力された状態でコマンドラインが表示されるので、続けて s/^/rm -f /と入力します。(rm は、ファイルやディレクトリを削除してあげるコマンドで、 -f は、「削除しますか?」的なメッセージを都度出さないようにするオプションです。)

Enterを押して、全ての行(一番上以外)の先頭にs/^/rm -f /が追加されているのを確認したら、もう一度:を押します。一番下の行に入力欄が出てくるので、wq!と入力し、Enterを押します。

⑤通常の入力モードに戻るので、そこで

terminal
$ bash ~/.del_files.sh

を実行。これでファイルが削除されます。

⑥以上が完了したら、

terminal
$ brew doctor

を実行し、エラー文が消えていたら、成功です。

追記

⑦もし、以下のようなWarningが出現した場合

Warning: Broken symlinks were found. Remove them with `brew cleanup`:
  /usr/local/lib/libgvplugin_core.dylib
  /usr/local/lib/libgvplugin_dot_layout.dylib
  /usr/local/lib/libgvplugin_gd.dylib
  /usr/local/lib/libgvplugin_neato_layout.dylib
  /usr/local/lib/libgvplugin_quartz.dylib

エラー文に従って、

terminal
$ brew cleanup

してあげましょう。


こちらのWarningも上記と同じ方法で解決できます!!

Warning: Unbrewed header files were found in /usr/local/include.
If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.

Unexpected header files:
  /usr/local/include/arith.h
  /usr/local/include/binary_log_types.h
  /usr/local/include/cdt.h
  /usr/local/include/cgraph.h
  /usr/local/include/color.h
  /usr/local/include/decode.h
  /usr/local/include/demux.h
  /usr/local/include/encode.h
  /usr/local/include/errmsg.h
  /usr/local/include/fcfreetype.h
  /usr/local/include/fcprivate.h
  /usr/local/include/fontconfig.h
  /usr/local/include/freetype/config/ftconfig.h
  /usr/local/include/freetype/config/ftheader.h
  /usr/local/include/freetype/config/ftmodule.h
  /usr/local/include/freetype/config/ftoption.h
  /usr/local/include/freetype/config/ftstdlib.h
  /usr/local/include/freetype/freetype.h
  /usr/local/include/freetype/ftadvanc.h
  /usr/local/include/freetype/ftbbox.h
  /usr/local/include/freetype/ftbdf.h
  /usr/local/include/freetype/ftbitmap.h
  /usr/local/include/freetype/ftbzip2.h
  /usr/local/include/freetype/ftcache.h
  /usr/local/include/freetype/ftchapters.h
  /usr/local/include/freetype/ftcid.h
  /usr/local/include/freetype/ftdriver.h
  /usr/local/include/freetype/fterrdef.h
  /usr/local/include/freetype/fterrors.h
  /usr/local/include/freetype/ftfntfmt.h
  /usr/local/include/freetype/ftgasp.h
  /usr/local/include/freetype/ftglyph.h
  /usr/local/include/freetype/ftgxval.h
  /usr/local/include/freetype/ftgzip.h
  /usr/local/include/freetype/ftimage.h
  /usr/local/include/freetype/ftincrem.h
  /usr/local/include/freetype/ftlcdfil.h
  /usr/local/include/freetype/ftlist.h
  /usr/local/include/freetype/ftlzw.h
  /usr/local/include/freetype/ftmac.h
  /usr/local/include/freetype/ftmm.h
  /usr/local/include/freetype/ftmodapi.h
  /usr/local/include/freetype/ftmoderr.h
  /usr/local/include/freetype/ftotval.h
  /usr/local/include/freetype/ftoutln.h
  /usr/local/include/freetype/ftparams.h
  /usr/local/include/freetype/ftpfr.h
  /usr/local/include/freetype/ftrender.h
  /usr/local/include/freetype/ftsizes.h
  /usr/local/include/freetype/ftsnames.h
  /usr/local/include/freetype/ftstroke.h
  /usr/local/include/freetype/ftsynth.h
  /usr/local/include/freetype/ftsystem.h
  /usr/local/include/freetype/fttrigon.h
  /usr/local/include/freetype/fttypes.h
  /usr/local/include/freetype/ftwinfnt.h
  /usr/local/include/freetype/t1tables.h
  /usr/local/include/freetype/ttnameid.h
  /usr/local/include/freetype/tttables.h
  /usr/local/include/freetype/tttags.h
  /usr/local/include/ft2build.h
  /usr/local/include/geom.h
  /usr/local/include/graphviz_version.h
  /usr/local/include/gvc.h
  /usr/local/include/gvcext.h
  /usr/local/include/gvcjob.h
  /usr/local/include/gvcommon.h
  /usr/local/include/gvconfig.h
  /usr/local/include/gvplugin.h
  /usr/local/include/gvplugin_device.h
  /usr/local/include/gvplugin_layout.h
  /usr/local/include/gvplugin_loadimage.h
  /usr/local/include/gvplugin_render.h
  /usr/local/include/gvplugin_textlayout.h
  /usr/local/include/gvpr.h
  /usr/local/include/lt_dlloader.h
  /usr/local/include/lt_error.h
  /usr/local/include/lt_system.h
  /usr/local/include/mux.h
  /usr/local/include/mux_types.h
  /usr/local/include/my_command.h
  /usr/local/include/my_list.h
  /usr/local/include/mysql.h
  /usr/local/include/mysql/client_plugin.h
  /usr/local/include/mysql/plugin_auth_common.h
  /usr/local/include/mysql/udf_registration_types.h
  /usr/local/include/mysql_com.h
  /usr/local/include/mysql_time.h
  /usr/local/include/mysql_version.h
  /usr/local/include/mysqld_error.h
  /usr/local/include/mysqlx_ername.h
  /usr/local/include/mysqlx_error.h
  /usr/local/include/mysqlx_version.h
  /usr/local/include/pack.h
  /usr/local/include/pathgeom.h
  /usr/local/include/pathplan.h
  /usr/local/include/png.h
  /usr/local/include/pngconf.h
  /usr/local/include/textspan.h
  /usr/local/include/types.h
  /usr/local/include/usershape.h
  /usr/local/include/xdot.h

参考URL

brew doctorでのWarningを解決してみた(for Mac)
HowTo: Save File in Vi / Vim Text Editor
server - E45 readonly option is set (add ! to override) - Ask Ubuntu

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google検索結果をスクレイピングするときは、User-Agentの設定に気をつけよう

自分のブログの転載記事です。

TL;DR

User-Agentの設定に気をつけないと、Google検索結果のスクレイピングがうまくいかないかもしれません、というお話です。
ついでに、Google検索結果のスクレイピングはグレーっぽいというお話です。

やってみて失敗したこと

Googleの検索結果をスクレイピングするなら、あなたはどのようなコードを書くでしょうか。
私はこんな感じで書きます。

search_url = "https://www.google.co.jp/search?hl=jp&gl=JP&"
query = URI.encode_www_form(q: "日本M&Aセンター")
search_url += query

charset = nil

html = open(search_url) do |f|
  charset = f.charset
  f.read
end

doc = Nokogiri::HTML.parse(html, nil, charset)

人によって細かい違いはあるかもしれませんが、上記はごく普通のNokogiriでのスクレイピングだと思います。
ここから、最初の検索結果に現れたサイトのURLを取得したいとします。
スクリーンショット 2019-02-03 11.57.29.png
xpathでの指定が簡単そうでしたので、要素のxpathを指定し、そこからリンクテキストをスクレイピングするとします。

link = doc.xpath('//div[@class="r"]/a')
link[0].attribute('href').text

そうすると、変数linkには何が入っているでしょうか。

puts link
=> nil

何も入っていません。何故でしょう。

結論から言うと、xpathの指定が間違っているからです。
そしてなぜxpathが間違っているのかというと、User-Agentを指定していないからなのです。

User-Agentの検証

実際に検証してみましょう。
まずはUser-Agentの設定を何も変えずに検索してみます。
(User-Agent設定はChromeのdeveloper toolから見られます)
スクリーンショット 2019-02-03 12.12.22.png
この場合、自分の環境ではUser-Agentは以下のような設定になっています。

"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36"

では次にUser-Agentに意味のない文字を設定して、同じ画面を表示してみます。
スクリーンショット 2019-02-03 12.12.50.png
検索結果の画面が変わったことがわかるでしょうか。
表示されている内容が大きく変わったわけではありませんが、レイアウトやスタイルが変更されています。

これがUser-Agentを設定せずにGoogle検索をおこなった場合のレスポンス、というわけです。
この状態でもスクレイピングは可能なのですが、要素のxpathが変わってしまうため、Chromeを見ながらスクレイピングするということがやりにくく、非常に開発がしづらくなってしまいます。

対策

対策としては、User-Agentを明示的に設定してあげればよいです。
というわけで修正したソースコードがこちら。

user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36"

search_url = "https://www.google.co.jp/search?hl=jp&gl=JP&"
query = URI.encode_www_form(q: "日本M&Aセンター")
search_url += query

charset = nil

html = open(search_url, 'User-Agent' => user_agent) do |f|
  charset = f.charset
  f.read
end

doc = Nokogiri::HTML.parse(html, nil, charset)

link = doc.xpath('//div[@class="r"]/a')
link[0].attribute('href').text

これで、自分の使用しているChrome環境と同じレスポンスを取得できるようになります。
スクレイピングのマナーとしてもUser-Agentは設定するべきですので、良いことづくめですね。

余談

ちなみにGoogleの検索結果をスクレイピングするにあたっていろいろ調べたのですが、Googleは検索結果のスクレイピングを認めていないようですね。
robots.txtにも、以下の記載がありますので。

User-agent: *
Disallow: /search

とはいっても、実際はある程度黙認されているようです。
何にせよ、スクレイピングは自己責任で行うべきですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 文字列とシンボルの違い

今日は、文字列とシンボルの違いについて説明します。

結論から言うと文字列(クラスがStirng)は
同じ文字列の指定であっても
Rubyのインタプリタは毎回新たに
Stringのインスタンスを作成します。
(=毎回新しいオブジェクトを作成)

一方、同じシンボルを指定した際は、
一度できたSymbolのインスタンス
を参照するのでオブジェクトは一つです。
(=オブジェクトは新たに作成されない)

ん、訳わかんないですね:joy:
下の方で絵で解説しています。

まずは実際に文字列とシンボルを指定して 
オブジェクトのidを確認してみましょう。

diff_Str_Sym.rb
st1 = "moji"
st2 = "moji"

sy1 = :moji
sy2 = :moji

p st1.object_id
p st2.object_id
p sy1.object_id
p sy2.object_id

これを実行した結果を -> で書いてます。

diff_Str_Sym.rb
p st1.object_id #->70123700938120
p st2.object_id #->70123700938060
p sy1.object_id #->938588
p sy2.object_id #->938588

Stringではオブジェクトidが異なるのに対して
Symbolはオブジェクトidが同じ。
Stirng(文字列)は同じ文字の並びを指定したときでも
オブジェクトが新しく作られていることがわかります。
Symbol(シンボル)は同じオブジェクトですね。

絵で描いてみます。

文字列とシンボルの違い1.jpg

じゃあ、文字列はコピーとか連結したらどうなるの???
と思う方、いると思います。
例えば以下のようなコピーの場合、オブジェクトは一つしか生まれません。

copy.rb
st1 = "moji"
st2 = st1

これはst2にはオブジェクトを参照する矢印がコピーされていると考えるとわかりやすいと思います。

文字列とシンボルの違い2.jpg

文字列の連結の場合は以下のようになります。

add.rb
st1 += "add"

文字列とシンボルの違い3.jpg

"add"のオブジェクトが作られて、"moji"のオブジェクトと連結して
新しい"mojiadd"オブジェクトが作られる。
st1は"mojiadd"オブジェクトを参照する。
st2には影響無し。

他にも不思議に思ったことがあった場合は
この概念と.object_idを使ってに確かめてみましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む