質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.61%

  • Ruby

    7366questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RubyによるWebスクレイピングでの404エラー対策

受付中

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 1,846

mycee0001

score 10

RubyにてWebスクレイピングをおこないたいと思っております。
対象のURLは末尾の7桁の数字を変更して、変更した先のh2要素を抜くというものです。
初回のアクセス時にbasic認証があり、mechanizeで対策をしたのですが(以下コード)
現在問題点は2つございます。
①unexpected end of input errorが起きているが場所がわからない
②mechanizeの書き方が間違っていないか?
ご教示いただけると幸いです

require './Loader.rb'
require 'open-uri'
require 'openssl'
require 'nokogiri'
require 'mechanize'

test = Loader.new
test.loading
agent = WWW::Mechanize.new{|a| a.log=Logger.new('access.log')}
agent.verify_mode = OpenSSL::SSL::VERIFY_NONE
agent.user_agent = 'Windows IE11'
agent.add_auth("URLパス", 'ユーザー名', 'パスワード')
url = "対象URL"
  
  $arry.each do $num
      charset = nil
      begin
          html = open(agent.get(url)) do |f|
          charset = f.charset 
          f.read 

          rescue Timeout::Error
            puts "caught Timeout::Error!"
        retry # タイムアウト用
          rescue WWW::Mechanize::ResponseCodeError => e
        case e.response_code
        when "404"
              puts "caught Net::HTTPNotFound!"
          next # 404用
        when "502"
              puts "caught Net::HTTPBadGateway!"
          retry # 502用
        else
              puts "caught Excepcion!" + e.response_code
          retry       
      end



    doc = Nokogiri::HTML.parse(html, nil, charset)
    doc.xpath('//div[@id="main"]/h2').each do |node|
    p node
    end 
  end


  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+3

* エラーに関して: ruby -c file_name.rb で構文チェックを走らせるとわかりやすいです
$arry.each に処理を詰め込みすぎています 処理のグループ分け(名前付け)をして、関数に切り出しましょう
* グローバル変数の利用は避け、そういったシーンではクラスを定義してその中でインスタンス変数を利用するとよいかもしれません
* if と同様に、do...end もネストを深くしないよう関数に切り出すなどで浅く保つ

まずは見易いコードに整え、そのあとで構文エラーに対応しても遅くはないと思います


# 生の処理を関数に換える

例外が発生する処理も、yield を利用すると関数として切り出すことができるとおもいます

例:
def calc
  yield
rescue ZeroDivisionError => e
  puts e.message
end

puts calc { 1 + 1 } # => 2
puts calc { 0 / 0 } # => divided by 0

Mechanize の初期化:
def mechanize_agent
  agent = WWW::Mechanize.new { |a| a.log = Logger.new('access.log') }
  agent.verify_mode = OpenSSL::SSL::VERIFY_NONE
  agent.user_agent = 'Windows IE11'
  agent.add_auth('URLパス', 'ユーザー名', 'パスワード')
  agent
end

このように関数として切り出していくと、構文エラーの発生しにくいコードにしていくことができるのではないでしょうか

何か参考になれば幸いです

# Links

bbatsov/rubocop - GitHub
Ruby - Rubocopを使ってコーディングルールへの準拠チェックを自動化 - Qiita

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/09 13:19

    @gouf様
    的確なアドバイスありがとうございます。まずは頭の整理ふくめて、関数単位の切出しやってみます

    キャンセル

0

1. インデント(段下げ)がでたらめになっているのは自覚されていますか?
インデントを正しくすれば、endの抜けに気づけると思います。二カ所ですね。
インデントを全くしないのでもなく、でたらめにするというのは、何故なんですかね?

2. $arry や $num は、Loader.rb で定義されているのだと思いますが、グローバル変数経由以外の方法がおそらくとれると思います。

3. $num が意味不明に置かれています。

4. リトライしても又同じエラーになりそうな気がしますけど。

5. each ループ内にこれくらいの機能の処理を書くのは良いと思いますが、エラー処理が混ざっているのが見にくいですね。aget.getに一皮かぶせたメソッドを作って、その中でエラー処理をするのがいいように思います。

6. require './Loader.rb' は、「実行時のカレントディレクトリにあるLoader.rb」を読み込みますが、意図と合っていますか?「このスクリプトと同じディレクトリにあるLoader.rb」の場合は、require_relative 'Loader.rb' です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

他の方が指摘されているようにbegin内の
html = open(agent.get(url)) do |f|
に対してendが無いように見受けられます。

私も初心者で
やりたいことに合致するかわかりませんが、
下記のようにしてみてはいかがでしょうか?

      begin
        page = agent.get(url)
      rescue Timeout::Error
        puts "caught Timeout::Error!"
        retry # タイムアウト用
      rescue Mechanize::ResponseCodeError => e
        case e.response_code
          when "404"
            puts "caught Net::HTTPNotFound!"
          when "502"
            puts "caught Net::HTTPBadGateway!"
            retry # 502用
          else
            puts "caught Excepcion!" + e.response_code
            retry
        end
      end

お役に立てれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Ruby

    7366questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。