76 votes

Comment convertir du HTML en texte en C# ?

Je cherche du code C# pour convertir un document HTML en texte brut.

Je ne cherche pas une simple suppression des balises, mais quelque chose qui produise du texte brut avec un nom d'utilisateur et un mot de passe. raisonnable la préservation de la disposition originale.

Le résultat devrait ressembler à ceci :

Html2Txt au W3C

J'ai regardé le HTML Agility Pack, mais je ne pense pas que ce soit ce dont j'ai besoin. Quelqu'un a-t-il d'autres suggestions ?

EDIT : Je viens de télécharger le pack HTML Agility de CodePlex et a exécuté le projet Html2Txt. Quelle déception (au moins le module qui convertit le html en texte) ! Tout ce qu'il a fait, c'est enlever les balises, aplatir les tableaux, etc. Le résultat ne ressemble en rien à ce que Html2Txt @ W3C a produit. Dommage que cette source ne semble pas être disponible. Je cherchais à voir s'il y avait une solution plus "en boîte" disponible.

EDIT 2 : Merci à tous pour vos suggestions. FlySwat m'a fait prendre la direction que je voulais prendre. Je peux utiliser le System.Diagnostics.Process pour exécuter lynx.exe avec l'option "-dump" afin d'envoyer le texte vers la sortie standard, et capturer le stdout avec ProcessStartInfo.UseShellExecute = false et ProcessStartInfo.RedirectStandardOutput = true . Je vais envelopper tout cela dans une classe C#. Ce code ne sera appelé qu'occasionnellement, donc je ne suis pas trop préoccupé par la création d'un nouveau processus plutôt que de le faire dans le code. De plus, Lynx est RAPIDE !

4 votes

Pourquoi le pack Html Agility ne répond-il pas à vos besoins ? Cela pourrait aider à orienter les gens vers votre besoin spécifique.

0 votes

Je ne l'ai pas regardé en détail, peut-être que ça pourrait marcher ? Pouvez-vous m'indiquer un exemple de code quelque part ?

0 votes

Matt, as-tu déjà écrit ce code ? J'aimerais bien voir le résultat.

53voto

Brent Points 465

Juste une note sur le HtmlAgilityPack pour la postérité. Le projet contient un exemple d'analyse syntaxique de texte en html qui, comme l'a noté l'OP, ne gère pas du tout les espaces comme le ferait une personne écrivant du HTML. Il existe des solutions de rendu de texte complet, notées par d'autres personnes à cette question, ce qui n'est pas le cas de celle-ci (elle ne peut même pas gérer les tableaux dans sa forme actuelle), mais elle est légère et rapide, ce qui est tout ce que je voulais pour créer une version texte simple d'emails html.

//small but important modification to class http://htmlagilitypack.codeplex.com/sourcecontrol/changeset/view/62772#52179
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 };
    }
}

A titre d'exemple des différentes analyses syntaxiques :

<!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>
will be translated as:
===================
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 
===================
As opposed to:
===================

        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
===================

0 votes

Vous pouvez également gérer node.name tr nouvelle ligne et td espace pour améliorer la mise en forme dans les tableaux.

0 votes

Pouvez-vous afficher un code qui renvoie une chaîne de sortie formatée, c'est-à-dire la même quantité de nouvel espace de ligne, etc.

0 votes

Cela semble intéressant bien que cela ne gère pas les RH.

35voto

Richard Points 11249

Vous pourriez utiliser ceci :

 public static string StripHTML(string HTMLText)
        {
            Regex reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase);
            return reg.Replace(HTMLText, "");
        }

4 votes

Elle est incomplète... par exemple elle ne tient pas compte des entités comme &nbsp ; etc...

2 votes

C'était génial, et ce sera encore mieux si on le combine avec HtmlDecoded Je veux dire : 'HTMLText = HttpUtility.HtmlDecode(HTMLText);'.

4 votes

C'est en fait un excellent exemple ! Je l'ai utilisé dans mon application web. Tout notre contenu est stocké en HTML dans une base de données. Un exemple plus direct est de l'utiliser comme ceci. string test = HttpUtility.HtmlDecode(StripHTML(htmlText)) ;

17voto

madcolor Points 4077

J'ai entendu dire de source sûre que, si vous faites de l'analyse syntaxique HTML en .Net, vous devriez revoir le pack d'agilité HTML

http://www.codeplex.com/htmlagilitypack

Quelques échantillons sur SO..

http://stackoverflow.com/questions/655603/html-agility-pack-parsing-tables

1 votes

Bonne chance Je ne prétends pas que cela va être facile mais je pense que c'est le bon chemin à suivre.

12voto

FlySwat Points 61945

Ce que vous recherchez, c'est un moteur de rendu DOM en mode texte qui produise du texte, comme Lynx ou d'autres navigateurs texte... C'est beaucoup plus difficile à réaliser que vous ne le pensez.

0 votes

Non, en fait, ça facilite les choses ! !! (voir l'édition de la question). Merci encore !

3 votes

@MattCrouch en quoi cela facilite-t-il les choses ? La réponse de l'Edit 2 dans la question originale est à peine plus qu'un hack - complètement inacceptable pour moi et je soupçonne la situation de presque tout le monde - le reconnaîtriez-vous ?

3voto

inspite Points 8872

Avez-vous essayé http://www.aaronsw.com/2002/html2text/ c'est Python, mais open source.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X