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

@テク野路ジーロード

配信タグシェアリングシステムpickvyを開発、運営開始。最近は、Googleアナリティクスを研究中

Ruby On Railsで出た"redirection forbidden"について調べてみた

こんにちは。大阪は朝からじんわりと暑いのですが、小雨でジメジメです。
自宅にこもってパチパチしてるsunday150です。

今回、指定したウェブサイトをスクレイピングして、OGP(Open Graph Protocol)で
タイトルやアイキャッチ画像を取得するプログラムを書きました。

しかし、こんなエラーが出たので、調べてみました。

エラー文

redirection forbidden

具体的なエラー文は以下でした。

redirection forbidden: https://www.yahoo.com/gma/know-man-minnesota-police-killed-traffic-stop-151906909--abc-news-topstories.html -> http://gma.yahoo.com/know-man-minnesota-police-killed-traffic-stop-151906909--abc-news-topstories.html

これが意味するところ、最初よくわかりませんでした。リダイレクト先が悪いの?どうすれば!?と悩んだので、調べてみました。
今は重要な部分を赤字にしているので、なんとなく推測しやすいですけど。

原因追及

コードの中でエラー出力箇所はどこか? 以下でした。
これは私のプログラムの一部ですが、よくあるコードです。

  • html = open(url) do |f|

open-uri関数からエラーが出ていました。
さらに、open-uri関数のどこがエラーを吐いてるのか?
調査の中でこのサイト「[ヅ] Ruby で redirection forbidden というエラーが発生 (2014-05-08)」が見つかりまして、どうやら以下に記載されているようです。

中を探って見つけました!!
確かにあのエラー文 redirection forbidden が出ている。

unless OpenURI.redirectable?(uri, redirect)
          raise "redirection forbidden: #{uri} -> #{redirect}"
end

このエラーはredirectable?関数がfalseなら発動しているようだ。
この関数をさらに見てみる。

def OpenURI.redirectable?(uri1, uri2)
      uri1.scheme.downcase == uri2.scheme.downcase || 
      (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme) 
end

 ほー。二つの条件式が書いてあって、どちらかを満たせばtrueなわけで、エラーがでないわけだ。

各条件式を個別にみてみる。あくまでイメージをつかむためにざっくりとね。

1つ目の条件式:uri1.scheme.downcase == uri2.scheme.downcase

uri.shemaは、urlのプロトコル部分を抜き出すもの。
例えば、http://www.yahoo.co.jpを与えたら、httpが戻り値となる。

ということで、こんな感じのことが書かれている。
「リダイレクト元とリダイレクト先の呼び出しプロトコルが同じだったらOK!」

例えば、以下はOK。

  • リダイレクト元:http
  • リダイレクト先:http

もちろん、以下もOK。

  • リダイレクト元:https
  • リダイレクト先:https

リダイレクト元とリダイレクト先の両方が「http」であればOK。
もちろん、「https」でもOK。但し、どちらかが異なったらNG。

2つ目の条件式: (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme)  

これは正規表現で書かれていてすごいわかりにくい。
すごいざっくり表現するとこんな感じ。
「リダイレクト元とリダイレクト先の呼び出しプロトコルが異なってる場合は、httpかftpだったらOK!」例えば、以下はOK

  • リダイレクト元:http or ftp
  • リダイレクト先:http or ftp

Redirection Forbiddenの理由

redirection禁止の原因は「プロトコル変更」なわけです。
リダイレクト先に問題があるよねということではないんですよね。
解決先としてはこの「プロトコル変更」を許可するとか、無視するとかを組込むことなわけです。
しかし、自分の手でゴニョゴニョやるのは、車輪の再開発なわけですから、他人様の御知恵を借りることにします。

 解決策

以下のgemを使わせていただきましょう。

github.com

Railsでの使い方を簡単に書いておきます。

まずGemfileに以下を追加して、bundle installして下さい(もっと良いコマンドあればよしなにお願いします)

  • gem 'open_uri_redirections'

さらに、適当なところで、以下を追加して下さい。

  • require 'open_uri_redirections'

そして、http->httpsのみを許可するのか、https->httpも許可するのかで記述方法が変わります。

  • http->httpsのみ許可する場合
        open(url, :allow_redirections => :safe)
  • http->httpshttps->httpsの両方を許可する場合
       open(url, :allow_redirections => :all)

サンプルコードとしは以下の感じです。

require 'open-uri'
require 'open_uri_redirections'
url = @urlstring
charset = nil
html = open(url, :allow_redirections => :all) do |f|
    charset = f.charset
    f.read
end

 

お試しあれ。

 

 

 

あー、大阪、暑い(全国どこでもそうなんだろうけど)

 

以上

(横の小さい画像はアイキャッチ用の画像です。気にしないで下さい)f:id:sunday150:20160709155902p:plain