Поиск по блогу

суббота, 26 июня 2010 г.

Очистка текста от лишних html-тегов

С задачей очистки html от лишних тегов сталкиваются абсолютно все.

Первое, что приходит на ум, это использовать php-функцию strip_tags():
string strip_tags (string str [, string allowable_tags])


Функция возвращает строку, очищенную от тегов. В качестве аргумента allowable_tags передаются теги, которые не надо удалять. Функция работает, но, мягко говоря, неидеально. По ходу, там нет проверки на валидность кода, что может повлечь за собой удаление текста, не входящего в тэги.
Инициативные разработчики сложа руки не сидели — в сети можно найти доработанные функции. Хорошим примером является strip_tags_smart.

Применять или не применять готовые решения — личный выбор программиста. Так сложилось, что мне чаще всего не требуется "универсального" обработчика и бывает удобнее почистить код регулярками.

От чего зависит выбор того или иного способа обработки?

1. От исходного материала и сложности его анализа.
Если вам нужно обрабатывать достаточно простые htmp-тексты, без какой-либо навороченной верстки, ясные, как день :), то можно использовать стандартные функции.
Если в текстах есть определенные особенности, которые надо учесть, то тут-то и пишутся специальные обработчики. В одних может использоваться просто str_replace. Например:

$s = array('’' => '’',         // Right-apostrophe (eg in I'm)
'“' => '“', // Opening speech mark
'–' => '—', // Long dash
'â€' => '”', // Closing speech mark
'é' => 'é', // e acute accent
chr(226) . chr(128) . chr(153) => '’', // Right-apostrophe again
chr(226) . chr(128) . chr(147) => '—', // Long dash again
chr(226) . chr(128) . chr(156) => '“', // Opening speech mark
chr(226) . chr(128) . chr(148) => '—', // M dash again
chr(226) . chr(128) => '”', // Right speech mark
chr(195) . chr(169) => 'é', // e acute again
);

foreach ($s as $needle => $replace)
{
$htmlText = str_replace($needle, $replace, $htmlText);
}


Другие могут быть основаны на регулярных выражениях. Как пример:

function getTextFromHTML($htmlText)
{
$search = array ("'<script[^>]*?>.*?</script>'si", // Remove javaScript
"'<style[^>]*?>.*?</style>'si", // Remove styles
"'<xml[^>]*?>.*?</xml>'si", // Remove xml tags
"'<[\/\!]*?[^<>]*?>'si", // Remove HTML-tags
"'([\r\n])[\s] '", // Remove spaces
"'&(quot|#34);'i", // Replace HTML special chars
"'&(amp|#38);'i",
"'&(lt|#60);'i",
"'&(gt|#62);'i",
"'&(nbsp|#160);'i",
"'&(iexcl|#161);'i",
"'&(cent|#162);'i",
"'&(pound|#163);'i",
"'&(copy|#169);'i",
"'&#(\d );'e"); // write as php

$replace = array ("",
"",
"",
"",
"\\1",
"\"",
"&",
"<",
">",
" ",
chr(161),
chr(162),
chr(163),
chr(169),
"chr(\\1)");

return preg_replace($search, $replace, $htmlText);
}

(В такие минуты как никогда радует возможность preg_replace работать с массивами в качестве параметров). Массив вы при необходимости дополняете своими регулярками. Помочь в их составлении вам может, например, этот конструктор регулярных выражений. Начинающим разработчикам может быть полезной статья "All about HTML tags. 9 Regular Expressions to strip HTML tags". Посмотрите там примеры, проанализируйте логику.

2. От объемов.
Объемы напрямую связаны со сложностью анализа (из предыдущего пункта). Большое количество текстов увеличивает вероятность, что, пытаясь предусмотреть и почистить все регулярками, вы можете что-нибудь да упустить. В этом случае подойдет метод "многоступенчатой" очистки. То есть очистить сначала, допустим, функцией strip_tags_smart (исходники на всякий случай не удаляем). Потом выборочно просматриваем некоторое количество текстов на выявление "аномалий". Ну и "зачищаем" аномалии регулярками.

3. От того, что надо получить в результате.
Алгоритм обработки может быть упрощен разными способами в зависимости от ситуации. Случай, описанный мной в одной из предыдущих статей, хорошо это демонстрирует. Напомню, текст там находился в div-е, в котором кроме него был еще div с "хлебными крошками", реклама адсенс, список похожих статей. При анализе выборки статей обнаружилось, что статьи не содержат рисунков и просто разбиты на абзацы с помощью <p></p>. Чтобы не чистить "главный" див от посторонних вещей, можно найти все абзацы (с Simple HTML DOM Parser это очень просто) и соединить их содержимое. Так что прежде чем составлять регулярки для чистки, посмотрите, нельзя ли обойтись малой кровью.

Вообще, между сторонниками парсинга html-кода, основанного чисто на регулярных выражениях, и парсинга, в основе которого лежит анализ DOM-структуры документа, в сети разгораются настоящие холивары. Вот, например, на оверфлоу. Невинный с первого взгляда вопрос вызвал очень бурное обсуждение (особенно обратите внимание на первый коммент, за который проголосовало уже более 3 тысяч человек, — чувак отжег :) ).
В общем, каждый выбирает то, что ему ближе и что лучше подходит для конкретной ситуации.

А какой способ предпочитаете вы?
___

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

среда, 23 июня 2010 г.

3 способа установки User Agent при работе с библиотекой Simple HTML DOM Parser

Про юзерагенты на этом блоге я уже рассказывала. И говорила, что "неподставление" данных о User Agent-е в заголовок вашего запроса может выйти вам боком. Как подставить данные, если вы пользуетесь библиотекой Simple HTML DOM Parser?

Есть несколько очевидных способов.

Способ 1. Используйте Simple HTML DOM Parser в связке с cURL.
Самый простой способ. И для меня — самый удобный. Для установки юзерагента используйте параметр CURLOPT_USERAGENT.

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($cr, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)');
curl_setopt($ch, CURLOPT_TIMEOUT,5);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);

$html_curl = curl_exec($ch);
curl_close($ch);

$html = str_get_html($html_curl);



Способ 2. Можете установить браузер по умолчанию в php.ini или использовать ini_set().

Пример:
ini_set("user_agent","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");


Тоже достаточно простой и удобный способ. Первый я в основном использую из-за того, что, как правило, приходится работать с cURL-ом (и с его возможностями) при загрузке страниц, поэтому мне удобнее установить User Agent именно там. Если бы курл был не нужен - использовала бы ini_set.


Способ 3. Можете непосредственно внести изменения в функцию библиотеки load_file().
Я сама этот способ не пробовала, но в архивах на всякий случай записан этот способ, взятый с стэковерфлоу.

В исходном коде функция выглядит следующим образом:

// load html from file
function load_file() {
$args = func_get_args();
$this->load(call_user_func_array('file_get_contents', $args), true);
}


А ее модификация может быть, например, такой:

// load html from file
function load_file() {
$args = func_get_args();
// Added by Mithun
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept-language: en\r\n" .
"User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n".
"Cookie: foo=bar\r\n"
)
);
$context = stream_context_create($opts);
$args[1] = FALSE;
$args[2] = $context;
// End Mithun
$this->load(call_user_func_array('file_get_contents', $args), true);
}


Еще раз напомню, для чего вам User Agent. Допустим, вы пишете парсер, краулер или бота. Некоторые вебмастеры в robots.txt запрещают доступ к материалам сайта, если в хэдэре запроса установлен User Agent, стандартный для библиотек, работающих с http (или информация о нем вообще отсутствует). Устанавливая User Agent, вы маскируете свой запрос так, словно он поступил от браузера.

Но в любом случае надо знать меру. Если вы генерируете сотни запросов в минуту, то никакой юзерагент вас не прикроет :)
___

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

вторник, 15 июня 2010 г.

Разработка парсера каталога статей на движке WordPress с использованием PHP Simple HTML DOM Parser. Пошаговая инструкция

Всем доброго дня! Сегодня я опять отвечу на вопрос читателя блога. Вопрос был задан еще до объявления об Акции "Разобрать на моем примере", тем не менее:

Маша, помогите пожалуйста спарсить http://articlet.com/, точнее расскажите что нужно делать пошагово.


Руководство по мере написания вылилось в достаточно длинную статью, но я решила не разбивать ее на части.

Изучение ресурса и разработка алгоритма



Итак, первое, что мы сделаем, — посмотрим, что из себя представляет ресурс. Это каталог статей, сделанный на WordPress. Структура рубрикатора — двухуровневая. То есть в корне находится несколько разделов, в каждом из которых могут находиться подразделы. Доступ к подразделам можно осуществить и с главной страницы и с внутренней страницы раздела. Нам, естественно, удобнее собрать все с главной.

Статьи могут находиться как в главных разделах, так и в подразделах.

четверг, 10 июня 2010 г.

Delphi: пример поиска в структуре веб-страницы значения нужного элемента

Привет! Этот пост — ответ на вопрос читателя блога. Конкретный ответ на конкретный вопрос. Именно им я хочу положить начало акции "Разберу на вашем примере". Подробнее об акции и ее условиях читайте в конце этой статьи.


Цитата из письма:

Мне нужно с сайта http://stock.rbc.ru/demo/micex.0/intraday/eod.rus.shtml скачать хоть одну ячейку например: цену открытия акций Газпрома, и сохранить это значение в какую-нибудь БД или таблицу (например в таблицу PARADOX7) или в EXCEL (лучше PARADOX7). И все, мне больше ничего не требуется, Ваша программа будет служить мне эталонным примером, а дальше я сам разберусь.
...
Я пытаюсь работать в среде С++Builder6 и пробовал использовать компонент XMLDocument, но кроме того как вытащить его на форму ничего не могу с ним сделать. Буду рад если вы напишите мне на С++Builder, но насколько я знаю, что С++Builder и DELPHI имеют множество сходств то и программе написанной на DELPHI буду благодарен...


Случай сложен тем, что, как мне кажется, если человек не программист, то ему исходный код на другом языке вряд ли поможет. А пример "сохранения в таблицу" не научит работе с базами данных. На скорую руку набросала поиск нужного элемента на странице, постаралась прокомментировать по-максимуму, так что дополнительно сказать нечего.

Итак, код для поиска элемента:

воскресенье, 6 июня 2010 г.

User-Agent и идентификация. Взгляд с разных сторон

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

User Agent — это клиентское приложение, использующее определённый сетевой протокол. Термин обычно используется для приложений, осуществляющих доступ к веб-сайтам, таким как браузеры, поисковые роботы (и другие «пауки»), мобильные телефоны и другие устройства.
При посещении веб-сайта клиентское приложение обычно посылает веб-серверу информацию о себе. Это текстовая строка, являющаяся частью HTTP запроса, начинающаяся с User-agent: или User-Agent:, и обычно включающая такую информацию, как название и версию приложения, операционную систему компьютера и язык. У «пауков» эта строка часто содержит URL и email-адрес, по которым веб-мастер может связаться с оператором «паука».


User-Agent и вебмастер


Что дает вебмастеру и оптимизатору знание о таком понятии как User-Agent? Если знать названия поисковых роботов (и роботов различных ресурсов, краулеров и т.д.), то можно самостоятельно регламентировать доступ этих роботов к различным частям сайта. Это делается в небезызвестном файле robots.txt, который должен находиться в корневой папке сайта.

Вот как описан механизм работы робота Яндекса в зависимости от настроек в robots.txt (полная версия мануала по использованию robots.txt):

В роботе Яндекса используется сессионный принцип работы, на каждую сессию формируется определенный пул страниц, которые планирует закачать робот. Сессия начинается с закачки robots.txt сайта, если его нет, он не текстовый или на запрос робота возвращается HTTP-код отличный от '200', считается, что доступ роботу не ограничен. В самом robots.txt проверяется наличие записей, начинающихся с 'User-agent:', в них ищутся подстроки 'Yandex', либо '*' (регистр значения не имеет), причем, если обнаружено 'User-agent: Yandex', директивы для 'User-agent: *' не учитываются. Если записи 'User-agent: Yandex' и 'User-agent: *' отсутствуют, считается, что доступ роботу не ограничен.


Итак, для "управления" зоной видимости определенного робота надо добавить в robots.txt несколько строк. "Распознаются" директивы Allow и Disallow, соответственно разрешающие и запрещающие доступ роботу/краулеру/и т.д. к определенным частям вашего ресурса.

Списки названий роботов поисковиков открыты, их можно без проблем найти в интернете. Вот, например, совсем недавно, буквально месяц назад (6 мая 2010 года), на блоге Яндеска был опубликован свеженький список User-Agent-ов его роботов.

Итак, вы можете редактировать robots.txt, чтобы запретить или разрешить доступ определенных роботов к разным частям сайта. Рассмотрим конкретный случай: у Гугля есть новостной робот, но архивы вашего сайта не нуждаются в полном обходе этим роботом, поэтому пишем:

User-agent: Googlebot-News
Disallow: /archives


Если у вас чисто новостной сайт, вы можете захотеть запретить индексацию всего сайта всеми роботами Гугля кроме новостного. Тогда вам понадобится уже две группы директив:

User-agent: Googlebot
Disallow: /

User-agent: Googlebot-News
Disallow:


Первая группа определит правило для всех гуглеботов, а вторая изменит установленные ранее правила конкретно для Googlebot-News.

Помимо роботов поисковых систем по сети шарят множество всяких краулеров, собирающих контент для разных нужд. В буржунете их наблюдается больше, по крайней мере больше жалоб :) Краулеры могут поднимать нагрузки на ваш сервак на нежелательный уровень. А "палятся" некоторые из них, как вы уже, наверное, догадались, своими User-Agent-ами. В буржунете я не раз встречала целые списки юзерагентов для блокировки, выложенные в паблик. При желании вы можете их найти. Например: The top 10 spam bot user agents you MUST block. NOW. ("Десять User-agent-ов спам-ботов, которые вы ДОЛЖНЫ заблокировать. НЕМЕДЛЕННО.") Или, как вариант, можете посмотреть сайт Bots vs Browsers, на котором в момент написания статьи собрана информация о 499551 юзерагентах, из которых ботов - 4439. Или уже известный многим сайт www.user-agents.org.

User-Agents vs browsers

Информацию о юзерагентах ваших "посетителей" вы можете посмотреть в лог-файле статистики. Если увидите что-нибудь подозрительное — уже знаете, как поступить :)

Отмечу также, что для каждого движка есть отдельные наборы рекомендаций к правилам составления robots.txt для роботов поисковых систем. Прежде чем самостоятельно придумывать конфигурацию, поищите в Интернете.


User-Agent и программист парсеров :)


Постоянные читатели, наверное, в недоумении: чего это я распространяюсь о том, как защититься от ботов? Для вновь прибывших повторюсь: я сама против воровства контента и его несанкционированного использования. Но целями парсинга может быть всякое (тут уж каждому по способностям).

Итак, рассмотрю вопрос применения знаний о User-Agent-ах со стороны создателя парсеров. Все более-менее серьезные библиотеки работы с HTTP поддерживают возможность подстановки в заголовки формируемых запросов "левой" информации о браузере. Не забудьте поменять установленные по умолчанию значения на что-нибудь более правдоподобное. (Отсутствие информации о браузере подозрительно в первую очередь). У Indy, например, по умолчанию стоит

Mozilla/3.0 (compatible; Indy Library)


Ну как тут не спалиться? Это первый претендент на добавление в black-list.

libwww, lwp-trivial, curl, PHP/, urllib, GT::WWW, Snoopy, MFC_Tear_Sample, HTTP::Lite, PHPCrawl, URI::Fetch, Zend_Http_Client, http client, PECL::HTTP — все это может вас выдать с головой.

Самый простой способ, это создать файлик со всевозможными браузерами, а потом при организации запроса подставлять рандомно одну из записей. Ресурсов с базами подходящих User-Agent-ов - масса, ссылки на них я приводила в первой части статьи.
___

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

Поделиться