Regex: извлечение читабельных (некодных) текста и URL-адресов из документов HTML
Вопрос
Я создаю приложение, которое возьмет URL в качестве ввода, извлеките содержимое HTML страницы с веб-сайта и извлечения все, что не содержится в теге. Отказ Другими словами, текстовое содержание страницы, как видно посетитель этой страницы. Который включает в себя «маскировку», все инкапсулировано в <script></script>
, <style></style>
а также <!-- -->
, поскольку эти порции содержат текст, который не охватывается в теге (но лучше оставить в покое).
Я построил это регез:
(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>)
Он правильно выбирает все содержимое, которое я хочу игнорировать, и оставляет только текстовое содержимое страницы. Однако это означает, что то, что я хочу извлечь, не будет отображаться в коллекции совпадений (я использую vb.net в Visual Studio 2010).
Есть ли способ «инвертировать» сопоставление целого документа, подобного этому, чтобы я получил совпадения на всех текстовых строках, которые остаются подходящим в приведенном выше Regex?
До сих пор, что я сделал, было добавить еще одну альтернативу в конце, что выбирает любую последовательность, которая не содержит <или> «», которая затем означает остаток в лешке. Я назвал этот последний бит в группе захвата, и когда я повторяю на матчах, я проверяю наличие текста в группе «Текст». Это работает, но мне было интересно, было ли удалось сделать все это через Regex и только что в конечном итоге с матчами на простом тексте.
Это должно быть совместно работать, не зная каких-либо конкретных тегов в HTML. Это должно извлечь все текст. Кроме того, мне нужно сохранить оригинальный HTML, поэтому страница сохраняет все его ссылки и сценарии - мне нужно только иметь возможность извлечь текст, чтобы я мог выполнить поиск и замены в нем, без боязни в «переименовании» любых тегов, атрибуты или Script Vimiables и т. Д. (Итак, я не могу просто сделать «заменить ни на что» на все спички, которые я получаю, потому что, хотя я потом ушел с тем, что мне нужно, это хлопот, чтобы перейти в правильные места Полностью функциональный документ).
Я хочу знать, что это вообще возможно, используя Regex (и я знаю о HTML Agility Pack и XPath, но не чувствую себя как).
Какие-либо предложения?
Обновлять:Вот решение (Regeex) решение, которое я оказался: http://www.martinwardener.com/regex/, внедренные в демонстрационном приложении, которое покажет как активные строки Regeex вместе с тестовым двигателем, который позволяет запустить анализ на любой онлайн-HTML-странице, давая вам время анализа и извлеченные результаты (для ссылки, URL и текстовых порций индивидуально - как Хорошо, как мнения, где все спички Regex выделены на месте в полном HTML-документе).
Решение 5
Хорошо, так вот как я делаю это:
Используя мое оригинальное регулярное выражение (с добавленным рисунком поиска для простого текста, что происходит, чтобы быть любым текстом, который оставлен выше после выполнения поиска тегов):
(?:(?:<(?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
Теперь содержит точную копию оригинала, кроме любого вхождения «оригинального слова» на странице (так как он представлен в браузере), переключается с «заменой Word», и весь код HTML и скрипта сохраняется нетронутым. Конечно, можно было бы / поставить в более сложный рутину замены, но это показывает основной принцип. Это 12 строк кода, включая декларацию функции и загрузку HTML-кода и т. Д. Я был бы очень заинтересован в виде параллельного решения, выполненного в DOM и т. Д. Для сравнения (да, я знаю, что этот подход может быть выброшен от баланса определенный Вхождения некоторых вложенных тегов quirks - в переписывании сценария - но ущерб от этого все равно будет очень ограничено, если таковые имеются (см. Некоторые из комментариев выше), и вообще это сделает работу довольно Darn хорошо).
Другие советы
Что я сделал, это было добавить еще одну альтернативу в конце, что выбирает любую последовательность, которая не содержит
<
или>
«Что тогда означает остаток в лешке. Я назвал этот последний бит в группе захвата, и когда я повторяю на матчах, я проверяю наличие текста в группе« Текст ».
Это то, что обычно делают. Или даже проще, замените каждое совпадение шаблона разметки с пустой строкой и то, что вы оставили, - это то, что вы ищете.
Это как бы работает, но здесь, кажется, есть строка, и там поднята, что не должно быть.
Ну, да, это потому, что ваше выражение - и регулярное выражение вообще - неадекватно, чтобы разбирать даже Valid HTML, не говоря уже о ужасах, которые находятся в настоящей сети. Первый совет, чтобы посмотреть, если вы действительно хотите преследовать этот бесполезный подход: значения атрибутов (а также текстовое содержимое в целом) могут содержать беззаботный >
персонаж.
Я хотел бы еще раз предложить преимущества пакета ловкости HTML.
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: url('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)
и это просто совершенно действительная разметка, что нет Сопоставьте правильную ссылку, не какую-либо из возможных недействительных разметки, разметки, которые не должны соответствовать ссылке или любую из многих проблем с другой техникой размытия расщепления от текста. Это вершина айсберга.
Regex не является надежным для извлечения текстового содержимого HTML-документов. Regex не может обрабатывать вложенные теги. Предположим, что документ не содержит вложенного тега, Regex все еще требует, чтобы все теги были правильно закрыты.
Если вы используете PHP, для простоты я настоятельно рекомендую вам использовать DOM (модель объекта документов) для разбора / извлечения HTML-документов. Библиотека DOM обычно существует в каждом языке программирования.
Если вы хотите извлечь части строки, не совпадающей на Regex, вы можете просто заменить детали, которые являются сопоставляется с пустой строкой для того же эффекта.
Обратите внимание, что единственная причина, по которой это может работать, это потому, что теги, которые вы заинтересованы в удалении, <script>
а также <style>
Теги, не могут быть вложены.
Тем не менее, это не редкость для одного <script>
Теги, чтобы содержать код для программного добавления другого <script>
Тег, в этом случае ваше регулярное выражение будет потерпеть неудачу. Он также не удается в случае, когда любой тег не закрыт должным образом.
Вы не можете анализировать HTML с регулярными выражениями.
Разбор HTML с регулярными выражениями приводит к печали.
Я знаю, что вы просто делаете это для удовольствия, но там так много пакетов там, чем на самом деле делать разбор правильного пути и сделать это надежно, и были проверены.
Не переименяйте колесо и делаем это таким образом, но гарантированно расстраивает вас по дороге.
Довожу до вашего сведения,
Вместо регулярного воздействия, с jQuery, можно извлечь текст в одиночку от разметки HTML. Для этого вы можете использовать следующий рисунок.
$("<div/>").html("#elementId").text()
Вы можете отослать это Jsfiddle.