AngleSharp извлекает форматированный текст
Мне интересно, можно ли извлечь форматированный текст из HTMLDocument с помощью AngleSharp. Я использую следующий код для извлечения текста. У меня проблема в том, что извлеченный текст работает вместе, между каждым элементом нет перерыва.
var parser = new HtmlParser();
var document = parser.Parse("<script>var x = 1;</script> <h1>Some example source</h1><p>This is a paragraph element</p>");
var text = document.Body.Text();
Это возвращает следующий текст
Некоторый пример sourceThis является элементом абзаца
В идеале я хотел бы, чтобы он возвращал некоторый пример источника. Это элемент параграфа, в котором есть некоторое разделение между каждым из текстовых значений узлов.
2 ответа
Я знаю, что опаздываю на вечеринку, но лучше поздно, чем никогда (также я надеюсь, что кто-то еще выиграет от этого ответа).
Комментарии к вопросу являются правильными. С одной стороны, у нас есть спецификация W3C и источник документа, который говорит нам, что в (официальной) сериализации не будет пробела, с другой стороны, у нас есть довольно распространенный случай "интеграции" некоторых пробелов, когда это применимо. (или, возможно, даже переводы строк, например, если <br>
стихия видна).
При написании библиотека не знает вашего конкретного варианта использования (например, когда вы хотите вставить пробелы). Тем не менее, это может помочь вам легче достичь желаемого состояния.
Сериализация из DOM в строку выполняется через экземпляр класса, который реализует IMarkupFormatter
, ToHtml()
метод любого узла DOM принимает такой объект для возврата строки. Делать
var myFormatter = new MyMarkupFormatter();
var text = document.Body.ToHtml(myFormatter);
Теперь вопрос сводится к реализации MyMarkupFormatter, которая работает для нас. Этот форматер, по существу, будет давать только текстовые узлы, однако некоторые теги будут обрабатываться по-разному (то есть возвращать некоторый текст, например пробелы).
public class MyMarkupFormatter : IMarkupFormatter
{
String IMarkupFormatter.Comment(IComment comment)
{
return String.Empty;
}
String IMarkupFormatter.Doctype(IDocumentType doctype)
{
return String.Empty;
}
String IMarkupFormatter.Processing(IProcessingInstruction processing)
{
return String.Empty;
}
String IMarkupFormatter.Text(String text)
{
return text;
}
String IMarkupFormatter.OpenTag(IElement element, Boolean selfClosing)
{
switch (element.LocalName)
{
case "p":
return "\n\n";
case "br":
return "\n";
case "span":
return " ";
}
return String.Empty;
}
String IMarkupFormatter.CloseTag(IElement element, Boolean selfClosing)
{
return String.Empty;
}
String IMarkupFormatter.Attribute(IAttr attr)
{
return String.Empty;
}
}
Если удаление всей нетекстовой информации - это не то, что вам нужно, то AngleSharp также предлагает PrettyMarkupFormatter
из коробки - может быть, это уже довольно близко к тому, что вы хотели (более симпатичный форматер разметки).
Надеюсь это поможет!
Вот моя реализация IMarkupFormatter. Он улучшает пример Флориана, поскольку добавляет разрывы строк для любого блочного элемента, а не только для абзацев. Он помещает разрыв строки до и после каждого блочного элемента, чтобы текст из блочных элементов не размещался на той же строке, что и текст из других узлов. Как и в принятом ответе, в моей реализации используется только один разрыв строки для элементов <br>. Наконец, он не добавляет пробелы к элементам <span> или другим встроенным элементам. Вместо этого он сохраняет пробелы, которые уже присутствовали в исходной строке HTML.
using AngleSharp;
using AngleSharp.Dom;
public class TextMarkupFormatter : IMarkupFormatter
{
public string Text(ICharacterData text)
{
return text.Data;
}
public string LiteralText(ICharacterData text)
{
return "";
}
public string Comment(IComment comment)
{
return "";
}
public string Processing(IProcessingInstruction processing)
{
return "";
}
public string Doctype(IDocumentType doctype)
{
return "";
}
public string OpenTag(IElement element, bool selfClosing)
{
if (IsBlockLevelElement(element))
return "\n";
return "";
}
public string CloseTag(IElement element, bool selfClosing)
{
if (IsBlockLevelElement(element) || element.TagName == "BR")
return "\n";
return "";
}
private bool IsBlockLevelElement(IElement element)
{
switch (element.TagName)
{
case "ADDRESS":
case "ARTICLE":
case "ASIDE":
case "BLOCKQUOTE":
case "DETAILS":
case "DIALOG":
case "DD":
case "DIV":
case "DL":
case "FIELDSET":
case "FIGCAPTION":
case "FIGURE":
case "FOOTER":
case "FORM":
case "H1":
case "H2":
case "H3":
case "H4":
case "H5":
case "H6":
case "HEADER":
case "HGROUP":
case "HR":
case "LI":
case "MAIN":
case "NAV":
case "OL":
case "P":
case "PRE":
case "SECTION":
case "TABLE":
case "UL":
return true;
default:
return false;
}
}
}
Если вы работаете с HTML в строке, а не с полным HTML-документом, вы можете проанализировать и отформатировать его следующим образом:
var text = new HtmlParser()
.ParseFragment("Hello<div>World</div>", null)
.ToHtml(new TextMarkupFormatter())
.Trim();
Console.WriteLine(text); // Writes "Hello\nWorld"