Ответ 1
То, что вы ищете, это текстовый репозиторий DOM, который выводит текст, подобно Lynx или другим текстовым браузерам... Это гораздо сложнее, чем вы ожидали.
Я ищу код С# для преобразования HTML-документа в обычный текст.
Я не ищу простой дескриптор тега, но что-то, что выведет обычный текст с разумным сохранением исходного макета.
Результат должен выглядеть следующим образом:
Я просмотрел HTML Agility Pack, но я не думаю, что мне нужно. Есть ли у кого-нибудь другие предложения?
EDIT: Я просто загружаю пакет Agility Pack из CodePlex и запускал проект Html2Txt. Какое разочарование (по крайней мере модуль, который делает html для преобразования текста)! Все, что было сделано, это разбить теги, свернуть таблицы и т.д. Выход не выглядел так, как Html2Txt @W3C. Слишком плохо, что источник кажется недоступным. Я смотрел, есть ли доступ к более "консервированным" решениям.
РЕДАКТИРОВАТЬ 2: Спасибо всем за ваши предложения. FlySwat подскакивал мне в направлении, в котором я хотел идти. Я могу использовать класс System.Diagnostics.Process
для запуска lynx.exe с помощью переключателя "-dump", чтобы отправить текст на стандартный вывод и захватить stdout с помощью ProcessStartInfo.UseShellExecute = false
и ProcessStartInfo.RedirectStandardOutput = true
. Я завершу все это в класс С#. Этот код будет называться только случайным образом, поэтому я не слишком обеспокоен появлением нового процесса и его выполнением в коде. Кроме того, Lynx FAST!!
То, что вы ищете, это текстовый репозиторий DOM, который выводит текст, подобно Lynx или другим текстовым браузерам... Это гораздо сложнее, чем вы ожидали.
Просто заметка о пакете HtmlAgilityPack для потомков. Проект содержит пример парсинга текста в html, который, как отмечается в OP, вообще не обрабатывает пробелы, как это предполагал бы любой, кто пишет HTML. Существуют полнотекстовые решения для рендеринга, отмеченные другими на этот вопрос, но это не так (он даже не может обрабатывать таблицы в их текущем виде), но он легкий и быстрый, и это все, что я хотел для создания простого текста. версия HTML писем.
using System.IO;
using System.Text.RegularExpressions;
using HtmlAgilityPack;
//small but important modification to class https://github.com/zzzprojects/html-agility-pack/blob/master/src/Samples/Html2Txt/HtmlConvert.cs
public static class HtmlToText
{
public static string Convert(string path)
{
HtmlDocument doc = new HtmlDocument();
doc.Load(path);
return ConvertDoc(doc);
}
public static string ConvertHtml(string html)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
return ConvertDoc(doc);
}
public static string ConvertDoc (HtmlDocument doc)
{
using (StringWriter sw = new StringWriter())
{
ConvertTo(doc.DocumentNode, sw);
sw.Flush();
return sw.ToString();
}
}
internal static void ConvertContentTo(HtmlNode node, TextWriter outText, PreceedingDomTextInfo textInfo)
{
foreach (HtmlNode subnode in node.ChildNodes)
{
ConvertTo(subnode, outText, textInfo);
}
}
public static void ConvertTo(HtmlNode node, TextWriter outText)
{
ConvertTo(node, outText, new PreceedingDomTextInfo(false));
}
internal static void ConvertTo(HtmlNode node, TextWriter outText, PreceedingDomTextInfo textInfo)
{
string html;
switch (node.NodeType)
{
case HtmlNodeType.Comment:
// don't output comments
break;
case HtmlNodeType.Document:
ConvertContentTo(node, outText, textInfo);
break;
case HtmlNodeType.Text:
// script and style must not be output
string parentName = node.ParentNode.Name;
if ((parentName == "script") || (parentName == "style"))
{
break;
}
// get text
html = ((HtmlTextNode)node).Text;
// is it in fact a special closing node output as text?
if (HtmlNode.IsOverlappedClosingElement(html))
{
break;
}
// check the text is meaningful and not a bunch of whitespaces
if (html.Length == 0)
{
break;
}
if (!textInfo.WritePrecedingWhiteSpace || textInfo.LastCharWasSpace)
{
html= html.TrimStart();
if (html.Length == 0) { break; }
textInfo.IsFirstTextOfDocWritten.Value = textInfo.WritePrecedingWhiteSpace = true;
}
outText.Write(HtmlEntity.DeEntitize(Regex.Replace(html.TrimEnd(), @"\s{2,}", " ")));
if (textInfo.LastCharWasSpace = char.IsWhiteSpace(html[html.Length - 1]))
{
outText.Write(' ');
}
break;
case HtmlNodeType.Element:
string endElementString = null;
bool isInline;
bool skip = false;
int listIndex = 0;
switch (node.Name)
{
case "nav":
skip = true;
isInline = false;
break;
case "body":
case "section":
case "article":
case "aside":
case "h1":
case "h2":
case "header":
case "footer":
case "address":
case "main":
case "div":
case "p": // stylistic - adjust as you tend to use
if (textInfo.IsFirstTextOfDocWritten)
{
outText.Write("\r\n");
}
endElementString = "\r\n";
isInline = false;
break;
case "br":
outText.Write("\r\n");
skip = true;
textInfo.WritePrecedingWhiteSpace = false;
isInline = true;
break;
case "a":
if (node.Attributes.Contains("href"))
{
string href = node.Attributes["href"].Value.Trim();
if (node.InnerText.IndexOf(href, StringComparison.InvariantCultureIgnoreCase)==-1)
{
endElementString = "<" + href + ">";
}
}
isInline = true;
break;
case "li":
if(textInfo.ListIndex>0)
{
outText.Write("\r\n{0}.\t", textInfo.ListIndex++);
}
else
{
outText.Write("\r\n*\t"); //using '*' as bullet char, with tab after, but whatever you want eg "\t->", if utf-8 0x2022
}
isInline = false;
break;
case "ol":
listIndex = 1;
goto case "ul";
case "ul": //not handling nested lists any differently at this stage - that is getting close to rendering problems
endElementString = "\r\n";
isInline = false;
break;
case "img": //inline-block in reality
if (node.Attributes.Contains("alt"))
{
outText.Write('[' + node.Attributes["alt"].Value);
endElementString = "]";
}
if (node.Attributes.Contains("src"))
{
outText.Write('<' + node.Attributes["src"].Value + '>');
}
isInline = true;
break;
default:
isInline = true;
break;
}
if (!skip && node.HasChildNodes)
{
ConvertContentTo(node, outText, isInline ? textInfo : new PreceedingDomTextInfo(textInfo.IsFirstTextOfDocWritten){ ListIndex = listIndex });
}
if (endElementString != null)
{
outText.Write(endElementString);
}
break;
}
}
}
internal class PreceedingDomTextInfo
{
public PreceedingDomTextInfo(BoolWrapper isFirstTextOfDocWritten)
{
IsFirstTextOfDocWritten = isFirstTextOfDocWritten;
}
public bool WritePrecedingWhiteSpace {get;set;}
public bool LastCharWasSpace { get; set; }
public readonly BoolWrapper IsFirstTextOfDocWritten;
public int ListIndex { get; set; }
}
internal class BoolWrapper
{
public BoolWrapper() { }
public bool Value { get; set; }
public static implicit operator bool(BoolWrapper boolWrapper)
{
return boolWrapper.Value;
}
public static implicit operator BoolWrapper(bool boolWrapper)
{
return new BoolWrapper{ Value = boolWrapper };
}
}
Как пример, следующий HTML-код...
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<header>
Whatever Inc.
</header>
<main>
<p>
Thanks for your enquiry. As this is the 1<sup>st</sup> time you have contacted us, we would like to clarify a few things:
</p>
<ol>
<li>
Please confirm this is your email by replying.
</li>
<li>
Then perform this step.
</li>
</ol>
<p>
Please solve this <img alt="complex equation" src="http://upload.wikimedia.org/wikipedia/commons/8/8d/First_Equation_Ever.png"/>. Then, in any order, could you please:
</p>
<ul>
<li>
a point.
</li>
<li>
another point, with a <a href="http://en.wikipedia.org/wiki/Hyperlink">hyperlink</a>.
</li>
</ul>
<p>
Sincerely,
</p>
<p>
The whatever.com team
</p>
</main>
<footer>
Ph: 000 000 000<br/>
mail: whatever st
</footer>
</body>
</html>
... будет преобразован в:
Whatever Inc.
Thanks for your enquiry. As this is the 1st time you have contacted us, we would like to clarify a few things:
1. Please confirm this is your email by replying.
2. Then perform this step.
Please solve this [complex equation<http://upload.wikimedia.org/wikipedia/commons/8/8d/First_Equation_Ever.png>]. Then, in any order, could you please:
* a point.
* another point, with a hyperlink<http://en.wikipedia.org/wiki/Hyperlink>.
Sincerely,
The whatever.com team
Ph: 000 000 000
mail: whatever st
... в отличие от:
Whatever Inc.
Thanks for your enquiry. As this is the 1st time you have contacted us, we would like to clarify a few things:
Please confirm this is your email by replying.
Then perform this step.
Please solve this . Then, in any order, could you please:
a point.
another point, with a hyperlink.
Sincerely,
The whatever.com team
Ph: 000 000 000
mail: whatever st
Вы можете использовать это:
public static string StripHTML(string HTMLText, bool decode = true)
{
Regex reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase);
var stripped = reg.Replace(HTMLText, "");
return decode ? HttpUtility.HtmlDecode(stripped) : stripped;
}
Обновление
Спасибо за комментарии, которые я обновил для улучшения этой функции.
Я слышал из надежного источника, что, если вы занимаетесь анализом HTML в .Net, вам следует снова взглянуть на пакет гибкости HTML.
http://www.codeplex.com/htmlagilitypack
Некоторые примеры на SO..
Поскольку мне нужно преобразование в обычный текст с помощью LF и bullets, я нашел это довольно приемлемое решение для codeproject, которое охватывает множество приложений конверсии:
Преобразование HTML в обычный текст
Да, выглядит так большой, но отлично работает.
Вы пробовали http://www.aaronsw.com/2002/html2text/ его Python, но с открытым исходным кодом.
Предполагая, что у вас есть хорошо сформированный html, вы также можете попробовать преобразование XSL.
Вот пример:
using System;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml.Xsl;
class Html2TextExample
{
public static string Html2Text(XDocument source)
{
var writer = new StringWriter();
Html2Text(source, writer);
return writer.ToString();
}
public static void Html2Text(XDocument source, TextWriter output)
{
Transformer.Transform(source.CreateReader(), null, output);
}
public static XslCompiledTransform _transformer;
public static XslCompiledTransform Transformer
{
get
{
if (_transformer == null)
{
_transformer = new XslCompiledTransform();
var xsl = XDocument.Parse(@"<?xml version='1.0'?><xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"" exclude-result-prefixes=""xsl""><xsl:output method=""html"" indent=""yes"" version=""4.0"" omit-xml-declaration=""yes"" encoding=""UTF-8"" /><xsl:template match=""/""><xsl:value-of select=""."" /></xsl:template></xsl:stylesheet>");
_transformer.Load(xsl.CreateNavigator());
}
return _transformer;
}
}
static void Main(string[] args)
{
var html = XDocument.Parse("<html><body><div>Hello world!</div></body></html>");
var text = Html2Text(html);
Console.WriteLine(text);
}
}
Простейшим может быть удаление дескрипторов тегов вместе с заменой некоторых тегов элементами текста, такими как тире для элементов списка (li) и разрывов строк для br и p. Не должно быть слишком сложно распространить это на таблицы.
У меня были некоторые проблемы с расшифровкой с помощью HtmlAgility, и я не хотел тратить время на его исследование.
Вместо этого я использовал эту утилиту из API Team Team Foundation:
var text = HtmlFilter.ConvertToPlainText(htmlContent);
Другое сообщение предлагает пакет гибкости HTML:
Это гибкий HTML-парсер, который создает DOM для чтения/записи и поддерживает простой XPATH или XSLT (вы на самом деле НЕ ИМЕЕТ понять XPATH и XSLT, чтобы использовать его, не волнуйтесь...). это библиотека .NET-кода, которая позволяет вам анализировать "вне Интернета" HTML файлы. синтаксический анализатор очень терпим к "реальному мир" искаженный HTML. Объект модель очень похожа на то, что предлагает System.Xml, но для документов HTML (или потоки).
Я использовал Detagger в прошлом. Это довольно хорошая работа по форматированию HTML в виде текста и больше, чем просто удаление тегов.
Эта функция конвертирует "То, что вы видите в браузере" в простой текст с переносами строк. (Если вы хотите увидеть результат в браузере, просто используйте возвращаемое значение с комментариями)
public string HtmlFileToText(string filePath)
{
using (var browser = new WebBrowser())
{
string text = File.ReadAllText(filePath);
browser.ScriptErrorsSuppressed = true;
browser.Navigate("about:blank");
browser?.Document?.OpenNew(false);
browser?.Document?.Write(text);
return browser.Document?.Body?.InnerText;
//return browser.Document?.Body?.InnerText.Replace(Environment.NewLine, "<br />");
}
}
Я не знаю С#, но есть довольно маленький и удобный для чтения python html2txt script здесь: http://www.aaronsw.com/2002/html2text/
У меня недавно появилось сообщение о решении, которое сработало для меня, используя файл Markdown XSLT для преобразования источника HTML. Источник HTML, конечно же, должен быть корректным XML сначала
Попробуйте простой и удобный способ: просто позвоните StripHTML(WebBrowserControl_name);
public string StripHTML(WebBrowser webp)
{
try
{
doc.execCommand("SelectAll", true, null);
IHTMLSelectionObject currentSelection = doc.selection;
if (currentSelection != null)
{
IHTMLTxtRange range = currentSelection.createRange() as IHTMLTxtRange;
if (range != null)
{
currentSelection.empty();
return range.text;
}
}
}
catch (Exception ep)
{
//MessageBox.Show(ep.Message);
}
return "";
}
В Genexus Вы можете сделать с Regex
& pattern = '< [^ > ] + > '
& TSTRPNOT = & TSTRPNOT.ReplaceRegEx(& шаблон "")
В Genexus possiamo gestirlo con Regex,
Вы можете использовать элемент управления WebBrowser, чтобы отобразить в памяти ваш html-контент. После запуска LoadCompleted...
IHTMLDocument2 htmlDoc = (IHTMLDocument2)webBrowser.Document;
string innerHTML = htmlDoc.body.innerHTML;
string innerText = htmlDoc.body.innerText;
Если вы используете .NET framework 4.5, вы можете использовать System.Net.WebUtility.HtmlDecode(), который берет строку с кодировкой HTML и возвращает декодированную строку.
Документировано в MSDN по адресу: http://msdn.microsoft.com/en-us/library/system.net.webutility.htmldecode(v=vs.110).aspx
Вы также можете использовать это в приложении Windows Store.
Это еще одно решение для преобразования HTML в текст или RTF в С#:
SautinSoft.HtmlToRtf h = new SautinSoft.HtmlToRtf();
h.OutputFormat = HtmlToRtf.eOutputFormat.TextUnicode;
string text = h.ConvertString(htmlString);
Эта библиотека не бесплатна, это коммерческий продукт, и это мой собственный продукт.