Ruby - net / http - следующие редиректы

У меня есть URL-адрес, и я использую HTTP GET для передачи запроса на страницу. Что происходит с самым последним вариантом (в net/http), так это то, что сценарий не выходит за рамки ответа 302. Я пробовал несколько разных решений; HTTPClient, net / http, Rest-Client, Patron ...

Мне нужен способ перейти к последней странице, чтобы проверить тег атрибута на этой странице html. Перенаправление происходит из-за того, что мобильный пользовательский агент попадает на страницу, которая перенаправляет на мобильное представление, следовательно, мобильный пользовательский агент в заголовке. Вот мой код, как он есть сегодня:

require 'uri'
require 'net/http'

class Check_Get_Page

    def more_http
        url = URI.parse('my_url')
        req, data = Net::HTTP::Get.new(url.path, {
        'User-Agent' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_2 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8H7 Safari/6533.18.5'
        })
        res = Net::HTTP.start(url.host, url.port) {|http|
        http.request(req)
            }
        cookie = res.response['set-cookie']
        puts 'Body = ' + res.body
        puts 'Message = ' + res.message
        puts 'Code = ' + res.code
        puts "Cookie \n" + cookie
    end

end

m = Check_Get_Page.new
m.more_http

Любые предложения будут ценны!


person r3nrut    schedule 03.08.2011    source источник
comment
Я использовал гем final_redirect_url, чтобы получить окончательный URL-адрес после нескольких перенаправлений.   -  person Indyarocks    schedule 04.05.2017


Ответы (6)


Чтобы следовать перенаправлениям, вы можете сделать что-то вроде этого (взято из ruby-doc)

После перенаправления

require 'net/http'
require 'uri'

def fetch(uri_str, limit = 10)
  # You should choose better exception.
  raise ArgumentError, 'HTTP redirect too deep' if limit == 0

  url = URI.parse(uri_str)
  req = Net::HTTP::Get.new(url.path, { 'User-Agent' => 'Mozilla/5.0 (etc...)' })
  response = Net::HTTP.start(url.host, url.port, use_ssl: true) { |http| http.request(req) }
  case response
  when Net::HTTPSuccess     then response
  when Net::HTTPRedirection then fetch(response['location'], limit - 1)
  else
    response.error!
  end
end

print fetch('http://www.ruby-lang.org/')
person emboss    schedule 03.08.2011
comment
Есть какие-нибудь подсказки относительно того, как добавить пользовательский агент в заголовок? response = Net :: HTTP.get_response (URI.parse (uri_str.encode), {'User-Agent' = ›ua}) Я пробовал это, но похоже, что это не работает. Я получаю следующую ошибку: c: /Ruby191/lib/ruby/1.9.1/net/http.rb: 581: in `initialize ': невозможно преобразовать URI :: HTTP в String (TypeError) - person r3nrut; 04.08.2011
comment
Это НЕ работает для ссылки, которая перенаправляется на себя, но с добавлением обратной косой черты, например, fetch('http://epn.dk/okonomi2/dk/ECE5373277/chefoekonom-corydon-skyder-langt-over-mal'), первая итерация, генерирует #<Net::HTTPMovedPermanently 301 Moved Permanently readbody=true>, затем исключение ... - person Peter Lee; 26.04.2013
comment
Это не работает, если response['Location'] является относительным путем, например: '/ inbox'. В таком случае необходимо указать исходный путь uri, например: url.path = response['Location']. - person Matt Huggins; 19.07.2013
comment
где вы определяете переменную ua? - person ecleel; 01.01.2015
comment
@MattHuggins Согласно спецификации HTTP, заголовок местоположения всегда должен быть абсолютный URI, а не относительный путь. Где вы видите относительные пути? - person David Moles; 28.05.2015
comment
@DavidMoles, я не знаю, это было 2 года назад. Но свершилось! - person Matt Huggins; 28.05.2015
comment
@DavidMoles - Например, http://www.puzzledragonx.com/en/monster.asp?n=9999 - curl показывает перенаправление 302 с заголовком Location: /, а приведенный выше шаблон кода подавляется без совета @MattHuggins. Или, скорее, с небольшой настройкой - создайте новый new_uri = URI.parse(response['Location']), затем if new_uri.relative? установите new_uri.scheme = uri.scheme' and 'new_uri.host = uri.host - в противном случае, если вы попытаетесь обновить исходный путь, любой раздел запроса или фрагмента все равно останется из исходного uri. - person DreadPirateShawn; 28.09.2015
comment
comment
@DreadPirateShawn, я считаю, что порт также нужно обновить со старого uri. - person Konstantin; 29.01.2018
comment
@MattHuggins, @DreadPirateShawn: вместо копирования определенных атрибутов URI используйте URI.join(old_uri, new_location). Это позволит сохранить любые атрибуты, не указанные в new_location из старого URI, но использовать новую схему или имя хоста, если они предоставлены. - person sondra.kinsey; 16.09.2018

Учитывая URL-адрес, который перенаправляет

url = 'http://httpbin.org/redirect-to?url=http%3A%2F%2Fhttpbin.org%2Fredirect-to%3Furl%3Dhttp%3A%2F%2Fexample.org'

A. Net::HTTP

begin
  response = Net::HTTP.get_response(URI.parse(url))
  url = response['location']
end while response.is_a?(Net::HTTPRedirection)

Убедитесь, что вы обрабатываете случай, когда перенаправлений слишком много.

B. OpenURI

open(url).read

OpenURI::OpenRead#open по умолчанию следует перенаправлениям, но не ограничивает их количество.

person Panic    schedule 27.06.2018

Я написал для этого еще один класс, основанный на приведенных здесь примерах, всем большое спасибо. Я добавил файлы cookie, параметры и исключения и наконец получил то, что мне нужно: https://gist.github.com/sekrett/7dd4177d6c87cf8265cd

require 'uri'
require 'net/http'
require 'openssl'

class UrlResolver
  def self.resolve(uri_str, agent = 'curl/7.43.0', max_attempts = 10, timeout = 10)
    attempts = 0
    cookie = nil

    until attempts >= max_attempts
      attempts += 1

      url = URI.parse(uri_str)
      http = Net::HTTP.new(url.host, url.port)
      http.open_timeout = timeout
      http.read_timeout = timeout
      path = url.path
      path = '/' if path == ''
      path += '?' + url.query unless url.query.nil?

      params = { 'User-Agent' => agent, 'Accept' => '*/*' }
      params['Cookie'] = cookie unless cookie.nil?
      request = Net::HTTP::Get.new(path, params)

      if url.instance_of?(URI::HTTPS)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      end
      response = http.request(request)

      case response
        when Net::HTTPSuccess then
          break
        when Net::HTTPRedirection then
          location = response['Location']
          cookie = response['Set-Cookie']
          new_uri = URI.parse(location)
          uri_str = if new_uri.relative?
                      url + location
                    else
                      new_uri.to_s
                    end
        else
          raise 'Unexpected response: ' + response.inspect
      end

    end
    raise 'Too many http redirects' if attempts == max_attempts

    uri_str
    # response.body
  end
end

puts UrlResolver.resolve('http://www.ruby-lang.org')
person sekrett    schedule 22.01.2016
comment
Спасибо за этот фрагмент кода! Я думаю, вы можете закрыть http-соединения (finish), чтобы они не протекали. Очень признателен! - person gmcnaughton; 20.05.2016
comment
Определенно лучшее решение для меня на данный момент. После этого я мог легко работать со страницей с html_to_parse = Nokogiri::HTML(UrlResolver.resolve('http://www.ruby-lang.org')). Спасибо. - person DemitryT; 15.07.2016
comment
Я не уверен на 100%, но в Ruby я думаю, что каждый объект автоматически уничтожается, когда выходит за пределы функции def. - person sekrett; 08.08.2016
comment
Вы также можете использовать url.request_uri вместо создания path вручную, он включает параметры запроса. - person gmcnaughton; 27.09.2016
comment
@gmcnaughton, хорошо. Вы можете отправить мне запрос на перенос на Github? - person sekrett; 28.09.2016

Ссылка, которая сработала для меня, находится здесь: http://shadow-file.blogspot.co.uk/2009/03/handling-http-redirection-in-ruby.html

По сравнению с большинством примеров (включая принятый здесь ответ), он более надежен, поскольку обрабатывает URL-адреса, которые являются просто доменом (http://example.com - необходимо добавить /), специально обрабатывает SSL, а также относительные URL-адреса.

Конечно, в большинстве случаев вам будет лучше использовать библиотеку, такую ​​как RESTClient, но иногда требуется низкоуровневая детализация.

person mahemoff    schedule 18.04.2014

Возможно, вы можете использовать здесь драгоценный камень curb-fu https://github.com/gdi/curb-fu единственное, что это дополнительный код, чтобы он следовал за перенаправлением. Раньше я использовал следующее. Надеюсь, поможет.

require 'rubygems'
require 'curb-fu'

module CurbFu
  class Request
    module Base
      def new_meth(url_params, query_params = {})
        curb = old_meth url_params, query_params
        curb.follow_location = true
        curb
      end

      alias :old_meth :build
      alias :build :new_meth
    end
  end
end

#this should follow the redirect because we instruct
#Curb.follow_location = true
print CurbFu.get('http://<your path>/').body
person Yesh    schedule 04.08.2011
comment
У меня были сложности с тем, чтобы заставить работать curb-fu на моем компьютере с Windows с использованием Ruby 1.9.1p430 ... Я могу заставить его работать на моем Mac, но, поскольку это то, что мне нужно запустить на сервере Windows, мне нужно ограничить- фу для завершения установки. Спасибо за предложение. - person r3nrut; 04.08.2011

Если вам не нужно заботиться о деталях при каждом перенаправлении, вы можете использовать библиотеку Mechanize.

require 'mechanize'

agent = Mechanize.new
begin
    response = @agent.get(url)
rescue Mechanize::ResponseCodeError
    // response codes other than 200, 301, or 302
rescue Timeout::Error
rescue Mechanize::RedirectLimitReachedError
rescue StandardError
end

Он вернет целевую страницу. Или вы можете отключить перенаправление следующим образом:

agent.redirect_ok = false

Или вы можете при желании изменить некоторые настройки по запросу

agent.user_agent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Mobile Safari/537.36"
person quangkid    schedule 26.02.2020