Извлечь информацию из html файла
Три функции, предназначенные для извлечения из HTML-документа только нужной информации. Администраторам систем Windows периодически приходится заниматься извлечением конкретной информации из документов HTML. Такой документ может быть локальным файлом, страницей состояния устройства, подключенного к локальной сети, отчетом из базы данных, доступной через Web, или любой другой из тысячи типов страниц. В каждом из этих случаев мы обычно сталкиваемся с двумя проблемами использования данных. Первая из них состоит в подключении к нужной Web-странице и считывании содержащихся в ней данных.
Три функции, предназначенные для извлечения из HTML-документа только нужной информации
Администраторам систем Windows периодически приходится заниматься извлечением конкретной информации из документов HTML. Такой документ может быть локальным файлом, страницей состояния устройства, подключенного к локальной сети, отчетом из базы данных, доступной через Web, или любой другой из тысячи типов страниц. В каждом из этих случаев мы обычно сталкиваемся с двумя проблемами использования данных. Первая из них состоит в подключении к нужной Web-странице и считывании содержащихся в ней данных. Дело в том, что если страница не является статическим файлом, доступным через какие-либо разделяемые ресурсы Windows или файловую систему в нашей сети, то мы не можем считывать ее содержимое, используя такие стандартные средства, как, например, объект Scripting.FileSystemObject. Кроме того, для доступа к устройству, обслуживающему интересующую Web-страницу, может потребоваться указать соответствующие имя пользователя и пароль. При этом после решения указанной проблемы мы сталкиваемся с еще более сложной задачей: как извлечь интересующую информацию из практически "сырого" (raw) HTML?
Обе проблемы могут быть решены с помощью стандартных компонентов Windows Script Host (WSH). Я представлю общую идею на примере демонстрационного сценария, с помощью которого из DSL-маршрутизатора извлекается Web-страница, из которой, в свою очередь, извлекаются данные о публичном (public) IP-адресе маршрутизатора. Затем из этого процесса будут выделены три универсальных функции, которые в дальнейшем можно использовать для извлечения информации из страниц различных типов.
Обращаю ваше внимание, что в данной статье словам "информация" и "данные" придаются разные значения. Под "информацией" здесь следует понимать те полезные сведения, которые нужно извлечь - в данном случае это сведения о публичном IP-адресе маршрутизатора. Если же используется термин "данные", то речь идет о содержимом страницы в "сыром" виде, которое содержит как "информацию", так и ненужный материал, который должен быть отсеян.
Извлечение данных
В состав Windows 2000 и последующих версий включены разнообразные компоненты, которые могут использоваться для извлечения Web-данных. Более ранние 32-разрядные версии Windows обычно имеют такую функциональность только в тех случаях, когда на них установлены обновления и пакеты Feature Pack начиная с 1999 года. Наиболее очевидный инструмент для извлечения данных из Web-страниц - Microsoft Internet Explorer (IE), но лично я его не использую. Конечно, существует много примеров применения IE в сценариях, и, тем не менее, он разработан в первую очередь для интерактивного отображения материала. В частности, проблемы появляются при использовании IE в неграфических сеансах; IE может стать причиной возникновения ошибок, блокирующих ход выполнения, а при работе с удаленными данными произвольного содержания весьма вероятны побочные эффекты. Следует также отметить, что поскольку IE автоматически обрабатывает и отображает дополнительный материал (в частности, встроенные изображения), это существенно замедляет его работу.
Здесь method - строковый аргумент, описывающий тип выполняемого запроса. В случае HTTP-соединений он обычно имеет значение "GET". Аргумент url также является строковым параметром, в котором, в случае обращения к удаленным данным, должен полностью определяться корректный путь URL. При работе с локальными файлами данный аргумент тоже используется, но в этом случае он представляет собой полный путь к файлу, с которым предполагается работать (при этом префикс file:// в описании пути использовать не нужно).
Необязательный аргумент async служит для того, чтобы указать запросчику, должен ли он ожидать отклика (синхронный режим, ему соответствует значение False) или переходить к следующей строке кода сразу после отправки запроса (асинхронный режим - значение True). Если данный аргумент пропущен, то по умолчанию берется значение True, но в рассматриваемом примере это недопустимо, так как после отправки запроса необходимо, чтобы сценарий дождался ответа, поскольку в сценарии сразу вслед за отправкой запроса выполняется обработка его результатов. Итак, в данном случае должно быть задано значение False.
Следующие два аргумента будут полезны в случае обращения к ресурсам с ограниченным доступом, но жестко задавать их в сценариях следует с осторожностью. Если для доступа к ресурсу должны указываться имя пользователя и пароль, то они могут быть заданы, соответственно, в виде аргументов user и pass , что позволяет избежать ошибки аутентификации при выполнении запроса. Но если для доступа к интересующему ресурсу аутентификация не требуется, тогда эти аргументы задавать не нужно, или можно указать пустую строку - либо с помощью vbNullString, либо через символ двойных кавычек (""). Теперь у нас есть вся необходимая информация для установки соединения, и мы добавляем в сценарий следующую строку:
Таким образом, мы сконфигурировали и установили соединение, но сам запрос еще не сделали. Для этого воспользуемся методом send :
По окончании выполнения запроса происходит обращение к полученным данным, для чего используется свойство responseText запросчика:
Теперь вся страница целиком стала доступна в качестве данных для нашего сценария. Надо сказать, что все мои пояснения были довольно пространными, однако сам код, реализующий эти процедуры, состоит всего из четырех строк. Причем ближе к окончанию статьи данный код будет встроен в функцию, после чего станет понятно, как можно вызывать его с помощью одной строки.
Итак, данные получены, но это еще не все. Напомню, что нас интересует только информация об IP-адресе. Если же взглянуть на страницу конфигурации, отображаемую в окне браузера, мы увидим около дюжины строк текста, из которых только пять фрагментов содержат собственно данные, в том числе и IP-адрес, представленный следующим образом:
Видно, что в полученных в результате выполнения запроса данных присутствует значительный уровень "шума", соответственно, для выделения интересующей нас информации этот шум должен быть отфильтрован. Иначе говоря, мы имеем более 200 строк текста и примерно 1250 слов, и во всей этой массе требуется найти только то слово, которое нас интересует.
Фильтрация шумов
Было бы хорошо иметь механизм для быстрого отсеивания этих шумов. В принципе, тэги HTML и XML можно отфильтровать, загрузив полученные данные в объект XmlDocument или HtmlDocument, после чего выполнить запрос, возвращающий только текст документа. Однако если данные не вполне соответствуют требованиям используемого объекта, это может привести к проблемам.
В целом, на мой взгляд, в данном подходе больше риска, чем преимуществ, и вот почему. Объект XmlDocument может работать с конфигурационной страницей нашего маршрутизатора, но если она не будет иметь полностью корректный формат документа XML, то процесс обработки даст сбой. Что же касается объекта HtmlDocument, то, в принципе, он предоставляет прекрасные возможности фильтрации нежелательного содержимого, но с ним связан и значительный риск, поскольку работа нашего сценария будет прервана. Если страница содержит нерегулярные данные - причем это может быть и такое содержимое, которое без ошибок обрабатывается в IE - то в этом случае объект HtmlDocument может сгенерировать окно ошибки, блокирующее интерфейс. Причем сам сценарий может, тем не менее, продолжить работу на данной странице с виртуальным документом, что в результате приведет к нестабильной работе.
На мой взгляд, наилучший способ состоит в использовании регулярных выражений. С помощью короткого регулярного выражения можно получить сведения об IP-адресе, хотя может показаться предпочтительным задействовать объект XmlDocument. Но это было бы не совсем корректно, поскольку данный способ, наиболее удобный для описываемого случая, совершенно бесполезен для других задач. В то же время, фильтрация стандартных Web-данных с помощью регулярных выражений позволит привести их к такому виду, при котором с помощью более простого регулярного выражения или обычного поиска строковых значений можно будет быстро найти интересующую нас информацию.
Итак, сначала требуется создать ссылку на механизм работы с регулярными выражениями в VBScript. Поскольку интересующий нас фрагмент может быть обнаружен в нескольких строках извлеченных данных, мы должны задействовать свойства Multiline и Global:
Первый шаг в процессе выделения целевой информации состоит в удалении из общего объема данных всех тэгов HTML. Поскольку тэги HTML и XML всегда начинаются и заканчиваются символами угловых скобок (<>), их можно легко идентифицировать, применив следующий шаблон (pattern):
Те, кто имеет опыт работы с регулярными выражениями, уже поняли, что этот шаблон определяет поиск строк, начинающихся символом " ". Однако многих может сбить с толку часть выражения, находящаяся внутри угловых скобок (т.е. [^>]+). Когда в выражении используется символ вставки (^), заключенный между символами набора знаков ([) и (]), это предписывает выявлять совпадения с любыми символами, не входящими в данный набор. Знак плюс (+) более привычен для тех, кто пользуется регулярными выражениями; он означает выявление совпадений с последовательностью из одного или более знаков из данного набора . Следовательно, выражение [^>]+ обозначает выявление совпадения с любым символом, кроме символа > . Таким образом, полное описание шаблона ]+> определяет выявление любых последовательностей, начинающихся с символа , но не содержащих символ >.
После того как все тэги были выявлены, остается просто удалить их. Для этого используем регулярное выражение в режиме замены, подставляя вместо тэгов пустые строки:
Если взглянуть на стандартную Web-страницу после выполнения этой операции, заметим, что на ней осталось значительное количество данных, причем большая их часть является пустыми, т.е. комбинацией символов табуляции, стандартных пробелов и символов окончания строки. Также встречаются и специальные символы HTML. И хотя в рассматриваемом здесь примере Web-страницы конфигурации маршрутизатора их наличие не является принципиальным, тем не менее, имеет смысл очистить страницу от одного из этих символов, поскольку это позволит упростить последующую процедуру поиска: речь идет о специальном символе неразрывного пробела, обычно обозначаемом на Web-страницах как " ". В данном случае для замены этого символа можно снова воспользоваться возможностями регулярных выражений, но проще использовать функцию замены символов из VBScript. С помощью приведенной ниже команды каждый из этих спецсимволов будет заменен символом пробела:
Заключительный шаг общей процедуры очистки состоит в том, чтобы преобразовать все встречающиеся в наших данных последовательности пустых знаков в единичные символы пробелов. Это нужно для того, чтобы в процессе поиска строки не приходилось определять, сколько непечатаемых символов (и какого типа) отделяют одно слово от другого. Для преобразования пустот в единичные символы пробелов зададим новый шаблон регулярного выражения, обеспечивающий поиск совпадений с одним или более пустым символом любого типа:
Затем заменим каждое из найденных совпадений символом единичного пробела:
Значимая информация
Большая часть оставшейся работы будет специфичной для каждой конкретной Web-страницы, из которой требуется извлечь информацию, однако в большинстве случаев она может быть выполнена с помощью двух-трех строчек кода. Большинство задач сетевого мониторинга и управления по сути сводятся к повторяющимся процедурам доступа к нескольким страницам, поэтому незначительные временные затраты, необходимые для создания программного инструмента для извлечения нужной информации в дальнейшем быстро оправдают себя.
На Рис. 1 показаны те данные, которые остаются после очистки страницы конфигурации маршрутизатора от тэгов HTML и дополнительных пробелов. Здесь приводится не вся страница целиком - из соображений компактности часть материала отсечена - однако тот прием, который будет использован для извлечения информации, столь же успешно работает и с наборами данных большего объема. Суть данного подхода состоит в том, чтобы просмотреть данные, которые находятся непосредственно перед теми, которые нас интересуют, и выделить из них фрагмент минимальной длины, который есть на данной странице, но встречается только один раз.
Рассмотрим, например, словосочетание "Internet Address: " (обратите внимание, что в данном примере адрес имеет заведомо некорректное значение 256.261.381.125). Непосредственно перед значением адреса находятся символы двоеточия и пробела, но данный фрагмент не является на рассматриваемой странице уникальным. Если мы передвинемся по тексту назад, то получим набор символов "Address: ", который опять-таки не уникален. А вот словосочетание "Internet Address: " встречается на странице только один раз. Соответственно, если расщепить данные по разделителю "Internet Address: ", то получится массив, состоящий из двух строковых элементов, один из которых будет содержать текст, расположенный до разделителя "Internet Address: ", а другой - текст после разделителя. В данном случае нас интересует второй элемент, который имеет индекс 1, поэтому поступим следующим образом:
Теперь наша задача проста. По тексту сразу после интересующей нас информации следует символ пробела, а поскольку в самом значении IP-адреса пробелов быть не может, можно расщепить данные по символу пробела. Сохраним первую строку (она имеет индекс 0) из сформированного выше с помощью оператора Split массива с помощью приведенного ниже оператора:
и расположим ее слева от значения 256.261.381.125.
Чтобы убедиться в том, что данная методика пригодна для повторного использования, взгляните на Листинг 1. Каждый значимый этап обработки данных - получение данных страницы, удаление тэгов и дополнительных пробелов, локализация интересующей строковой информации - здесь реализован в виде отдельной функции (см. фрагмент с меткой B). После определения этих функций сам процесс извлечения из страницы информации об IP-адресе сводится к трем строкам кода, что иллюстрирует фрагмент с меткой A.
Если просмотреть приведенный ниже код, увидим, что в функции GetSubString используется встроенная функция Trim языка VBScript, хотя непосредственно в нашем примере она не использовалась. Функция Trim служит для удаления пробелов в начале и в конце той текстовой строки, к которой она применяется. Я включил ее в функцию GetSubString для того, чтобы можно было не принимать во внимание возможные пробелы в начале и в конце тех строк, которые используются для извлечения данных.
Наличие этих функций позволит упростить процесс адаптации данного сценария под конкретные задачи. В частности, если вы имеете опыт извлечения нужного материала из страниц с помощью регулярных выражений и предпочитаете делать это каким-либо привычным способом, тогда вы можете пользоваться только функцией GetWebXml для извлечения данных, а потом обрабатывать их нужным образом. Если же вы предпочитаете использовать готовые решения, но имеете некоторые специфические требования (например, необходимо извлечь не одну, а несколько строк), то в этом случае можно задействовать функцию CleanTaggedText и игнорировать функцию GetSubString. И, наконец, если из всей страницы требуется извлечь только одну строку и получить соответствующий уникальный текст перед самим значением и после него, тогда можно использовать все три функции - так, как это делаю я.
В предыдущей статье мы рассмотрели основные понятия и термины в рамках технологии Data Mining. Сегодня более детально остановимся на Web Mining и подходах к извлечению данных из веб-ресурсов.
- Анализ DOM дерева, использование XPath.
- Парсинг строк.
- Использование регулярных выражений.
- XML парсинг.
- Визуальный подход.
Анализ DOM дерева
Этот подход основывается на анализе DOM дерева. Используя этот подход, данные можно получить напрямую по идентификатору, имени или других атрибутов элемента дерева (таким элементом может служить параграф, таблица, блок и т.д.). Кроме того, если элемент не обозначен каким-либо идентификатором, то к нему можно добраться по некоему уникальному пути, спускаясь вниз по DOM дереву, например:
body -> p[10] -> a[1] -> текст ссылки
или пройтись по коллекции однотипных элементов, например:
body -> links -> 5 элемент -> текст ссылки
- можно получить данные любого типа и любого уровня сложности
- зная расположение элемента, можно получить его значение, прописав путь к нему
- различные HTML / JavaScript движки по-разному генерируют DOM дерево, поэтому нужно привязываться к конкретному движку
- путь элемента может измениться, поэтому, как правило, такие парсеры рассчитаны на кратковременный период сбора данных
- DOM-путь может быть сложный и не всегда однозначный
Data Extracting SDK использует Microsoft.mshtml для анализа DOM дерева, но является «надстройкой» над библиотекой для удобства работы:
UriHtmlProcessor proc = new UriHtmlProcessor( new Uri ( "http://habrahabr.ru/new/page1/" ));
proc.Initialize();
* This source code was highlighted with Source Code Highlighter .
Следующим эволюционным этапом анализа DOM дерева является использования XPath — т.е. путей, которые широко используются при парсинге XML данных. Суть данного подхода в том, чтобы с помощью некоторого простого синтаксиса описывать путь к элементу без необходимости постепенного движения вниз по DOM дереву. Данный подход использует всеми известная библиотека jQuery и библиотека HtmlAgilityPack:
HtmlDocument doc = new HtmlDocument();
doc.Load( "file.htm" );
foreach (HtmlNode link in doc.DocumentElement.SelectNodes( "//a[@href" ])
HtmlAttribute att = link[ "href" ];
att.Value = FixLink(att);
>
doc.Save( "file.htm" );
* This source code was highlighted with Source Code Highlighter .
Парсинг строк
Несмотря на то, что этот подход нельзя применять для написания серьезных парсеров, я о нем немного расскажу.
Иногда данные отображаются с помощью некоторого шаблона (например, таблица характеристик мобильного телефона), когда значения параметров стандартные, а меняются только их значения. В таком случае данные могут быть получены без анализа DOM дерева, а путем парсинга строк, например, как это сделано в Data Extracting SDK:
Компания: Microsoft
Штаб-квартира: Редмонд
string data = "Компания: Microsoft
Штаб-квартира: Редмонд
" ;
string company = data.GetHtmlString( "Компания: " , "" );
string location = data.GetHtmlString( "Штаб-квартира: " , "" );
// output
// company = "Microsoft"
// location = "Редмонт"
* This source code was highlighted with Source Code Highlighter .
Использование набора методов для анализа строк иногда (чаще — простых шаблонных случаях) более эффективный чем анализ DOM дерева или XPath.
Регулярные выражения и парсинг XML
Очень часто видел, когда HTML полностью парсили с помощью регулярных выражений. Это в корне неверный подход, так как таким образом можно получить больше проблем, чем пользы.
Регулярные выражения необходимо использоваться только для извлечения данных, которые имеют строгий формат — электронные адреса, телефоны и т.д., в редких случаях — адреса, шаблонные данные.
Еще одним неэффективным подходом является рассматривать HTML как XML данные. Причина в том, что HTML редко бывает валидным, т.е. таким, что его можно рассматривать как XML данные. Библиотеки, реализовавшие такой подход, больше времени уделяли преобразованию HTML в XML и уже потом непосредственно парсингу данных. Поэтому лучше избегайте этот подход.
Визуальный подход
В данный момент визуальный подход находится на начальной стадии развития. Суть подхода в том, чтобы пользователь мог без использования программного языка или API «настроить» систему для получения нужных данных любой сложности и вложенности. О чем-то похожем (правда применимым в другой области) — методах анализа веб-страниц на уровне информационных блоков, я уже писал. Думаю, что парсеры будущего будут именно визуальными.
Проблемы и общие рекомендации
Проблемы при парсинге HTML данных — использование JavaScript / AJAX / асинхронных загрузок очень усложняют написание парсеров; различные движки для рендеринга HTML могут выдавать разные DOM дерева (кроме того, движки могут иметь баги, которые потом влияют на результаты работы парсеров); большие объемы данных требуют писать распределенные парсеры, что влечет за собой дополнительные затраты на синхронизацию.
Нельзя однозначно выделить подход, который будет 100% применим во всех случаях, поэтому современные библиотеки для парсинга HTML данных, как правило, комбинируют, разные подходы. Например, HtmlAgilityPack позволяет анализировать DOM дерево (использовать XPath), а также с недавних пор поддерживается технология Linq to XML. Data Extracting SDK использует анализ DOM дерева, содержит набор дополнительных методов для парсинга строк, а аткже позволяет использовать технологию Linq для запросов в DOM модели страницы.
На сегодня абсолютным лидером для парсинга HTML данных для дотнетчиков является библиотека HtmlAgilityPack, но ради интереса можно посмотреть и на другие библиотеки.
Я хотел бы извлечь текст из HTML-файла с помощью Python. Я хочу получить тот же результат, что и при копировании текста из браузера и вставке его в блокнот.
Я хотел бы что-то более надежное, чем использование регулярных выражений, которые могут не работать на плохо сформированном HTML. Я видел, как многие люди рекомендуют красивый суп, но у меня было несколько проблем с его использованием. Во-первых, он взял нежелательный текст, такой как источник JavaScript. Кроме того, он не интерпретировал объекты HTML. Например, я ожидал бы, что " in HTML source будет преобразован в Апостроф в тексте, как если бы я вставил содержимое браузера в блокнот.
обновление html2text выглядит многообещающе. Он правильно обрабатывает HTML-объекты и игнорирует JavaScript. Однако это не совсем простой текст; он создает уценку, которая затем должна быть превращена в обычный текст. В нем нет примеров или документации, но код выглядит чистым.
- отфильтровать HTML-теги и разрешить объекты в python
- преобразование объектов XML / HTML в строку Unicode в Python
html2text Это программа Python, которая делает довольно хорошую работу в этом.
лучший кусок кода, который я нашел для извлечения текста без получения javascript или не хотел вещей:
вам просто нужно установить BeautifulSoup перед :
Примечание: NTLK больше не поддерживает clean_html функции
оригинальный ответ ниже и альтернатива в разделах комментариев.
Я потратил свои 4-5 часов, исправляя проблемы с html2text. К счастью, я мог столкнуться с НЛТК.
Это работает волшебно.
сегодня я столкнулся с той же проблемой. Я написал очень простой парсер HTML, чтобы очистить входящее содержимое от всех наценок, возвращая оставшийся текст с минимальным форматированием.
вот версия ответа xperroni, которая немного более полная. Он пропускает разделы сценария и стиля и переводит charrefs (например, ') и HTML-объекты (например, &).
Он также включает в себя тривиальный преобразователь простого текста в html.
вы также можете использовать метод html2text в библиотеке stripogram.
для установки stripogram запустите sudo easy_install stripogram
существует библиотека шаблонов для интеллектуального анализа данных.
вы даже можете решить, какие теги сохранять:
PyParsing делает большую работу. Pyparsing wiki был убит, поэтому вот еще одно место, где есть примеры использования PyParsing (пример ссылки). Одна из причин, по которой он потратил немного времени на pyparsing, заключается в том, что он также написал очень краткое, очень хорошо организованное руководство O'Reilly Short Cut, которое также является недорогим.
Исходный код страницы
Для начала, я хочу очень кратко рассказать про то, из чего состоит веб-страница. Вы можете открыть любой сайт, например мой блог и нажать Ctrl+U (или правой кнопкой на странице и выбрать «Исходный код»). Вы увидите код, это обычный текст в формате HTML, который браузер преобразует в веб-страницу. Ключевые слова, заключенные в «<>» — называются тегами, свойства внутри них — атрибутами и значениями атрибута. Все это нам пригодится, чтобы отыскивать необходимые теги с нужной информацией.
Пример:
Что же делает браузер с этим кодом? Браузер, руководствуясь этим кодом, располагает информацию на странице в указанном порядке, подгружает таблицы стилей css, которые добавляют оформление элементам, подгружает и вставляет картинки, скачивает и выполняет различные скрипты. Правила, по которым браузер это делает — называются спецификацией, которая необходима, чтобы все браузеры отрисовывали страницы и обрабатывали скрипты одинаково.
Методы извлечения информации из тегов средствами Javascript
Код на JS(test.js):
Здесь мы использовали поиск элемента по тегу. На самом деле, метод querySelector принимает в качестве параметра любое выражение в формате css-правила. Еще важно знать, что querySelector возвращает только первый элемент из DOM-дерева, который совпал. Чтобы получить все элементы, подходящие под указанное правило, используйте querySelectorAll .
Приведу несколько примеров:
Вы можете получать список всех элементов, подходящих под заданное правила, используйте для этого querySelectorAll() — он всегда возвращает коллекцию элементов либо null.
Давайте выведем все ссылки в консоль.
Тут мы видим набор элементов, а если быть точнее, то коллекцию узлов DOM-дерева. Квадратные скобки могут натолкнуть на мысль о том, что это обычный массив, но это не совсем так. Мы не сможем для такой коллекции использовать стандартные методы массива, такие, как .map(), .filter() и крайне удобный .forEach() . Поэтому для перебора и обработки элементов такой коллекции мы будем использовать классический цикл for .
В коллекции 6 элементов, вы можете нажать на любой и тогда откроется инспектор DOM-дерева и вы увидите элемент, на который нажали.
Хоть это и неполноценный массив, но нам все равно доступно свойство length . Давайте посчитаем количество ссылок в коллекции и выведем для каждой ссылки заголовок и адрес, куда она ссылается. В JS есть несколько вариантов реализации циклов, мы пока используем самый простой for .
Выводим все ссылки на странице и информацию о них:
Этот код покажет в консоли количество найденных элементов и выведет информацию о каждой ссылке на странице. Это базовый пример, позже вы научитесь создавать, изменять, фильтровать массивы полученной информации, сохранять в каком угодно виде и разумеется считывать из файлов и использовать данные в своих программах.
Помимо получения значений аттрибутов при помощи метода getAttribute() , текста внутри тега при помощи innerHTML, мы можем делать разные другие вещи. Чтобы посмотреть доступные методы, вы можете поискать в документации по JS, или воспользоваться автокомплитом из консоли, попробуйте вручную ввести window.document.querySelectorAll("a")[0]. или window.document.querySelector("a"). , что будет равнозначно, и после ввода точки появится список поддерживаемых методов и свойств, более подробно о каждом свойстве или методе вы легко найдете в гугле. Подобным образом вы сможете исследовать методы и свойства любого объекта.
И еще, вы можете указывать несколько селекторов через запятую, например window.document.querySelectorAll("a, .styled")
Есть и другие методы для получения нужных нам тегов и извлечения из них различных свойств и атрибутов, но они не особо актуальны, потому что querySelectorAll — это метод, который поддерживают все современные браузеры, обладает куда большим функционалом и у нас нет необходимости использовать устаревшие методы, вроде window.document.getElementById() , window.document.getElementsByClassName() , window.document.getElementsByTagName() и прочие.
Также, для мощного поиска по тексту веб-страницы, можно использовать регулярные выражения, инструмент невиданной силы и мощи, который заслуживает отдельной книги страниц на 600, о нем я расскажу в следующих уроках.
обновление
основываясь на комментарии Фрейзера, вот более элегантное решение:
таким образом, вы можете сделать что-то вроде:
вместо модуля HTMLParser проверьте htmllib. Он имеет аналогичный интерфейс, но делает больше работы для вас. (Он довольно древний, поэтому это не очень помогает с точки зрения избавления от javascript и css. Вы можете создать производный класс, но и добавить методы с именами, такими как start_script и end_style (см. документы python для деталей), но это трудно сделать надежно для искаженного html.) Во всяком случае, вот что-то простое, что печатает простой текст на консоль
Если вам нужно больше скорости и меньше точности, то вы можете использовать raw lxml.
установить html2text используя
красивый суп преобразует HTML-объекты. Это, вероятно, ваш лучший выбор, учитывая, что HTML часто глючит и заполнен проблемами кодирования unicode и html. Это код, который я использую для преобразования html в необработанный текст:
Я рекомендую пакет Python под названием goose-extractor Гусь попытается извлечь следующую информацию:
основной текст статьи Основное изображение статьи Любые фильмы Youtube/Vimeo, встроенные в статью метаописание Мета теги
другой вариант-запустить html через текстовый веб-браузер и сбросить его. Например (с помощью Lynx):
Это можно сделать в скрипте python следующим образом:
Он не даст вам точно только текст из HTML-файла, но в зависимости от вашего варианта использования он может быть предпочтительнее вывода html2text.
другое решение, отличное от python: Libre Office:
причина, по которой я предпочитаю этот вариант другим альтернативам, заключается в том, что каждый HTML-абзац преобразуется в одну текстовую строку (без разрывов строк), что я и искал. Другие методы требуют последующей обработки. Lynx производит хороший результат, но не совсем то, что я искал. Кроме того, Libre Office можно использовать для конвертации из любых форматов.
Если у вас уже есть HTML-файлы, загруженные вами можете сделать что-то вроде этого:
В статье рассматривается реализация разбора структурированных текстов на примере анализа HTML-страниц средствами PHP
Разбор структурированного текста может быть осуществлен с помощью регулярных выражений или программных библиотек, предназначенных для восстановления его древовидной структуры. Но регулярные выражения для сложных текстов теряют наглядность, их становится трудно воспринимать и отлаживать. Построение структурного дерева текста требует больших затрат памяти и является излишним для многих практических задач.
При извлечении информации из HTML-страниц реальных интернет-сайтов можно столкнуться с ошибками верстки, которые не критичны для отображения этих страниц в браузере, но приводят к неожиданным эффектам при построении структурного дерева документа. Веб-мастера могут вносить на сайт незначительные изменения, помещать сведения в дополнительные контейнеры или, наоборот, уменьшать степень вложенности текстов. Желательно минимизировать влияние таких преобразований структуры документов на работу программы-анализатора, извлекающей информацию с сайта.
Продемонстрируем извлечение информации из HTML-файла комбинированным способом |
В этой статье будет продемонстрировано, что построение собственного анализатора позволяет записать решение задачи анализа в императивных терминах, сохраняя при этом контроль над количеством используемых ресурсов – вычислительной мощности и оперативной памяти.
В общем виде анализатор текста должен состоять из трех модулей [1]:
- Лексический анализатор. На вход лексического анализатора подается исходный текст в виде последовательности символов – букв, цифр, скобок и знаков препинания. На выход лексического анализатора выдается последовательность лексем – слов, чисел, строк.
- Синтаксический анализатор. Принимает последовательность лексем от лексического анализатора, проверяет на соответствие формальным правилам и составляет на их основе таблицы идентификаторов и констант.
- Семантический анализатор использует результаты работы лексического и синтаксического анализаторов для окончательного решения задачи анализа текста, представляет собранную информацию в удобной для дальнейшего использования форме.
При разработке анализатора HTML-документов постараемся воспроизвести эту эталонную систему.
Рубрики
Исследование различных веб-технологий, математических алгоритмов и проектирование веб-приложений.
О кодировании текстов
При обработке текстов нельзя игнорировать кодировку, в которой они представлены. Актуальные в настоящее время кодировки можно разделить на два класса: многобайтовые (UTF-8, UTF-16, UTF-32), представляющие полное множество символов UNICODE, и однобайтовые (ASCII, КОИ-8, CP866, Windows-1251), в которых представлены подмножества из 256 выбранных символов.
В общем случае для обработки текстов, представленных в многобайтовых кодировках, нужно использовать специальные функции языка программирования, учитывающие их особенность. Очевидно, что даже длина строки в многобайтовой кодировке будет отличаться от значения, которое вернет «обычная» функция, подсчитывающая количество байтов.
В языке программирования PHP функции для обработки текстов в многобайтовых кодировках начинаются с префикса «mb_»: mb_strlen (), mb_strpos (), mb_strstr () и т.д. Они выполняются несколько медленнее своих однобайтовых аналогов, потому что для правильной работы им нужно выполнять фоновый анализ встречающихся символов. Впрочем, в интерпретируемых языках программирования этим замедлением можно смело пренебречь.
В подпрограммах, описанных в этой статье, используются обычные однобайтовые функции. Такое решение принято по той причине, что в подавляющем большинстве случаев структура анализируемых текстов (документов HTML и XML, новостных лент RSS, информационных хранилищ JSON) определяется фразами из символов, входящих в подмножество первых 127 символов кодовой таблицы ASCII. А завоевавшая широкое распространение кодировка UTF-8 использует в своей основе префиксное кодирование [2], благодаря которому между кодами первых 127 символов кодировок ASCII и UTF-8 установлено взаимно однозначное соответствие. Многобайтовые символы UNICODE в кодировке UTF-8 содержат в своей записи только байты со значениями от 127 до 255.
Описанные в статье подпрограммы можно смело использовать для обработки текстов в однобайтовых кодировках и кодировке UTF-8. Они будут корректно определять структурные элементы текста, записанные младшими 127 символами, не затрагивая его информационное наполнение, которое в свою очередь может быть произвольным.
Мы продемонстрировали извлечение информации из HTML-файла комбинированным способом, который включает:
- прямой поиск начала интересующего участка HTML-кода с помощью стандартной функции PHP strpos ();
- последующий сбор данных путем анализа структуры HTML-кода с помощью разработанных для этой цели функций лексического и синтаксического анализа текста.
Такой подход позволяет сочетать быструю локализацию области поиска благодаря однократному вызову встроенной функции и гибкость при выполнении анализа текста специально разработанными на языке PHP для этой цели средствами. В большинстве случаев он не имеет недостатков по сравнению с построением структурного дерева HTML-документа. А кроме того не требует установки сторонних библиотек и при необходимости может быть дополнен средствами работы с регулярными выражениями.
Задача извлечения информации из HTML-страницы, рассмотренная в этой статье, гораздо проще проекта разработки компилятора для какого-нибудь языка программирования. Но следование общим принципам построения анализатора позволило сделать программу обозримой и легко адаптируемой к возможным изменениям в дизайне анализируемого сайта. Разработанные вспомогательные функции можно применять для анализа текстов на других похожих языках – XML, RSS, JSON.
Включение в стандартную библиотеку интерпретируемых языков программирования функций SkipOver (текст, набор_символов [,начальная_позиция]) и SkipTo (текст, набор_символов [,начальная_позиция]) позволило бы существенно повысить эффективность реализаций на этих языках программ для обработки текстовой информации.
Определение элементов структуры текста
Начнем с лексического анализа. При его выполнении исходный текст последовательно просматривается от начала к концу с попутным выделением структурных элементов. В ходе анализа HTML-текста основной задачей является определение в нем тегов – участков текста, заключенных между угловыми скобками «» (знак больше). В свою очередь, теги состоят из имени (слова, следующего сразу за знаком «<») и атрибутов, которые тоже могут иметь значения.
Например, в программе анализатора часто встречается такой код:
- Пропустить символы-разделители «\040\t\r\n» (пробел, табуляция, возврат каретки и перевод строки) и записать новую позицию в переменную P1.
- Если символ в позиции P1 – цифра, то обнаружено число. Для его выделения нужно пропустить цифровые символы «0. 9» и записать новую позицию в переменную P2.
- Если символ в позиции P1 – буква, то обнаружен идентификатор. Для его выделения нужно пропустить алфавитно-цифровые символы «0..9A. Za. z» и записать новую позицию в переменную P2.
- Если символ в позиции P1 – открывающая кавычка, то обнаружено начало строки. Для нахождения позиции конца строки нужно пропустить все символы до тех пор, пока не встретится закрывающая кавычка. Позицию конца строки нужно записать в переменную P2, а значение переменной P1 увеличить на единицу, чтобы оно указывало на начало строки, а не на открывающую кавычку.
Такой подход к анализу текста очень экономичен в плане расхода памяти. Для хранения информации об элементах текста достаточно двух числовых переменных P1 и P2, в которые записываются позиции начала элемента и символа, следующего за его концом. Значение элемента хранится неявно в анализируемом тексте и при необходимости может быть легко получено из него как подстрока [P1:P2-1]. При этом легко вычисляется длина элемента L = P2 – P1, и она может быть оценена до обработки значения элемента. Например, можно сразу сообщить об ошибке, если длина элемента превышает допустимую.
Обобщая описанную схему, в процессе лексического анализа можно обнаружить регулярное выполнение двух действий (см. рис. 1):
- поиск в тексте позиции определенного символа, который служит признаком начала синтаксической конструкции или является символом-ограничителем;
- пропуск символов из заданного набора (символов-разделителей или алфавитно-цифровых символов для записи идентификаторов) до тех пор, пока не встретится «посторонний» символ.
Рисунок 1. Шаги лексического анализа тега HTML. Зеленая стрелка – переход к следующему символу, голубая стрелка – пропуск символов из набора, оранжевая – поиск символа из набора
Поэтому в основу разрабатываемого лексического анализатора были положены две взаимно дополняющие функции: SkipOver (…, набор_символов) и SkipTo (…, набор_символов). Первая служит для пропуска любых символов, не входящих в указанный набор, и возврата позиции символа, который в этот набор входит. Вторая функция выполняет поиск позиции символа, который совпадает с одним из символов указанного набора. Исходный текст на языке PHP этих функций приведен в файле iaparse r.php .
Особенностью интерпретируемых языков программирования, к каковым относится PHP, является то, что время работы участков кода сильно зависит от баланса между использованием в них встроенных функций языка и языковых конструкций. Поскольку встроенные функции являются скомпилированными блоками машинного кода, то они выполняются существенно быстрее, чем эквивалентные им конструкции, записанные операторами языка. Например, поиск символа C в строке S, записанный с помощью встроенной функции strpos ():
будет выполнен значительно быстрее, нежели реализованный средствами PHP:
Хотя на компилируемых языках оба варианта отработали бы примерно за одинаковое время. Так происходит потому, что конструкции языка PHP не выполняются процессором напрямую, а проходят через интерпретатор, который на каждой итерации цикла осуществляет вызов машинных микроподпрограмм для выполнения своих операторов. Затраты на организацию множественных вызовов могут привести к катастрофическому падению производительности.
Для повышения эффективности в реализациях часто вызываемых функций SkipOver () и SkipTo () включен «защитный код», имеющий вид:
Он обеспечивает определение длины обрабатываемой строки через вызов встроенной функции strlen (), только если ее значение не было передано через необязательный параметр $l. В противном случае сразу используется предоставленное значение.
Несмотря на то что исходные тексты функций SkipOver () и SkipTo () отличаются единственным символом (в первой содержится выражение с неравенством «!==», а во второй – с равенством «==»), что позволяет объединить их в одну функцию Skip () с дополнительным параметром, задающим специфику поведения, было решено это не делать для сохранения наглядности разрабатываемых с их использованием программ.
К сожалению, разработчики интерпретаторов не посчитали нужным включить в стандартную библиотеку функции для упрощения лексического анализа. И если в PHP5 появляется функция strpbrk (), которая подходит для решения первой задачи, то для пропуска разделителей по-прежнему придется написать ресурсозатратный код на PHP.
Еще один момент, на который следует обратить внимание для повышения эффективности программы, – это передача параметров в функцию. Если функция вызывается многократно для обработки большого объема текста, как например это происходит с функциями SkipOver () и SkipTo (), то передача в нее этого текста должна осуществляться по ссылке, а не по значению.
Для передачи параметра по ссылке в языке программирования PHP в заголовке объявления функции нужно предварить название параметра символом «&» (амперсанд). Если забыть это сделать, то при каждом вызове функции будет осуществляться копирование передаваемого значения (в нашем случае – всего обрабатываемого текста), что увеличит затраты и времени, и памяти.
Разбор HTML-документов
Функции, предназначенные для разбора текстов на языке HTML, собраны в файле iahtm l.php . Их ядром является функция iaFindTagOneOf ($p, &$s, $tags), которая осуществляет поиск в тексте $s начиная с позиции $p одного из тегов, заданных параметром $tags.
Если нужно найти какой-то конкретный тег, например «p» или «div», то искомое значение передается как строка. Если же требуется обнаружить один из нескольких тегов, например любой из набора «h1», «h2», «h3», «h4», «h5», «h6», то список тегов нужно передать как массив: array (‘h1’, ‘h2’, ‘h3’, ‘h4’, ‘h5’, ‘h6’). В представленной реализации функции регистр тегов имеет значение, как это регламентировано спецификацией языка XML, поэтому для поиска в «произвольном» HTML в список нужно включить и теги в верхнем регистре: «H1», «H2», «H3», «H4», «H5», «H6».
В результате своей работы функция iaFindTagOneOf () возвращает либо значение FALSE, означающее, что поиск завершился неудачей, либо ассоциативный массив такой структуры:
Для удобства практического использования результат работы функции iaFindTagOneOf () может напрямую передаваться ей же в качестве первого аргумента. В этом случае поиск очередного тега будет продолжен с позиции, следующей за найденным тегом.
Искомые теги задаются своими именами. После реализации функции iaFindTagOneOf () оказалось, что ее можно использовать для поиска как открывающего, так и закрывающего тега. В последнем случае достаточно перед именем тега поставить символ «/» (наклонную черту). Тогда код для поиска тегов, ограничивающих HTML-элемент «div», будет выглядеть как:
Остальные функции, содержащиеся в модуле, имеют вспомогательное назначение. Так, iaFindTagAny ($p, &$s) ищет в тексте $s начиная с позиции $p любой тег HTML. С ее помощью «центральная» функция iaFindTagOneOf () получает очередной тег, после чего проверяет его имя на соответствие списку, переданному в параметре $tags.
Результат работы функции $tag = iaFindTagOneOf () может быть использован в вызывающей программе напрямую, но во многих случаях синтаксические конструкции доступа к ассоциативному массиву выглядят громоздко. Поэтому в комплект включены функции обработки этого результата:
- iaFoundTagPos (&$tag) – возвращает номер позиции тега $tag в обрабатываемой строке;
- iaFoundTagAfterPos (&$tag) – возвращает номер позиции, следующей за тегом $tag;
- iaFoundTagAttr (&$tag, $attr) – позволяет получить значение атрибута $attr тега $tag (если атрибут отсутствует, то возвращается FALSE);
- iaFoundElementContent (&$s, &$tagb, &$tage) – возвращает содержимое элемента, заключенное между тегами $tagb и $tage.
Таким образом, функции из файла iahtm l.php помогают произвести синтаксический анализ HTML-кода.
Комменты
Каждый уважающий себя бот должен уметь извлекать любую информацию с сайта, например ссылки, заголовки, любой текст, номера телефонов, адреса электронной почты, картинки. В этом уроке мы рассмотрим несколько методов сбора данных на странице при помощи iMacros и Javascript.
Методы извлечения информации из тегов средствами iMacros
У iMacros есть возможности для извлечения текста и даже html-кода из тегов. Для начала я рекомендую вам установить расширение FireBug, если вы этого еще не сделали. Открывается он по нажатию клавиши F12, либо вы можете нажать в любом месте страницы правой кнопкой и тыкнуть «Инспектировать элемент при помощи Firebug».
Давайте приступим, я рекомендую вам сразу открыть эту ссылку в Firefox. Поскольку вам предстоит писать много кода, рекомендую первым делом поставить себе какой-нибудь редактор, например весьма распространенный Notepad++ или же мой любимый и в сто раз более удобный Sublime Text 3, я писал о плагинах для него и режиме Vim для быстрого и мощного редактирования.
Давайте нажмем на «Запись» в iMacros, тыкнем по первому заголовку и остановим запись.
Мы получим вот такой скрипт(Current.iim):
Давайте переделаем его в JS.
Создайте новый файл, например test.js. Допустим, нам необходимо получить содержимое тега "" . Переделаем макрос на js и извлечем из него содержимое тега "" .
Результат(test.js):
Обратите внимание — я заменил "Тестоваястраница" на "*" — поскольку текст заголовка может быть любым, а звездочка нам говорит, что текст может быть произвольным
TYPE=H1 — указывает что мы ищем тег « «, можно указать любой другой
POS=1 — указывает, что мы выбираем первый попавшийся тег , ведь на странице их может оказаться несколько
ATTR=TXT:* — указывает значение атрибута, в данном случае служебный атрибут, который ищет совпадение в тексте внутри тега
EXTRACT=TXT — указывает, что мы извлекаем текст, также можно указать "EXTRACT=HTM" , чтобы извлечь текст вместе со всеми тегами
DOM (Document Object Model) – объектная модель страницы
Основным инструментом работы и динамических изменений на странице является DOM (Document Object Model) – объектная модель, используемая для XML/HTML-документов. Согласно DOM-модели, документ является иерархией, деревом. Каждый HTML-тег образует узел дерева с типом «элемент». Вложенные в него теги становятся дочерними узлами. Для представления текста создаются узлы с типом «текст».
DOM – это представление страницы в виде дерева объектов, доступное для чтения и изменения через JavaScript.
При чтении неверного HTML браузер автоматически корректирует его для показа и при построении DOM. В частности, всегда будет верхний тег . Даже если в тексте нет – в DOM он будет, браузер создаст его самостоятельно. То же самое касается и тега . Например, если файл состоит из одного слова "Test" , то браузер автоматически обернёт его в и .
При генерации DOM браузер самостоятельно обрабатывает ошибки в документе, закрывает теги и делает все, чтобы корректно отобразить документ.
Демонстрация извлечения информации из HTML-страницы
В качестве примера использования описанных в статье функций рассмотрим извлечение информации из новостной ленты главной страницы сайта журнала «Системный администратор» (см. рис. 2).
Рисунок 2. Так выглядит лента новостей сайта журнала «Системный администратор» в браузере
Прежде всего найдем в HTML-коде страницы по строке «Поздравляем с наступающим Новым годом» интересующий нас участок и попробуем выявить в нем какие-нибудь характерные особенности.
Рисунок 3. HTML-код начала ленты новостей на сайте журнала «Системный администратор». Подчеркнуты характерные участки, которые используются в качестве ориентиров при обработке текста
Читайте также: