Как стать автором
Обновить

Комментарии 48

Действительно поэтичный комментарий, но следующий за ним ответ мне также по душе.
И — да, я пользовался XML парсерами. И в данный момент вполне обхожусь регулярными выражениями.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Это мой первый пост на Хабре и я ещё не настолько всем этим увлечён (да и не до конца разобрался в нюансах местной рейтинговой системы), чтобы остро переживать удары судьбы, являющиеся мне в виде красненьких циферок =)
Ну а там жизнь покажет.
Chuck Norris can parse HTML with regex
Хорошо хорошо. HTML бывает невалидным, бывает полезная для распознавания нужной области информация в комментариях, бывают обрубки HTML в скриптах, и много прочей страшной гадости.

Классические регулярные выражения, конечно же, слабоваты по своим возможностям, так что я для этих целей использую регулярные выражения на основе PEG — как раз получается хороший баланс между полноценным парсером и узкоспециализированным регулярным выражением.
Да, именно об этом.

Посмотрите пример pgrep в habrahabr.ru/blogs/crazydev/100292/

Это конечно же хак, но он работает.
Пардон, я тут немного припозднился с вопросом но все же (тем более сейчас для меня эта тема актуальна)… А как поможет дата мапинг для мапинга Html на объекты?
Если кто-то не может распарсить контент странички регулярками — значит он не знает регулярки!
А насчет метода с XML — один незакрытый тег и всё к чертям.
Это понятно. да и на страничке можно такое напридумывать, что запаришься писать парсер))
Можете продемонстрировать как вы с этой страницы htmlagilitypack.codeplex.com/
выберите содержание #WikiContent?
Элементарно, Ватсон!

Для начала посмотрим на HTML код… Визуально определяем что нужный нам контент начинается с уникальной последовательности символов:
<div id=«WikiContent» class=«WikiContent»>
А после него присутствует это:
</div>.*?<div id=«WikiInfo» class=«wikiSource WikiInfo»> (где .*? — это уже часть регулярки)

С помощью моего любимого regexp тестера проверяем регулярочку:

.*?<div id=«WikiContent» class=«WikiContent»>(?<content>.*?)</div>.*?<div id=«WikiInfo» class=«wikiSource WikiInfo»>.*

(можно было проверить по-разному, я решил использовать метод замены)

скрин тут
Давайте теперь сравнима с правильным инструментом для данной задачи.

var content = document.QuerySelector("#WikiContent").InnerHtml;

Предположим что завтра пропал блок #WikiInfo и вместо него будет пяток безымянных дивов, в моем случае ничего менять не нужно, что будете делать вы?
Так же как и вы, по диву с id, но регуляркой. (поверьте, у меня большой опыт в этом деле)

Хотя, если посмотреть на это с практической точки зрения:
На написание регулярки у меня ушло 2 минуты.
На парсинг всего сайта уйдет не более суток.
Завтра всё будет в моей базе ;) и мне будет всё-равно если что-то изменится.

Если серьезно — то вы правы на счет document.QuerySelector…
Так даже правильнее и красивее, но лишь в данном случае (с валидным HTML).

>Так же как и вы, по диву с id, но регуляркой.
Вот на это решение я и хотел посмотреть.

Думаю часть задач таки требует отслеживание изменений контента сайта.

Есть множество библиотек устраняющие не валидность html.

>один незакрытый тег и всё к чертям.
Tidy, BeatifulSoup как раз для таких ситуаций придумали
Конечно, можно что только не использовать, но главное в нашем деле скорость…
Чем больше операций с громадными текстами, тем медленнее всё работает.
Я тоже как-то использовал и SgmlReader и xpath, но регулярки оказались самым быстрым методом парсинга (и не только HTML).
Что то мне подсказывает что в данном синхронном примере основное время занимает ожидание сокета. Но в целом регулярками пожалуй быстрее. Зависит от реализации библиотек регулярок и html парсеров больше.
Куча каких-то регулярок, которые ещё и привязываются как расширение к строке. Не удобно же, число методов расширения будет плодиться со временем (если не из серии написал и забыл).

Мой вариант с использованием HtmlAgilityPack:

var autoRequest = WebRequest.Create(@"http://vin.auto.ru/resolve.html?vin=TMBBD41Z57B150932");
var autoResponse = (HttpWebResponse)autoRequest.GetResponse();
if (autoResponse.StatusCode == HttpStatusCode.OK)
{
	var doc = new HtmlDocument();
	doc.Load(autoResponse.GetResponseStream());
	var node = doc.DocumentNode.SelectSingleNode("//dl[@class=\"def-list md\"]");

	var dt = node.SelectNodes(".//strong");
	var dd = node.SelectNodes(".//dd");

	for(var i = 0;i<dt.Count;i++)
	{
		Console.WriteLine(dt[i].InnerText+" = "+ dd[i].InnerText);
	}
}


Кривость, конечно, но мне кажется, всё равно куда более читабельно, нежели
"<dt[\s\S]+?strong>(?<valDT>[\s\S]+?)</strong></dt><dd[^>]*>(?<valDD>[\s\S]+?)</dd>"
кстати mezastel хорошую же статью написал про совместное использование watin и HtmlAgilityPack habrahabr.ru/blogs/net/93958/
Да, я читал эту статью. К сожалению, в многопоточном варианте использование WatiN превращается в ад.
Ну не то что в ад, просто нужно понимать как их правильно готовить. А WatiN имеет смысл когда нужен реальный, живой браузер. Кстате, коллеги используют для этих целей WebKit — подробностей не знаю но многопоточность там наверняка получше.
Все равно Ад =) Пытались тут сценарий пользователя поставить на тестирование — прокувыркавшись 1.5 дня с ватином плюнул и взял WebAii — куда как лучшее изделие, как в плане документации так и поддержки. Телерик кстати купил контору — видать неспроста =).
Там тоже есть свои баги и особенности. Но оно хотя бы работает для сценариев чуть сложнее чем «открыть страничку-кликнуть кнопку», в отличии от watin, которого так и не удалось заставить загружать файлы в IE
А с селекторами могло бы быть ещё немного читабельней.
code.google.com/p/fizzler/
>>Куча каких-то регулярок,
Не куча, а 2, а точнее — можно использовать 1, о чём и написано.
>>которые ещё и привязываются как расширение к строке. Не удобно же, число методов расширения будет плодиться со временем
Для данного конкретного примера расширителя всего 2 (повторюсь, можно использовать только 1) и плодиться там уже просто некуда.

Ваш подход также вполне рабочий, просто он другой, это, имхо, вопрос привычки, личного опыта и предпочтений.
представьте себе ситуацию, вы написал 10 парсеров для 10 сайтов и каждый день их парсите. В один прекрасной момент, парсер для какого то сайта перестает работать. Причина кроется в том, что они изменили название класса, и теперь вместо стоит . Пользователь, который просматривает эту страницу, разницу не заметит, а ваш парсер перестанет работать. Вы конечно же исправите свою регулярку и все станет работать как прежде. Но пройдет некоторое время и опять перестанет работать по той же причине. После нескольких изменений вам все это надоест. И вы поймете, что ваш метод не очень то хороший.

В идеале скрипт сам должен определить зону, которая является контентом. Т.е. указал сайт и этого достаточно, скрипт сам находит блок, где находится контент и его забирает.
Но если это не получится, то хотя бы ваш парсер должен правильно работать когда произошли маленькие изменения в разметке html.
Все правильно, когда много сайтов и каждый день парсятся, то постоянно происходят какие-то изменения на сайте. И XPath перестает работать. Наилучший вариант — это автоматическое определение нужных блоков, например, по наличию текстового названия, ссылки, цены.
Несколько советов:
1. Если все чтто вам надо от зароса — это поставить несколько хедеров и выдернуть строку, то не стоит стрелять из пушки по воробьям. Все это нагромождение реализуется гораздо проще через WebClient.
WebClient client = new WebClient();
client.Headers[...
string result = client.DownloadString(...url...);

* This source code was highlighted with Source Code Highlighter.


2. Если уж достаем тяжелую артиллерию в виде мануальной работы с request/response, то лучше тогда показать на том, что не сделать через WebClient, например, работу с куками.

3. По поводу парсинга, целиком согласен с steck — лучше использовать HtmlAgilityPack. Тогда с документом можно работать как с DOM-моделью, используя XPath и т.п. Регулярные выражения громоздки и более чувствительны к ошибкам.

4. А если еще и поведать о секретах… Например, что приложение будет падать с исключением при обращении к https-сайтам с невалидным сертификатом и нужно делать кастомный обработчик. Что все фреймворки вплоть до 3.5sp1 имеют ошибку в обработках кук вида ".domain.com" и это вылечено не будет никогда, только переходом на 4.0… в общем что-то меня понесло :)
В итоге статья типа «дал вам представление о том, что такое веб-скрейпинг», по-тихоньку превращается в мастер-класс =)
Ну вот вам несколько ссылочек по теме: 1 2 3 4
Что-то прорвало Хабр на эту тему! Всем хочется WebRequest'ами поиграться. Хочется в очередной раз попиарить свои посты, но не буду. Только вот жаль, что информации новой — кот наплакал.

Реквестирую пост про то, как реагировать на изменения на сайте. Если никто не напишет, сам напишу :|
Многим эта тема знакома и многим приходилось это делать.
Насчет изменений на сайте — тут уж в большенстве случаев всё сводится к мониторингу логов и ручному изменению кода, чтобы быть up-to-date.
У вас есть какой-то другой способ?

Ну мониторинг — это тоже важно, особенно когда проект вешается на саппорт и нужно реактивно фиксить скрейперы и биллить за работу :)
xpath рулит со страшной силой.
Какая жесть. Взять готовый парсер и его заюзать совесть не позволяет?

Хотя-бы тотже питонячий BeautifulSoup/HTML5 который отлично жрется ironpython.
А где можно примеры посмотреть (с IronPython)?
Не в тему но всетаки: совсем недавно делал то же самое. Инструмент Python + lxml — вообще никаких сложнойстей. Вся программа 10 строчек.
lxml не всегда корректно кодировку распознает. Приходится немножко с бубном поплясать.
Но если один сайт и кодировка заранее известна — никаких проблем
Питонщикам очень рекомендую Scrapy scrapy.org

Очень мощный движок для веб-скрепинга. Он на основе Twisted написан т.е. асинхронный, работает очень шустро, есть веб-интерфейс, telnet консоль удаленного управления, консоль для тестирования xpath и отладки… В общем если нужно серьезного паука писать — MustHave!!!
Уверен что .Netчики тоже смогут воспользоваться через IronPython.
Офигеть. Теперь и реверсу БД придумали новое, современное название.

Хотел бы заступиться за регулярки! Икспасы и прочие игрища с домом — это все прекрасно, но бесполезно, особенно если объем, кхм, «скрейпинга», не по авто.ру за два вечера.

Во-первых, регулярки сильно шустрее и экономичнее, если есть опыт в составлении. Попробуйте в дотнете крутить постройкой ДОМа и «красивой» выборкой в писят потоков, и тут же попробуйте регулярки. Сами удостоверитесь.

Во-вторых, приноровившись, оператор будет писать регулярки, которые будут вылетать (при изменениях источника) очень редко. С опытом вырабатываются три-пять стандартных паттернов/подходов, следуя которым вы избавите себя от большинства проблем с редизайнами источника.

В-третьих, чаще встречаются проекты, у которых сложно выбрать плавающий элемент (а тем паче — группу элементов), используя ДОМ, но с регулярками все решается на раз-два.

В-четвертых, есть источники, из которых в принципе не выдрать информацию через ДОМ, не применяя тут же регулярки или аналог сабстр. Я гарантирую это!

В-пятых, разговоры о «нечитабельности регулярок» — позор для программисткого рода. Вы бы еще на нечитабельность ассемблера жаловались. Цель оправдывает средства. Другой вопрос, что читабельность регулярок сильно повышается по мере накопления опыта в составлении типовых «запросов», плюс простейший встроенный в ваш парсер подсветитель синтаксиса лишит вас многого геммора по первому времени. Встроенные паттерны регулярок и автоматические вычленятели типичных структур исходного документа — тоже.

Бага с куками ".domain.com" легко обходится алгоритмически. Даже гугля что-то там предлагает, но я предварительно самостоятельно ее выявил и пофиксил — там работы на секундочку.

Я писал софт для datarama.biz, а там самые маньячные сборщики, которых я знаю или о которых когда-либо слышал. Могу сказать, что ни один проект из представленных (а представлено там очень ограниченное число законченных проектов) не занимал на все про все более 60-80 минут, начиная от создания проекта в парсере, и заканчивая экспортом базы и генерацией статистического отчета.

Собственно, перед и во время написания этого вполне себе «универсального» парсера мы реально долго и муторно брейнштормили, распарсили кучу различной информации из разных источников (не только сайты), и хотите верьте, хотите — нет, но вкупе с грамотной моделью ПО, регулярки выдернут, во-первых, ВСЕ, во-вторых, намного быстрее. Необходимые данные уже будут лежать в 1-3NF базе к тому моменту, пока «красивые» одноразовые решения на XPath и прочих ДОМ-радостях будут стоять в очереди за оперативой.

Простите за сумбурность :) Конечно же, мои мысли относятся только к тем случаям, когда собирать надо действительно много, из разных источников и в течение длительного периода времени. Если же вам только авто.ру единожды распарсить, то спору нет — ДОМ наше все! :D
использовал, как и многие тут писали Html Agility Pack, когда то искал разные варианты outcoldman.livejournal.com/40291.html, но остановился на нем, так как просто и легко работает. Потом правда все таки реализовал идею только для своего сайта, паршу html страницу френд ленты LJ. Работает нормально. У каждого метода есть плюсы и минусы.

Поздравляю с первой статьей на хабре! Если критикуют — это хорошо, главное что есть обсуждение.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории