TECH EXPRT 9日目

TECH::EXPERT

土日のTECHは開始時間が9時じゃないので困る。
昨日もそうだったけど、ついつい9時台に行ってしまう。
NEETに曜日の概念はないから毎日がSunday!

今日は11時直前に行ったけど、結構タイミングがシビア。
朝礼が11時からなので10時半〜11時の間に教室に入らないといけない。
朝起きてから時間があるので、何かに集中していると過ぎちゃう可能性大。

今日はカリキュラムを外れて勝手に個人サイトの作成。
カリキュラムを進めてもいいんだけど、せっかくやったことを忘れないうちにoutputしないと。
ありとあらゆるところでつまづくので、エラー問題といてるようなもん。

やったことメモ

同一文字列の置換

shift + command + L
同一文字列の全選択

command + D
同一文字列の複数選択。押すたびに1つずつ増える。

mechanizeのsearchメソッド

返り値は配列。
get_attributeメソッドを使った際に、undefined method `get_attribute’ for #<Nokogiri::XML::NodeSet が出るようならsearchではなくat()を使う、もしくはeachでバラしてあげないと上手くいかない。

エラーした状況
tableからXpathで取得した情報で .inner_textは動いたのに.get_attributeや[:href]とした場合にエラーが出た。1つしか値がなくても配列なのでpage.search().get_attribute()とせずに、page.search()[0].get_attributeとすれば通った。
.inner_textと.get_attributeでの挙動が違う理由は知らない。

ir = Ir.where(name: page.search("//*[@id='IR1600']/tbody/tr[#{num}]/td[2]/a").inner_text).first_or_initialize
ir.com_url  = page.search("//*[@id='IR1600']/tbody/tr[#{num}]/td[2]/a")[0].get_attribute('href')
ir.ir_title = page.search("//*[@id='IR1600']/tbody/tr[#{num}]/td[4]/a")[0].inner_text
ir.ir_url   = page.search("//*[@id='IR1600']/tbody/tr[#{num}]/td[4]/a[1]")[0][:href]
ir.ir_date  = page.search("//*[@id='IR1600']/tbody/tr[#{num}]/th")[0].inner_text
ちなみに
hogehoge = agent.get(url).search("hogehoge")
とした後に
fugafuga = hogehoge.search("fugafuga")
すると、
fugafuga = agent.get(url).search("fugafuga")
とした場合と同じ結果が返った。

limit.orderとorder.limitで結果が同じになるケースと同じ。
おそらくRailsの内部処理で悪さしている。(2文に分けてるつもりでも合算したSQL文が発行されてる?)

Xpath

XML文章中の要素、属性値などを指定するための言語。
searchメソッド内でそのまま使える。
同一属性の指定が非常に楽。特にテーブルで。
htmlの検証画面から指定した要素を右クリックでコピーする際に選べる。

page.search("//*[@id='IR1600']/tbody/tr").count

仕様については勉強課題。

.each_with_index と .each.with_index()

.each_with_index do |hogehoge, index|

indexの番号は0から始まる。

.each.with_index(数字) do|hogehoge, index|

indexの番号は指定した数字から始まる。

.even? と .odd?

変数.even? とすると true/falseが返る。
3.even? => false

.nil?

nilかどうかを判定してくれる。
nil なら true
nil でなければ false

データベース上がNULLでもRubyでは.nil? => Trueとなる。

AddColumnToTables カラム名:型

rails g migration AddIrtimeToIrs ir_time:string
rake db:migrate

Rubyの文字連結

“aaa” + “bbb”
&じゃない。

RubyにおけるTime

似たようなクラスにDateクラスやTimeWithZoneクラスがある。
DBに型をTIMEで作成し、例として15:00:00を格納した場合にputsすると
=> Sat, 01 Jan 2000 15:00:00 UTC +00:00
rubyではTIMEのみで扱えない。強制的に日付がつく。

空白を削除

.stripメソッドを持ちる。文字間の空白は消さず、両サイドの空白を消す。
.lstrip  左側だけを消す。
.rstrip 右側だけを消す。

first_or_initializeとその類似

個人的にはfind_or_create_byの方が検索と作成保存まで1メソッドで済むので良さそう。

.first_or_initialize()

普通whereと一緒に使う。

user = User.where(name: "hogehoge").first_or_initialize(name: "fugafufa")

.find_or_initialize_by()

引数で検索してなければnew()で作ってくれる。

user = User.find_or_initialize_by(name: "fugafufa")
if user.new_record?
  user.save
end

.first_or_create()

.first_or_initializeとほぼ同じ。whereと併用する。
違いはcreate()かnew()かの違い。
生成したインスタンスに追加情報を加えるときはinitializeで、加えないときはcreateの方がsaveしないでいいので楽。

user = User.where(name: "hogehoge").first_or_create(name: "hogehoge")

.find_or_create_by()

.find_or_initialize_by()とほぼ同じ。
違いはcreate()かnew()かの違い。
createなのでsaveしなくていい。

ファイルのかき分けの問題

カリキュラムでScraping用のscraping.rbはデータテーブルを扱うモデルとは別に作った。
テーブルに関するメソッドはModelファイルに書くべきでは?
=> 1度しか使わないようなメソッド等は別ファイルに書き分けるのはよくある。
モデルファイルには別ファイルのメソッドを呼び出す記述だけにとどめて可読性をあげた方がよい。
=> カリキュラムではモデルフォルダにscraping.rbを作ったことが腑に落ちない。というかスッキリしない。ファイルが増えると管理しにくくなるはず。
=> モジュールで管理するのが普通なのかも?

Rubyのモジュール

モジュールとクラスはよく似ている。クラスはデータと処理を、モジュールは処理を担当する。
モジュールを用いることで、モジュール内に定義したメソッドをインスタンスメソッドとして取り込んだり(include)、クラスメソッドとして取り込んだり(extend)できる。

名前空間の提供というもっと重要そうな点については理解が追いつかないので割愛。

new_record?

モデル.new_record?と言う感じで使う。
返り値はtrue/false

mechanizeの使い方

Mechanizeのクラスインスタンスを作ってページを取得。
ページから必要な情報を抜粋。
格納したいテーブルクラスのインスタンスを作成。
whereでIDが既にあるかを調べて、first_or_initializeでIDがない場合に新規に作成する。
テーブルのインスタンスに代入(既知IDの場合は既知のデータを上書き)する。
最後にきちんと保存して終了。

agent = Mechanize.new
page = agent.get(URL)
id   = page.search(クラスや属性等の要素指定)
name = page.search(クラスや属性等の要素指定) ...

table = Table.where(id: id).first_or_initialize
table.name = name
table.save

メインページから個別ページに移って、それから情報を取得するケースではメインページ側でLinks=[]などの配列に一旦URLを格納し、eachで分解しながら上記のメソッドを呼ぶ。

コメント