Комментарии 33
Уж слишком базовый пример: парсинг одной страницы без перехода по ссылкам. В реальной жизни такой usecase встречается не часто. Так что автор правильно рекомендует mechanize в конце статьи.
+1
В ближайшее время планирую перевод следующей статьи этого автора, там рассказывается про использование mechanize.
+1
Расскажите о решении каких сложностей вы хотели бы услышать. Возможно, смогу рассказать.
+1
Мы используем PhantomJS из Ruby. С помощью Capybara. Наверное, это немного извращение, но работает.
+2
selenium есть, который рубями управляется, но он имеет некоторое количество ограничений и неудобств, с фантомом проще работать
+1
Переход по ссылкам — не самая лучшая идея, если вы собрались всерьез и надолго парсить сайт. Преимущественно вы знаете на какой странице находятся нужный данные и как можно сконструировать ссылку на нее. Каждый переход добавляет один HTTP запрос, а это не самое быстрое занятие. Возможно, если вы изучаете теорию алгоритмов, константы можно игнорировать, но в реальном мире миллион и два миллиона запросов — это две большие разницы. Конечно, если сайт очень хитрый и обфусцирует свои ссылки, ничего не поделаешь.
0
Не задумывался, вот тогда такой вопрос. Вот недавний пример из моей практики: сайт — каталог. Воск ссылки на две разные турбины из каталога: раз и два. Как видите, url состоит из:
Мне надо собрать информацию по всем турбинам на сайте. Я же правильно понимаю, что обход сайта по ссылкам — единственное решение?
- Засекреченного номера, идентифицирующего марку автомобиля
- oem артикул турбины
- артикул турбины
Мне надо собрать информацию по всем турбинам на сайте. Я же правильно понимаю, что обход сайта по ссылкам — единственное решение?
0
Пример из статьи хорош для самого начала. В реальной жизни приходится писать отдельно кроулер, который собирает основные направления и ссылки для парсера, учитывать необходимость вставки случайных пауз между запросами чтобы вероятность блокировки была пониже, дальше идут решения задачи с обрывами и продолжениями с обрыва (складываем прогресс в БД), дальше как вариант — параллелизация, запуск по расписанию и много чего еще.
Год назад хотел написать статью на реальном примере, но было лениво, да и не был уверен что правильно приводить некоторые примеры.
Могу посоветовать также хороший цикл статей по этой теме, там пошире немного: ruby.bastardsbook.com/chapters/html-parsing/
Год назад хотел написать статью на реальном примере, но было лениво, да и не был уверен что правильно приводить некоторые примеры.
Могу посоветовать также хороший цикл статей по этой теме, там пошире немного: ruby.bastardsbook.com/chapters/html-parsing/
0
от себя добавлю — механиз нужен в принципе только если на сайте есть логин. А так нокогири вполне себе «голый» отлично со всем справляется. А вот если нужно эмулировать браузер то да, механиз.
0
Не то, чтобы нужен, но с ним удобнее из-за наличия cookie jar. Залогиниться можно и с помощью Net::HTTP. Отправляем POST запрос с логином, паролем и, возможно, CSRF токеном; запоминаем возвращенный заголовок Set-Cookie. При этом, если нет CSRF, можно еще и сэкономить один запрос.
0
ну, если говорить о том что Вам хочется этот процесс оптимизировать — да, возможно такой подход во многих конкретных ситуациях оправдан. Но когда не хочется заморачиваться то 1 гем с простым интерфейсом самое то. Как в статье и продемонстрировано — простота. Создал обьект, и через простые методы обращаешься к нему а за кулисами он делает что-нибудь полезное и увы ресурсоемкое возможно.
По сему механиз вполне себе «когда не хочешь заморачиваться», особенно если скрапинг нужен на 1 фазе работы а не далее. Типа 1 раз срабатывание механиза, засунем в рельсы куда нибудь его в seeds.rb этот код или в миграцию, 1 раз отработаем начальное заполнение и норм )
По сему механиз вполне себе «когда не хочешь заморачиваться», особенно если скрапинг нужен на 1 фазе работы а не далее. Типа 1 раз срабатывание механиза, засунем в рельсы куда нибудь его в seeds.rb этот код или в миграцию, 1 раз отработаем начальное заполнение и норм )
0
Предлагаю ещё задуматься о nokogumbo, как о замене nokogiri.
0
А что это за PHP-style в первом куске кода из «Обработка html»?
Мне кажется, что имеет смысл сначала узнать про map-reduce, а потом писать статьи. А то не дай бог попадется джуниору какому — он так и будет людей кошмарить вот таким: `title_el.children.each { |c| c.remove if c.name == 'span' }` вместо `select`.
Мне кажется, что имеет смысл сначала узнать про map-reduce, а потом писать статьи. А то не дай бог попадется джуниору какому — он так и будет людей кошмарить вот таким: `title_el.children.each { |c| c.remove if c.name == 'span' }` вместо `select`.
+1
В защиту автора, статья — перевод. А на junior'ов есть code review. Как напишут так и перепишут.
+1
К переводчику — вопросов нет, кроме «а почему бы не дать тут примечанием „как правильно“».
0
Потому, что программирование — это французская поэзия, а не математика. Здесь нет правильно и неправильно. Вот, например, как нравится мне (на коленке, я понимаю, что можно много где улучшить). Но я уверен, что мой подход не понравится сторонникам функционального программирования, например. Кстати, по время рефакторинга заметил, что `remove` автор делает потому, что ему нужно потом получить текст parent node без span'ов. Я заменил это на dup, чтобы вызов метода не разрушал состояние объекта.
class Showing
attr_accessor :element
def initialize(element)
self.element = element
end
def id
element['id'].split('_').last.to_i
end
def tags
tag_links.map(&:text).map(&:strip)
end
def title
title_link.dup.touch { |title| title.children.reject! { |c| c.name == 'span' } }.text.strip
end
def dates
element.at_css('.start_and_pricing').inner_html.split('<br>').map(&:strip).map(&DateTime.method(:parse))
end
def description
element.at_css('.copy').text.gsub('[more...]', '').strip
end
def to_h
{
id: id,
title: title,
tags: tags,
dates: dates,
description: description
}
end
private
def tag_links
element.css('.tags a')
end
def title_link
element.at_css('h1 a')
end
end
showings = doc.css('.showing').map(&Showing.method(:initialize)).map(&:to_h)
+1
def to_h
Hash[%w(id title tags dates description).map { |w| [w.to_sym, "#{w}"] }]
end
:)
Hash[%w(id title tags dates description).map { |w| [w.to_sym, "#{w}"] }]
end
:)
0
Спасибо, но я же не претендовал на идеальный код, только хотел показать объектно-ориентированный способ. Кстати, ваш код не заработает: вы нигде не вызываете метод.
def to_h
attributes = %i(id title tags dates description)
attributes.zip(attributes.map(&method(:public_send))).to_h
end
0
в php есть array_filter, который аналогичен рубишному .select, так что не могу назвать это php-style.
0
array_filter медленнее, чем foreach, почти вдвое.
0
спасибо. а можно пруфлинк?
0
php.net/manual/en/function.microtime.php
Если вам неохота самому сделать benchmark, то вот: rocket-science.ru/hacking/2015/03/03/dont-be-a-language-slave/
Если вам неохота самому сделать benchmark, то вот: rocket-science.ru/hacking/2015/03/03/dont-be-a-language-slave/
0
А код на php медленнее чем на c++ и что?
В большинстве случаев более понятный код важнее чем производительность.
В большинстве случаев более понятный код важнее чем производительность.
+1
соглашусь
0
Так, да не так. Язык, как обычный разговорный, так и язык программирования, семантическими конструкциями провоцирует говорить/писать определенным образом. Тривиальный map-reduce, будучи записан с использованием php-синтаксиса, превращается в малочитаемую кашу.
Помимо производительности, разная нотация (`array_map` первым параметром хочет callback, `array_reduce` — уже наоборот), недоступность ключей без извращений типа `array_map(function($el) use($arr) { next($arr); ...` и прочие палки в колесах рано или поздно научат не выпендриваться и звать `foreach`.
Точно так же, как перевод Пруста или Манна на английский подчистую уничтожает авторский стиль.
Помимо производительности, разная нотация (`array_map` первым параметром хочет callback, `array_reduce` — уже наоборот), недоступность ключей без извращений типа `array_map(function($el) use($arr) { next($arr); ...` и прочие палки в колесах рано или поздно научат не выпендриваться и звать `foreach`.
Точно так же, как перевод Пруста или Манна на английский подчистую уничтожает авторский стиль.
0
То что вы написали верно, но именно задача фильтрации выглядит изящнее при array_filter, нежели foreach.
P.S. не хочу пост про прекрасный Ruby портить кодом на php, но можете сами написать реализацию для фильтрации по полю и посмотреть.
P.S. не хочу пост про прекрасный Ruby портить кодом на php, но можете сами написать реализацию для фильтрации по полю и посмотреть.
0
Добавлю для страждущих github.com/chriskite/anemone — очень прелестный Ruby инструмент для обхода сайта.
+2
Мы делаем на базе .net (sql, azure, asp.net) парсинг сайтов и мониторинг цен конкурентов xmldatafeed.com — помучались изрядно, но в итоге работает стабильно. Самое сложное — разбор html оказался.
0
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.
Веб-парсинг на Ruby