Как использовать Mechanize для разбора локального файла

Я использую Ruby и Mechanize для анализа локального HTML-файла, но не могу этого сделать. Это работает, если я использую URL:

agent = Mechanize.new
#THIS WORKS
#url = 'http://www.sample.com/sample.htm'
#page = agent.get(url) #this seems to work just fine but the following below doesn't

#THIS FAILS
file = File.read('/home/user/files/sample.htm') #this is a regular html file
page = Nokogiri::HTML(file)
pp page.body #errors here

page.search('/div[@class="product_name"]').each do |node|
  text = node.text  
  puts "product name: " + text.to_s
end

Ошибка:

/home/user/code/myapp/app/models/program.rb:35:in `main': undefined method `body' for #<Nokogiri::HTML::Document:0x000000011552b0> (NoMethodError)

Как мне получить объект страницы, чтобы я мог искать по нему?


person heebee313    schedule 07.02.2020    source источник
comment
Ошибка правильная, в документе Нокогири нет метода body. Nokogiri — это следующий слой ниже Mechanize, поэтому вы должны использовать его методы.   -  person the Tin Man    schedule 07.02.2020


Ответы (1)


Mechanize использует строки URI, чтобы указать на то, что он должен анализировать. Обычно мы используем схему «http» или «https», чтобы указать на веб-сервер, и в этом сильные стороны Mechanize, но доступны и другие схемы, включая «file", который можно использовать для загрузки локального файла.

У меня на рабочем столе есть небольшой HTML-файл под названием «test.rb»:

<!DOCTYPE html>
<html>
<head></head>
<body>
<p>
Hello World!
</p>
</body>
</html>

Запуск этого кода:

require 'mechanize'

agent = Mechanize.new
page = agent.get('file:/Users/ttm/Desktop/test.html')
puts page.body

Выходы:

<!DOCTYPE html>
<html>
<head></head>
<body>
<p>
Hello World!
</p>
</body>
</html>

Это говорит мне о том, что Mechanize загрузил файл, проанализировал его, а затем получил доступ к body.

Однако, если вам не нужно на самом деле манипулировать формами и/или перемещаться по страницам, Mechanize, вероятно, НЕ то, что вы хотите использовать. Вместо этого Nokogiri, который находится в разделе Mechanize, является лучшим выбором для синтаксического анализа, извлечения данных или манипулирования разметкой, и он не зависит от того, какая схема использовалась или где на самом деле находится файл:

require 'nokogiri'

doc = Nokogiri::HTML(File.read('/Users/ttm/Desktop/test.html'))
puts doc.to_html

который затем выводит тот же файл после его разбора.

Вернемся к вашему вопросу, как найти узел только с помощью Nokogiri:

Изменение test.html на:

<!DOCTYPE html>
<html>
<head></head>
<body>
<div class="product_name">Hello World!</div>
</body>
</html>

и работает:

require 'nokogiri'

doc = Nokogiri::HTML(File.read('/Users/ttm/Desktop/test.html'))
doc.search('div.product_name').map(&:text)
# => ["Hello World!"]

показывает, что Нокогири нашел узел и вернул текст.

Этот код в вашем примере может быть лучше:

text = node.text  
puts "product name: " + text.to_s

node.text возвращает строку:

doc = Nokogiri::HTML('<p>hello world!</p>')
doc.at('p').text # => "hello world!"
doc.at('p').text.class # => String

Так что text.to_s избыточно. Просто используйте text.

person the Tin Man    schedule 07.02.2020
comment
Вы благословение для мира. Большое спасибо. И за то, что научил меня быть более элегантным с моим кодом. - person heebee313; 08.02.2020
comment
Я не уверен насчет благословения, но это именно то, что мы должны здесь делать. Нокогири — действительно классный инструмент; Я использовал его для написания большого агрегатора RSS/RDF/Atom для компании и нашел его очень простым в использовании, особенно по сравнению с другими более ранними инструментами, с которыми нам приходилось работать. - person the Tin Man; 08.02.2020