Regex: извлечение читаемого (не кодированного) текста и URL-адресов из документов HTML - PullRequest
1 голос
/ 17 октября 2010

Я создаю приложение, которое будет принимать URL-адрес в качестве входного, извлекать html-содержимое страницы из Интернета и извлекать все, что не содержится в теге .Другими словами, текстовое содержание страницы, как видно посетителю этой страницы.Это включает в себя «маскирование» всего, что заключено в <script></script>, <style></style> и <!-- -->, так как эти части содержат текст, который не заключен в тег (но лучше оставить его в покое).

Я построилthis regex:

(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>)

Он правильно выбирает все содержимое, которое я хочу игнорировать, и оставляет только текстовое содержимое страницы.Однако это означает, что то, что я хочу извлечь, не будет отображаться в коллекции совпадений (я использую VB.Net в Visual Studio 2010).

Есть ли способ "инвертировать" сопоставлениевесь документ, подобный этому, чтобы я мог получить совпадения для всех текстовых строк, которые пропущены при сопоставлении в приведенном выше регулярном выражении?

До сих пор я добавил еще одну альтернативу в конце,это выбирает «любую последовательность, которая не содержит <или>», что означает остаток текста.Я назвал этот последний бит в группе захвата, и когда я перебираю совпадения, я проверяю наличие текста в «текстовой» группе.Это работает, но мне было интересно, если бы было возможно сделать все это через регулярное выражение и просто в конечном итоге с совпадениями в простом тексте.

Это должно работать в общем, без знания каких-либоконкретные теги в HTML.Предполагается извлечь весь текст.Кроме того, мне нужно сохранить исходный html, чтобы на странице были сохранены все ссылки и скрипты - мне нужно только иметь возможность извлекать текст, чтобы я мог выполнять поиск и замены внутри него, не опасаясь «переименования» любых тегов, атрибутовили переменные сценария и т. д. (поэтому я не могу просто выполнить «замену ничем» для всех совпадений, которые я получаю, потому что, несмотря на то, что после этого у меня остается то, что мне нужно, это затрудняет повторную вставку этого в правильные местаполностью функциональный документ).

Я хочу знать, возможно ли это вообще с помощью регулярных выражений (и я знаю о HTML Agility Pack и XPath, но не чувствую).

Любые предложения?

Обновление: Вот решение (на основе регулярных выражений), в котором я остановился: http://www.martinwardener.com/regex/,, реализованное в демонстрационном веб-приложении, которое будет показывать обе активные строки регулярных выражений вдольс тестовым движком, который позволяет вам выполнять анализ на любой html-странице в Интернете, давая вам время разбора и извлеченные результаты (для отдельных частей ссылки, URL-адреса и текста)в конечном итоге - а также представления, где все совпадения с регулярными выражениями выделены на месте в полном HTML-документе).

Ответы [ 6 ]

2 голосов
/ 17 октября 2010

В конце я добавил еще одну альтернативу, которая выбирает «любую последовательность, которая не содержит < или >», что означает остаток текста. Я назвал этот последний бит в группе захвата, и когда я перебираю совпадения, я проверяю наличие текста в группе «текст».

Это то, что обычно делают. Или еще проще: замените каждое совпадение шаблона разметки на пустую строку, и у вас останется то, что вы ищете.

Это вроде работает, но, кажется, тут и там есть цепочка, которую не нужно подбирать.

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

Я хотел бы еще раз предложить преимущества HTML Agility Pack.

ETA: так как вы, кажется, хотите этого, вот несколько примеров разметки, которая выглядит так, будто она испортит ваше выражение.

<a href=link></a> - unquoted
<a href= link></a> - unquoted, space at front matched but then required at back
<a href="~/link"></a> - very common URL char missing in group
<a href="link$!*'link"></a> - more URL chars missing in group
<a href=lïnk></a> - IRI
<a href
    ="link"> - newline (or tab)
<div style="background-image: url(link);"> - unquoted
<div style="background-image: url( 'link' );"> - spaced
<div style="background-image: u&#114;l('link');"> - html escape
<div style="background-image: ur\l('link');"> - css escape
<div style="background-image: url('link\')link');"> - css escape
<div style="background-image: url(\
'link')"> - CSS folding
<div style="background-image: url
('link')"> - newline (or tab)

и это просто полностью допустимая разметка, которая не будет соответствовать правильной ссылке, ни одной из возможных недопустимых разметок, разметки, которая не должна соответствовать ссылке, или любой из множества проблем Ваш другой метод отделения разметки от текста. Это вершина айсберга.

0 голосов
/ 21 ноября 2014

Для вашей информации,

Вместо Regex, с помощью JQuery, можно извлечь только текст из разметки html. Для этого вы можете использовать следующий шаблон.

$("<div/>").html("#elementId").text()

Вы можете сослаться на это JSFIDDLE

0 голосов
/ 17 октября 2010

Нельзя анализировать HTML с помощью регулярных выражений.

Анализ HTML с помощью регулярных выражений приводит к печали.

Я знаю, что вы просто делаете это для удовольствия, но существует так много пакетов, которые фактически выполняют правильный анализ, И делают это надежно, И были протестированы.

Не изобретай колесо и делай это так, что это почти наверняка расстроит тебя в будущем.

0 голосов
/ 17 октября 2010

ОК, вот как я это делаю:

Используя мое оригинальное регулярное выражение (с добавленным шаблоном поиска для простого текста, который оказывается любым текстом, оставшимся после выполнения поиска по тегу):

(?:(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>))|(?P<text>[^<>]*)

Тогда в VB.Net:

Dim regexText As New Regex("(?:(?:<(?<tag>script|style)[\s\S]*?</\k<tag>>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>))|(?<text>[^<>]*)", RegexOptions.IgnoreCase)
Dim source As String = File.ReadAllText("html.txt")
Dim evaluator As New MatchEvaluator(AddressOf MatchEvalFunction)
Dim newHtml As String = regexText.Replace(source, evaluator)

Фактическая замена текста происходит здесь:

Private Function MatchEvalFunction(ByVal match As Match) As String
    Dim plainText As String = match.Groups("text").Value
    If plainText IsNot Nothing AndAlso plainText <> "" Then
        MatchEvalFunction = match.Value.Replace(plainText, plainText.Replace("Original word", "Replacement word"))
    Else
        MatchEvalFunction = match.Value
    End If
End Function

Вуаля. newHtml теперь содержит точную копию оригинала, за исключением того, что каждое вхождение «Оригинального слова» на странице (как оно представлено в браузере) переключается с «Заменяющим словом», и весь html и код скрипта сохраняется без изменений. Конечно, можно / было бы поставить более сложную процедуру замены, но это показывает основной принцип. Это 12 строк кода, включая объявление функции и загрузку HTML-кода и т. Д. Мне было бы очень интересно увидеть параллельное решение, сделанное в DOM и т. Д. Для сравнения (да, я знаю, что этот подход может быть выведен из равновесия некоторые случаи появления некоторых вложенных тегов - при переписывании сценария - но ущерб от этого все равно будет очень ограниченным, если таковые имеются (см. некоторые комментарии выше), и в целом это будет делать чертовски хорошо) .

0 голосов
/ 17 октября 2010

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

Обратите внимание, что единственная причина, по которой это может работать, заключается в том, что теги, которые вы хотите удалить, теги <script> и <style> не могут быть вложенными.

Тем не менее, один тег <script> нередко содержит код для программного добавления другого тега <script>, и в этом случае ваше регулярное выражение не будет выполнено. Также произойдет сбой в случае, если какой-либо тег не закрыт должным образом.

0 голосов
/ 17 октября 2010

Regex не является надежным для получения текстового содержимого документов HTML. Regex не может обрабатывать вложенные теги. Предположим, что документ не содержит вложенных тегов, регулярное выражение по-прежнему требует, чтобы все теги были правильно закрыты.

Если для простоты вы используете PHP, я настоятельно рекомендую вам использовать DOM (объектную модель документа) для анализа / извлечения документов HTML. Библиотека DOM обычно существует на каждом языке программирования.

...