| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java: Общие вопросы > HTML Parser HTMLEditorKit |
| Автор: Platon 20.9.2008, 13:11 | ||||||||||||||
| Здравствуйте, уважаемые. Java HTML Parser: Введение Очередное утро - очередная задача - очередная порция новой информации - очередной FAQ. На этот раз взбрело мне в голову ковыряться в содержимом html документа не с помощью RegExp, хотя возможно он был бы менее ресурсозатратным(?), а с помощью более подходящего для этих целей инструмента HTMLEditorKit Сразу оговорю, чем HTMLEditorKit выгодно отличается, например, от того же http://forum.vingrad.ru/faq/topic-158003.html, он может работать с неправильным html кодом, т.е. скрадывает ошибки, допущенные верстальщиком при написании html кода. HTMLEditorKit сравнил с SAXParser не спроста. Дело в том, что оба этих инструмента используют похожую модель обработки данных. Так, что если вы знакомы с SAXParser, то вам не составит труда познать таинства HTMLEditorKit. HTMLEditorKit поставляется с jdk5 и используется для разбора html кода в компоненте JEditorPane HTMLEditorKit работает со стандартным DTD (HTML докуметами), но также может принять и пользовательский DTD, тем самым становится мощным и гибким инструментом для работы с частично неправильными xml документами Уверенным программистам рекомендую почитать информацию, предоставленную Sun http://java.sun.com/products/jfc/tsc/articles/bookmarks/ 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. Результат работы программы над винградовской главной страницей:
http://sespider.ru |
| Автор: Platon 23.9.2008, 14:45 | ||
Также часто встречаемое решение по сбору ссылок в документе:
Но этот подход не может обрабатывать ссылки "на лету", т.е. документ придется загрузить полностью + этот подход генерирует всё дерево тегов в документе, что отразится на увеличении потребляемой памяти. |
| Автор: unkis 23.9.2008, 19:09 |
| Есть один вопрос, что по вашему мнению будет работать быстрее, парсинье страниц с помощью RegEx или же с помощью HTMLEditorKit? |
| Автор: Platon 24.9.2008, 15:25 |
Я об этом упомянул, честно сказать не проверял. Но тут дело даже в другом - представьте себе такую схему: <body><a href="">test me</body>, просто напросто забыл закрыть тег a, в итоге, ваше регулярное выражение аля <a.*?href="(.*?)".*?>(.+?)</a> уже не сможет учесть эту ссылку. А HTMLEditorKit как раз-таки сможет. Проверил быстродействие, да HTMLEditorKit проигрывает по скорости в 2-3 раза. Его средний замер 350, средний замер регулярного выражения 125. |
| Автор: fotig 25.9.2008, 04:17 |
| 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. |
| Автор: nerezus 25.9.2008, 08:20 | ||
|
| Автор: LSD 25.9.2008, 12:07 |
Какая версия JDK используется? Чтобы скомпилировать этот код нужна 1.5 и старше. |
| Автор: fotig 25.9.2008, 13:49 | ||
компиляция делается на JCreator 4 c JDK 1.6.0_07 |
| Автор: Platon 25.9.2008, 14:01 |
| LSD, уважаемый, ну что ж вы пособничаете. Я писал письмо, чтоб все посты fotig в отдельную тему отцепить :( |
| Автор: LSD 25.9.2008, 16:54 | ||
Ну прям чуствую себя преступником Для того обсуждение статьи и сделано, чтобы ее можно было улучшить или исправить ошибки в ней. В данном случае в статье действительно есть ошибка, в классе LinkCollector нехватает импорта для javax.swing.text.BadLocationException. |
| Автор: armixx 6.9.2010, 10:43 |
| Всем добрый день. Уважаемый Platon. А можно ли с помощью описанных выше методов решить следующую проблему: JEditorPane при выводе HTML-документа не выделяет ссылки. Так же в режиме setEditable(false) отсутствует клавиатурная навигация. Я бы мог делать это сам, если бы знал соответствие начальную и конечную CaretPosition расположения текста ссылок в JEditorPane. Подозреваю, что решение где-то рядом, но моего опыта общения с JEditorPane, увы, пока не хватает. Или эту проблему можно решить как-то иначе? Документ в JEditorPane я загружаю так: ByteArrayInputStream bas=new ByteArrayInputStream(br); ep.read(bas,new HTMLDocument()); ew.setEditable(false); |