![]() |
Модераторы: LSD, AntonSaburov |
![]() ![]() ![]() |
|
Platon |
|
||||||||||||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1801 Регистрация: 25.4.2006 Репутация: 16 Всего: 40 |
Здравствуйте, уважаемые.
Java HTML Parser: Введение Очередное утро - очередная задача - очередная порция новой информации - очередной FAQ. На этот раз взбрело мне в голову ковыряться в содержимом html документа не с помощью RegExp, хотя возможно он был бы менее ресурсозатратным(?), а с помощью более подходящего для этих целей инструмента HTMLEditorKit Сразу оговорю, чем HTMLEditorKit выгодно отличается, например, от того же SAXParser, он может работать с неправильным html кодом, т.е. скрадывает ошибки, допущенные верстальщиком при написании html кода. HTMLEditorKit сравнил с SAXParser не спроста. Дело в том, что оба этих инструмента используют похожую модель обработки данных. Так, что если вы знакомы с SAXParser, то вам не составит труда познать таинства HTMLEditorKit. HTMLEditorKit поставляется с jdk5 и используется для разбора html кода в компоненте JEditorPane HTMLEditorKit работает со стандартным DTD (HTML докуметами), но также может принять и пользовательский DTD, тем самым становится мощным и гибким инструментом для работы с частично неправильными xml документами Уверенным программистам рекомендую почитать информацию, предоставленную Sun Swing HTML Parser Java HTML Parser: Задача. Из содержимого html документа извлечь все ссылки(часто встречаемая в быту). Java HTML Parser: Теоретическая часть. Сердцем всего HTML парсера является класс HTMLEditorKit.ParserCallback окинем взглядом его методы:
flush() - как и обычный потоковый flush(), этот метод сигнализирует нам, что парсинг HTML документа закончен и необходимо завершить все вычисления, слить все промежуточные данные. handleText(char[] data, int pos) - обработка текста <a href="...">текст</a> <p>текст</p> и т.д. data содержит текст, pos позиция в документе handleComment(char[] data, int pos) - обработка встречаемых комментариев. data содержит текст, pos позиция в документе handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) - обработка открытия тега, пример <a href="test.html" rel="nofollow"> t - тег (в нашем примере HTML.Tag.A), a содержит набор атрибутов (в нашем случае HTML.Attribute.HREF и HTML.Attribute.REL) handleEndTag(HTML.Tag t, int pos) - сигнализатор закрытия тега handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) - обработка тега, не предусматривающего закрытие (одиночки), пример <img src="img.png" border="0" />, t - тег (в нашем примере HTML.Tag.IMG), a содержит набор атрибутов (в нашем случае HTML.Attribute.SRC и HTML.Attribute.BORDER) handleError(String errorMsg, int pos) - обрабатывается ошибка, пример "end.missing div ? ?", pos - позиция в документе, где определена ошибка handleEndOfLineString(String eol) - обработка новой строки в документе. Есть пара моментов для уточнения: 1. handleText и handleEndOfLineString. handleText получает в качестве параметра символьный массив, в котором последовательность так называемых Whitespace символов заменяется на 1 пробельный символ. handleEndOfLineString поможет нам обработать перенос строки, склеив с текстом, а также поможет вести статистику строка/позиция, а не позиция. 2. handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos). Интересен тут следующий факт: обычно в передаваемом объекте MutableAttributeSet содержатся ссылки на объекты HTML.Attribute, такие как HTML.Attribute.HREF, HTML.Attribute.SRC, ... если в html документе встречаются недокументированные атрибуты, они передаются в MutableAttributeSet как строки, т.е. <a href="test.html" myattr="Доктор Хаус уже на экранах!">, в данном случае MutableAttributeSet будет содержать 2 атрибута HTTP.Attribute.HREF@4564(href) => "test.html" и String@4565(myattr) => "Доктор Хаус уже на экранах!" Java HTML Parser: Основная часть. Засекаем секундомеры. LinkCollector.java
LinkObjectStream.java
Link.java
ConsoleStream.java
LinkCollector - сердце нашей программы, в мы определяем поведение и состояние сборки ссылок. 3 метода делают всю работу:
Сначала мы создаем новую ссылку(Link), устанавливаем доступные нам атрибуты, за исключением текста, он пока остается пустым. затем, если будет текст внутри ссылки(в html коде), то вызывается метод обработки текста handleText и присваивается к тексту объекта текущей ссылки(Link). Как только мы получаем команду закрытия тега (а она будет, даже если тег в HTML тексте не прописан), мы сливаем объект ссылки (Link) высокоуровневому обработчику LinkObjectStream, или его конкретной реализации ConsoleStream. Результат работы программы над винградовской главной страницей:
Программа для определения позиций сайта SESpider Это сообщение отредактировал(а) Platon - 19.2.2009, 19:10 |
||||||||||||||
|
|||||||||||||||
Platon |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1801 Регистрация: 25.4.2006 Репутация: 16 Всего: 40 |
Также часто встречаемое решение по сбору ссылок в документе:
Но этот подход не может обрабатывать ссылки "на лету", т.е. документ придется загрузить полностью + этот подход генерирует всё дерево тегов в документе, что отразится на увеличении потребляемой памяти. Это сообщение отредактировал(а) Platon - 25.9.2008, 07:33 |
|||
|
||||
unkis |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 802 Регистрация: 8.9.2004 Репутация: нет Всего: 1 |
Есть один вопрос, что по вашему мнению будет работать быстрее, парсинье страниц с помощью RegEx или же с помощью HTMLEditorKit?
|
|||
|
||||
Platon |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1801 Регистрация: 25.4.2006 Репутация: 16 Всего: 40 |
Я об этом упомянул, честно сказать не проверял. Но тут дело даже в другом - представьте себе такую схему: <body><a href="">test me</body>, просто напросто забыл закрыть тег a, в итоге, ваше регулярное выражение аля <a.*?href="(.*?)".*?>(.+?)</a> уже не сможет учесть эту ссылку. А HTMLEditorKit как раз-таки сможет. Проверил быстродействие, да HTMLEditorKit проигрывает по скорости в 2-3 раза. Его средний замер 350, средний замер регулярного выражения 125. Это сообщение отредактировал(а) Platon - 24.9.2008, 15:26 |
|||
|
||||
fotig |
|
|||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 3.9.2008 Репутация: нет Всего: нет |
LinkCollector.java в процессе компиляции выдает сообщение:
--------------------Configuration: <Default>-------------------- C:\LinkCollector.java:45: cannot find symbol symbol : class BadLocationException location: class ru.vingrad.platon.htmlparser.LinkCollector public void flush() throws BadLocationException { ^ 1 error Process completed. Это сообщение отредактировал(а) fotig - 25.9.2008, 04:39 |
|||
|
||||
nerezus |
|
|||
![]() Вселенский отказник ![]() ![]() ![]() ![]() Профиль Группа: Участник Сообщений: 3330 Регистрация: 15.6.2005 Репутация: нет Всего: 43 |
|
|||
|
||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 210 Всего: 538 |
Какая версия JDK используется? Чтобы скомпилировать этот код нужна 1.5 и старше. -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
fotig |
|
|||
Новичок Профиль Группа: Участник Сообщений: 5 Регистрация: 3.9.2008 Репутация: нет Всего: нет |
||||
|
||||
Platon |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1801 Регистрация: 25.4.2006 Репутация: 16 Всего: 40 |
LSD, уважаемый, ну что ж вы пособничаете. Я писал письмо, чтоб все посты fotig в отдельную тему отцепить :(
|
|||
|
||||
LSD |
|
|||
![]() Leprechaun Software Developer ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 15718 Регистрация: 24.3.2004 Где: Dublin Репутация: 210 Всего: 538 |
Ну прям чуствую себя преступником ![]() Для того обсуждение статьи и сделано, чтобы ее можно было улучшить или исправить ошибки в ней. В данном случае в статье действительно есть ошибка, в классе LinkCollector нехватает импорта для javax.swing.text.BadLocationException. -------------------- Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it. |
|||
|
||||
armixx |
|
|||
Новичок Профиль Группа: Участник Сообщений: 33 Регистрация: 16.8.2010 Репутация: нет Всего: нет |
Всем добрый день.
Уважаемый Platon. А можно ли с помощью описанных выше методов решить следующую проблему: JEditorPane при выводе HTML-документа не выделяет ссылки. Так же в режиме setEditable(false) отсутствует клавиатурная навигация. Я бы мог делать это сам, если бы знал соответствие начальную и конечную CaretPosition расположения текста ссылок в JEditorPane. Подозреваю, что решение где-то рядом, но моего опыта общения с JEditorPane, увы, пока не хватает. Или эту проблему можно решить как-то иначе? Документ в JEditorPane я загружаю так: ByteArrayInputStream bas=new ByteArrayInputStream(br); ep.read(bas,new HTMLDocument()); ew.setEditable(false); |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Java" | |
|
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Java: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |