Комментарии 48
Плохо плохо использовать регулярные выражения для парсинга HTML.
You cannot parse HTML using regular expressions!
You cannot parse HTML using regular expressions!
Действительно поэтичный комментарий, но следующий за ним ответ мне также по душе.
И — да, я пользовался XML парсерами. И в данный момент вполне обхожусь регулярными выражениями.
И — да, я пользовался XML парсерами. И в данный момент вполне обхожусь регулярными выражениями.
НЛО прилетело и опубликовало эту надпись здесь
Chuck Norris can parse HTML with regex
Хорошо хорошо. HTML бывает невалидным, бывает полезная для распознавания нужной области информация в комментариях, бывают обрубки HTML в скриптах, и много прочей страшной гадости.
Классические регулярные выражения, конечно же, слабоваты по своим возможностям, так что я для этих целей использую регулярные выражения на основе PEG — как раз получается хороший баланс между полноценным парсером и узкоспециализированным регулярным выражением.
Классические регулярные выражения, конечно же, слабоваты по своим возможностям, так что я для этих целей использую регулярные выражения на основе PEG — как раз получается хороший баланс между полноценным парсером и узкоспециализированным регулярным выражением.
С PEG никогда не сталкивался, похоже что речь об этом, для C# реализация вот.
Надо бы посмотреть повнимательнее…
Надо бы посмотреть повнимательнее…
Да, именно об этом.
Посмотрите пример pgrep в habrahabr.ru/blogs/crazydev/100292/
Это конечно же хак, но он работает.
Посмотрите пример pgrep в habrahabr.ru/blogs/crazydev/100292/
Это конечно же хак, но он работает.
Рьяно плюсую камент. Сам раньше страдал таким, теперь вот одумался на старости лет, мэппинг начал использовать.
Если кто-то не может распарсить контент странички регулярками — значит он не знает регулярки!
А насчет метода с XML — один незакрытый тег и всё к чертям.
А насчет метода с XML — один незакрытый тег и всё к чертям.
Это понятно. да и на страничке можно такое напридумывать, что запаришься писать парсер))
Можете продемонстрировать как вы с этой страницы htmlagilitypack.codeplex.com/
выберите содержание #WikiContent?
выберите содержание #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»>.*
(можно было проверить по-разному, я решил использовать метод замены)
скрин тут
Для начала посмотрим на 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 и вместо него будет пяток безымянных дивов, в моем случае ничего менять не нужно, что будете делать вы?
var content = document.QuerySelector("#WikiContent").InnerHtml;
Предположим что завтра пропал блок #WikiInfo и вместо него будет пяток безымянных дивов, в моем случае ничего менять не нужно, что будете делать вы?
Так же как и вы, по диву с id, но регуляркой. (поверьте, у меня большой опыт в этом деле)
Хотя, если посмотреть на это с практической точки зрения:
На написание регулярки у меня ушло 2 минуты.
На парсинг всего сайта уйдет не более суток.
Завтра всё будет в моей базе ;) и мне будет всё-равно если что-то изменится.
Если серьезно — то вы правы на счет document.QuerySelector…
Так даже правильнее и красивее, но лишь в данном случае (с валидным HTML).
Хотя, если посмотреть на это с практической точки зрения:
На написание регулярки у меня ушло 2 минуты.
На парсинг всего сайта уйдет не более суток.
Завтра всё будет в моей базе ;) и мне будет всё-равно если что-то изменится.
Если серьезно — то вы правы на счет document.QuerySelector…
Так даже правильнее и красивее, но лишь в данном случае (с валидным HTML).
>один незакрытый тег и всё к чертям.
Tidy, BeatifulSoup как раз для таких ситуаций придумали
Tidy, BeatifulSoup как раз для таких ситуаций придумали
Конечно, можно что только не использовать, но главное в нашем деле скорость…
Чем больше операций с громадными текстами, тем медленнее всё работает.
Я тоже как-то использовал и SgmlReader и xpath, но регулярки оказались самым быстрым методом парсинга (и не только HTML).
Чем больше операций с громадными текстами, тем медленнее всё работает.
Я тоже как-то использовал и SgmlReader и xpath, но регулярки оказались самым быстрым методом парсинга (и не только HTML).
Куча каких-то регулярок, которые ещё и привязываются как расширение к строке. Не удобно же, число методов расширения будет плодиться со временем (если не из серии написал и забыл).
Мой вариант с использованием HtmlAgilityPack:
Кривость, конечно, но мне кажется, всё равно куда более читабельно, нежели
Мой вариант с использованием 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
Там тоже есть свои баги и особенности. Но оно хотя бы работает для сценариев чуть сложнее чем «открыть страничку-кликнуть кнопку», в отличии от watin, которого так и не удалось заставить загружать файлы в IE
А с селекторами могло бы быть ещё немного читабельней.
code.google.com/p/fizzler/
code.google.com/p/fizzler/
Как то так:
var doc = new HtmlDocument();
doc.Load(@«vin.auto.ru/resolve.html?vin=TMBBD41Z57B150932»);
…
var doc = new HtmlDocument();
doc.Load(@«vin.auto.ru/resolve.html?vin=TMBBD41Z57B150932»);
…
>>Куча каких-то регулярок,
Не куча, а 2, а точнее — можно использовать 1, о чём и написано.
>>которые ещё и привязываются как расширение к строке. Не удобно же, число методов расширения будет плодиться со временем
Для данного конкретного примера расширителя всего 2 (повторюсь, можно использовать только 1) и плодиться там уже просто некуда.
Ваш подход также вполне рабочий, просто он другой, это, имхо, вопрос привычки, личного опыта и предпочтений.
Не куча, а 2, а точнее — можно использовать 1, о чём и написано.
>>которые ещё и привязываются как расширение к строке. Не удобно же, число методов расширения будет плодиться со временем
Для данного конкретного примера расширителя всего 2 (повторюсь, можно использовать только 1) и плодиться там уже просто некуда.
Ваш подход также вполне рабочий, просто он другой, это, имхо, вопрос привычки, личного опыта и предпочтений.
представьте себе ситуацию, вы написал 10 парсеров для 10 сайтов и каждый день их парсите. В один прекрасной момент, парсер для какого то сайта перестает работать. Причина кроется в том, что они изменили название класса, и теперь вместо стоит . Пользователь, который просматривает эту страницу, разницу не заметит, а ваш парсер перестанет работать. Вы конечно же исправите свою регулярку и все станет работать как прежде. Но пройдет некоторое время и опять перестанет работать по той же причине. После нескольких изменений вам все это надоест. И вы поймете, что ваш метод не очень то хороший.
В идеале скрипт сам должен определить зону, которая является контентом. Т.е. указал сайт и этого достаточно, скрипт сам находит блок, где находится контент и его забирает.
Но если это не получится, то хотя бы ваш парсер должен правильно работать когда произошли маленькие изменения в разметке html.
В идеале скрипт сам должен определить зону, которая является контентом. Т.е. указал сайт и этого достаточно, скрипт сам находит блок, где находится контент и его забирает.
Но если это не получится, то хотя бы ваш парсер должен правильно работать когда произошли маленькие изменения в разметке html.
Уж лучше напрямую делать запросы к vinformer.su, а то какой-то испорченный телефон получается.
Малоли, чего эти багоделы из авто.ру подмешивают в данные… ;-)
Малоли, чего эти багоделы из авто.ру подмешивают в данные… ;-)
Несколько советов:
1. Если все чтто вам надо от зароса — это поставить несколько хедеров и выдернуть строку, то не стоит стрелять из пушки по воробьям. Все это нагромождение реализуется гораздо проще через WebClient.
2. Если уж достаем тяжелую артиллерию в виде мануальной работы с request/response, то лучше тогда показать на том, что не сделать через WebClient, например, работу с куками.
3. По поводу парсинга, целиком согласен с steck — лучше использовать HtmlAgilityPack. Тогда с документом можно работать как с DOM-моделью, используя XPath и т.п. Регулярные выражения громоздки и более чувствительны к ошибкам.
4. А если еще и поведать о секретах… Например, что приложение будет падать с исключением при обращении к https-сайтам с невалидным сертификатом и нужно делать кастомный обработчик. Что все фреймворки вплоть до 3.5sp1 имеют ошибку в обработках кук вида ".domain.com" и это вылечено не будет никогда, только переходом на 4.0… в общем что-то меня понесло :)
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… в общем что-то меня понесло :)
Более удобно работать с классом WebClient, а не WebRequest — msdn.microsoft.com/en-us/library/system.net.webclient.aspx
Ну и про парсинг HTML уже написали — вместо regexp можно попробовать ну хотя бы HtmlAgilityPack — проще будет.
Ну и про парсинг HTML уже написали — вместо regexp можно попробовать ну хотя бы HtmlAgilityPack — проще будет.
Что-то прорвало Хабр на эту тему! Всем хочется WebRequest'ами поиграться. Хочется в очередной раз попиарить свои посты, но не буду. Только вот жаль, что информации новой — кот наплакал.
Реквестирую пост про то, как реагировать на изменения на сайте. Если никто не напишет, сам напишу :|
Реквестирую пост про то, как реагировать на изменения на сайте. Если никто не напишет, сам напишу :|
Многим эта тема знакома и многим приходилось это делать.
Насчет изменений на сайте — тут уж в большенстве случаев всё сводится к мониторингу логов и ручному изменению кода, чтобы быть up-to-date.
У вас есть какой-то другой способ?
Насчет изменений на сайте — тут уж в большенстве случаев всё сводится к мониторингу логов и ручному изменению кода, чтобы быть up-to-date.
У вас есть какой-то другой способ?
xpath рулит со страшной силой.
Какая жесть. Взять готовый парсер и его заюзать совесть не позволяет?
Хотя-бы тотже питонячий BeautifulSoup/HTML5 который отлично жрется ironpython.
Хотя-бы тотже питонячий BeautifulSoup/HTML5 который отлично жрется ironpython.
Не в тему но всетаки: совсем недавно делал то же самое. Инструмент Python + lxml — вообще никаких сложнойстей. Вся программа 10 строчек.
Питонщикам очень рекомендую Scrapy scrapy.org
Очень мощный движок для веб-скрепинга. Он на основе Twisted написан т.е. асинхронный, работает очень шустро, есть веб-интерфейс, telnet консоль удаленного управления, консоль для тестирования xpath и отладки… В общем если нужно серьезного паука писать — MustHave!!!
Очень мощный движок для веб-скрепинга. Он на основе Twisted написан т.е. асинхронный, работает очень шустро, есть веб-интерфейс, telnet консоль удаленного управления, консоль для тестирования xpath и отладки… В общем если нужно серьезного паука писать — MustHave!!!
Офигеть. Теперь и реверсу БД придумали новое, современное название.
Хотел бы заступиться за регулярки! Икспасы и прочие игрища с домом — это все прекрасно, но бесполезно, особенно если объем, кхм, «скрейпинга», не по авто.ру за два вечера.
Во-первых, регулярки сильно шустрее и экономичнее, если есть опыт в составлении. Попробуйте в дотнете крутить постройкой ДОМа и «красивой» выборкой в писят потоков, и тут же попробуйте регулярки. Сами удостоверитесь.
Во-вторых, приноровившись, оператор будет писать регулярки, которые будут вылетать (при изменениях источника) очень редко. С опытом вырабатываются три-пять стандартных паттернов/подходов, следуя которым вы избавите себя от большинства проблем с редизайнами источника.
В-третьих, чаще встречаются проекты, у которых сложно выбрать плавающий элемент (а тем паче — группу элементов), используя ДОМ, но с регулярками все решается на раз-два.
В-четвертых, есть источники, из которых в принципе не выдрать информацию через ДОМ, не применяя тут же регулярки или аналог сабстр. Я гарантирую это!
В-пятых, разговоры о «нечитабельности регулярок» — позор для программисткого рода. Вы бы еще на нечитабельность ассемблера жаловались. Цель оправдывает средства. Другой вопрос, что читабельность регулярок сильно повышается по мере накопления опыта в составлении типовых «запросов», плюс простейший встроенный в ваш парсер подсветитель синтаксиса лишит вас многого геммора по первому времени. Встроенные паттерны регулярок и автоматические вычленятели типичных структур исходного документа — тоже.
Бага с куками ".domain.com" легко обходится алгоритмически. Даже гугля что-то там предлагает, но я предварительно самостоятельно ее выявил и пофиксил — там работы на секундочку.
Я писал софт для datarama.biz, а там самые маньячные сборщики, которых я знаю или о которых когда-либо слышал. Могу сказать, что ни один проект из представленных (а представлено там очень ограниченное число законченных проектов) не занимал на все про все более 60-80 минут, начиная от создания проекта в парсере, и заканчивая экспортом базы и генерацией статистического отчета.
Собственно, перед и во время написания этого вполне себе «универсального» парсера мы реально долго и муторно брейнштормили, распарсили кучу различной информации из разных источников (не только сайты), и хотите верьте, хотите — нет, но вкупе с грамотной моделью ПО, регулярки выдернут, во-первых, ВСЕ, во-вторых, намного быстрее. Необходимые данные уже будут лежать в 1-3NF базе к тому моменту, пока «красивые» одноразовые решения на XPath и прочих ДОМ-радостях будут стоять в очереди за оперативой.
Простите за сумбурность :) Конечно же, мои мысли относятся только к тем случаям, когда собирать надо действительно много, из разных источников и в течение длительного периода времени. Если же вам только авто.ру единожды распарсить, то спору нет — ДОМ наше все! :D
Хотел бы заступиться за регулярки! Икспасы и прочие игрища с домом — это все прекрасно, но бесполезно, особенно если объем, кхм, «скрейпинга», не по авто.ру за два вечера.
Во-первых, регулярки сильно шустрее и экономичнее, если есть опыт в составлении. Попробуйте в дотнете крутить постройкой ДОМа и «красивой» выборкой в писят потоков, и тут же попробуйте регулярки. Сами удостоверитесь.
Во-вторых, приноровившись, оператор будет писать регулярки, которые будут вылетать (при изменениях источника) очень редко. С опытом вырабатываются три-пять стандартных паттернов/подходов, следуя которым вы избавите себя от большинства проблем с редизайнами источника.
В-третьих, чаще встречаются проекты, у которых сложно выбрать плавающий элемент (а тем паче — группу элементов), используя ДОМ, но с регулярками все решается на раз-два.
В-четвертых, есть источники, из которых в принципе не выдрать информацию через ДОМ, не применяя тут же регулярки или аналог сабстр. Я гарантирую это!
В-пятых, разговоры о «нечитабельности регулярок» — позор для программисткого рода. Вы бы еще на нечитабельность ассемблера жаловались. Цель оправдывает средства. Другой вопрос, что читабельность регулярок сильно повышается по мере накопления опыта в составлении типовых «запросов», плюс простейший встроенный в ваш парсер подсветитель синтаксиса лишит вас многого геммора по первому времени. Встроенные паттерны регулярок и автоматические вычленятели типичных структур исходного документа — тоже.
Бага с куками ".domain.com" легко обходится алгоритмически. Даже гугля что-то там предлагает, но я предварительно самостоятельно ее выявил и пофиксил — там работы на секундочку.
Я писал софт для datarama.biz, а там самые маньячные сборщики, которых я знаю или о которых когда-либо слышал. Могу сказать, что ни один проект из представленных (а представлено там очень ограниченное число законченных проектов) не занимал на все про все более 60-80 минут, начиная от создания проекта в парсере, и заканчивая экспортом базы и генерацией статистического отчета.
Собственно, перед и во время написания этого вполне себе «универсального» парсера мы реально долго и муторно брейнштормили, распарсили кучу различной информации из разных источников (не только сайты), и хотите верьте, хотите — нет, но вкупе с грамотной моделью ПО, регулярки выдернут, во-первых, ВСЕ, во-вторых, намного быстрее. Необходимые данные уже будут лежать в 1-3NF базе к тому моменту, пока «красивые» одноразовые решения на XPath и прочих ДОМ-радостях будут стоять в очереди за оперативой.
Простите за сумбурность :) Конечно же, мои мысли относятся только к тем случаям, когда собирать надо действительно много, из разных источников и в течение длительного периода времени. Если же вам только авто.ру единожды распарсить, то спору нет — ДОМ наше все! :D
использовал, как и многие тут писали Html Agility Pack, когда то искал разные варианты outcoldman.livejournal.com/40291.html, но остановился на нем, так как просто и легко работает. Потом правда все таки реализовал идею только для своего сайта, паршу html страницу френд ленты LJ. Работает нормально. У каждого метода есть плюсы и минусы.
Поздравляю с первой статьей на хабре! Если критикуют — это хорошо, главное что есть обсуждение.
Поздравляю с первой статьей на хабре! Если критикуют — это хорошо, главное что есть обсуждение.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Веб-скрейпинг и .Net