Что лучший способ состоит в том, чтобы проанализировать HTML в C#? [закрытый]

Другое решение состоит в создании ассоциативного массива с форматом даты и последующим переформатированием данных.

Этот метод полезен для даты, отформатированной беспорядочным способом.

Пример:

    mydate='01.02.12 10:20:43':
    myformat='dd/mm/yy HH:MM:ss';


    dtsplit=mydate.split(/[\/ .:]/);
    dfsplit=myformat.split(/[\/ .:]/);

    // creates assoc array for date
    df = new Array();
    for(dc=0;dc<6;dc++) {
            df[dfsplit[dc]]=dtsplit[dc];
            }

    // uses assc array for standard mysql format
    dstring[r] = '20'+df['yy']+'-'+df['mm']+'-'+df['dd'];
    dstring[r] += ' '+df['HH']+':'+df['MM']+':'+df['ss'];
66
задан Charles Stewart 3 January 2010 в 08:29
поделиться

13 ответов

Пакет Гибкости HTML

Это - гибкий синтаксический анализатор HTML, который создает чтение-запись DOM и поддерживает плоскость XPath или XSLT (Вы на самом деле не должны понимать XPath, ни XSLT для использования его, не волнуйтесь...). Это - библиотека кода.NET, которая позволяет Вам анализировать "из веб-" файлов HTML. Синтаксический анализатор очень терпим с "реальным миром" уродливый HTML. Объектная модель очень похожа на то, что предлагает System.Xml, но для документов HTML (или потоки).

137
ответ дан Mark Cidade 24 November 2019 в 14:48
поделиться

Вы могли использовать DTD HTML и универсальный XML парсинг библиотек.

0
ответ дан Corin Blaikie 24 November 2019 в 14:48
поделиться

Проблема проанализировать HTML состоит в том, что это не точная наука. Если бы это был XHTML, который Вы анализировали, то вещи были бы намного легче (поскольку Вы упоминаете, что могли использовать общий синтаксический анализатор XML). Поскольку HTML является не обязательно правильно построенным XML, Вы войдете в большое количество проблем, пытающихся проанализировать его. Это почти должно быть сделано на основе сайта сайтом.

1
ответ дан Mark Ingram 24 November 2019 в 14:48
поделиться

Я думаю, что использование @Erlend HTMLDocument лучшее способ пойти. Однако мне также везло, пользуясь этой простой библиотекой:

SgmlReader

3
ответ дан Frank Krueger 24 November 2019 в 14:48
поделиться

Я использовал ZetaHtmlTidy в прошлом, чтобы загрузить случайные веб-сайты и затем удариться о различные части содержания с xpath (например,/html/body//p [@class ='textblock']). Это работало хорошо, но были некоторые исключительные сайты, с которыми это имело проблемы, таким образом, я не знаю, является ли это абсолютное лучшее решение.

1
ответ дан Rahul 24 November 2019 в 14:48
поделиться

Пакет Гибкости HTML был упомянут, прежде - если Вы идете для скорости, Вы могли бы также хотеть проверить Величественные 12 синтаксических анализаторов HTML . Его обработка является довольно неуклюжей, но это обеспечивает действительно быстрый опыт парсинга.

7
ответ дан Grimtron 24 November 2019 в 14:48
поделиться

Я написал некоторый код, который предоставляет "LINQ HTML" функциональность. Я думал, что совместно использую его здесь. Это основано на Величественных 12. Это берет Величественные 12 результатов и производит элементы LINQ XML. В той точке можно использовать весь LINQ для инструментов XML против HTML. Как пример:

        IEnumerable<XNode> auctionNodes = Majestic12ToXml.Majestic12ToXml.ConvertNodesToXml(byteArrayOfAuctionHtml);

        foreach (XElement anchorTag in auctionNodes.OfType<XElement>().DescendantsAndSelf("a")) {

            if (anchorTag.Attribute("href") == null)
                continue;

            Console.WriteLine(anchorTag.Attribute("href").Value);
        }

я хотел использовать Величественные 12, потому что я знаю, что это имеет большое встроенное знание относительно HTML, который найден в дикой природе. Что я нашел, хотя то, что для отображения Величественных 12 результатов на что-то, что примет LINQ, поскольку XML требует дополнительной работы. Код, который я включаю, делает большую эту чистку, но поскольку Вы используете это, Вы найдете страницы, которые отклоняются. Необходимо будет согласовать код для обращения к этому. Когда исключение будет выдано, проверьте исключение. Данные ["источник"], поскольку это, вероятно, установлено на HTML-тэг, который вызвал исключение. Обработка HTML хорошим способом время от времени не тривиальна...

Поэтому теперь, когда ожидания являются реалистично низкими, вот код:)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Majestic12;
using System.IO;
using System.Xml.Linq;
using System.Diagnostics;
using System.Text.RegularExpressions;

namespace Majestic12ToXml {
public class Majestic12ToXml {

    static public IEnumerable<XNode> ConvertNodesToXml(byte[] htmlAsBytes) {

        HTMLparser parser = OpenParser();
        parser.Init(htmlAsBytes);

        XElement currentNode = new XElement("document");

        HTMLchunk m12chunk = null;

        int xmlnsAttributeIndex = 0;
        string originalHtml = "";

        while ((m12chunk = parser.ParseNext()) != null) {

            try {

                Debug.Assert(!m12chunk.bHashMode);  // popular default for Majestic-12 setting

                XNode newNode = null;
                XElement newNodesParent = null;

                switch (m12chunk.oType) {
                    case HTMLchunkType.OpenTag:

                        // Tags are added as a child to the current tag, 
                        // except when the new tag implies the closure of 
                        // some number of ancestor tags.

                        newNode = ParseTagNode(m12chunk, originalHtml, ref xmlnsAttributeIndex);

                        if (newNode != null) {
                            currentNode = FindParentOfNewNode(m12chunk, originalHtml, currentNode);

                            newNodesParent = currentNode;

                            newNodesParent.Add(newNode);

                            currentNode = newNode as XElement;
                        }

                        break;

                    case HTMLchunkType.CloseTag:

                        if (m12chunk.bEndClosure) {

                            newNode = ParseTagNode(m12chunk, originalHtml, ref xmlnsAttributeIndex);

                            if (newNode != null) {
                                currentNode = FindParentOfNewNode(m12chunk, originalHtml, currentNode);

                                newNodesParent = currentNode;
                                newNodesParent.Add(newNode);
                            }
                        }
                        else {
                            XElement nodeToClose = currentNode;

                            string m12chunkCleanedTag = CleanupTagName(m12chunk.sTag, originalHtml);

                            while (nodeToClose != null && nodeToClose.Name.LocalName != m12chunkCleanedTag)
                                nodeToClose = nodeToClose.Parent;

                            if (nodeToClose != null)
                                currentNode = nodeToClose.Parent;

                            Debug.Assert(currentNode != null);
                        }

                        break;

                    case HTMLchunkType.Script:

                        newNode = new XElement("script", "REMOVED");
                        newNodesParent = currentNode;
                        newNodesParent.Add(newNode);
                        break;

                    case HTMLchunkType.Comment:

                        newNodesParent = currentNode;

                        if (m12chunk.sTag == "!--")
                            newNode = new XComment(m12chunk.oHTML);
                        else if (m12chunk.sTag == "![CDATA[")
                            newNode = new XCData(m12chunk.oHTML);
                        else
                            throw new Exception("Unrecognized comment sTag");

                        newNodesParent.Add(newNode);

                        break;

                    case HTMLchunkType.Text:

                        currentNode.Add(m12chunk.oHTML);
                        break;

                    default:
                        break;
                }
            }
            catch (Exception e) {
                var wrappedE = new Exception("Error using Majestic12.HTMLChunk, reason: " + e.Message, e);

                // the original html is copied for tracing/debugging purposes
                originalHtml = new string(htmlAsBytes.Skip(m12chunk.iChunkOffset)
                    .Take(m12chunk.iChunkLength)
                    .Select(B => (char)B).ToArray()); 

                wrappedE.Data.Add("source", originalHtml);

                throw wrappedE;
            }
        }

        while (currentNode.Parent != null)
            currentNode = currentNode.Parent;

        return currentNode.Nodes();
    }

    static XElement FindParentOfNewNode(Majestic12.HTMLchunk m12chunk, string originalHtml, XElement nextPotentialParent) {

        string m12chunkCleanedTag = CleanupTagName(m12chunk.sTag, originalHtml);

        XElement discoveredParent = null;

        // Get a list of all ancestors
        List<XElement> ancestors = new List<XElement>();
        XElement ancestor = nextPotentialParent;
        while (ancestor != null) {
            ancestors.Add(ancestor);
            ancestor = ancestor.Parent;
        }

        // Check if the new tag implies a previous tag was closed.
        if ("form" == m12chunkCleanedTag) {

            discoveredParent = ancestors
                .Where(XE => m12chunkCleanedTag == XE.Name)
                .Take(1)
                .Select(XE => XE.Parent)
                .FirstOrDefault();
        }
        else if ("td" == m12chunkCleanedTag) {

            discoveredParent = ancestors
                .TakeWhile(XE => "tr" != XE.Name)
                .Where(XE => m12chunkCleanedTag == XE.Name)
                .Take(1)
                .Select(XE => XE.Parent)
                .FirstOrDefault();
        }
        else if ("tr" == m12chunkCleanedTag) {

            discoveredParent = ancestors
                .TakeWhile(XE => !("table" == XE.Name
                                    || "thead" == XE.Name
                                    || "tbody" == XE.Name
                                    || "tfoot" == XE.Name))
                .Where(XE => m12chunkCleanedTag == XE.Name)
                .Take(1)
                .Select(XE => XE.Parent)
                .FirstOrDefault();
        }
        else if ("thead" == m12chunkCleanedTag
                  || "tbody" == m12chunkCleanedTag
                  || "tfoot" == m12chunkCleanedTag) {


            discoveredParent = ancestors
                .TakeWhile(XE => "table" != XE.Name)
                .Where(XE => m12chunkCleanedTag == XE.Name)
                .Take(1)
                .Select(XE => XE.Parent)
                .FirstOrDefault();
        }

        return discoveredParent ?? nextPotentialParent;
    }

    static string CleanupTagName(string originalName, string originalHtml) {

        string tagName = originalName;

        tagName = tagName.TrimStart(new char[] { '?' });  // for nodes <?xml >

        if (tagName.Contains(':'))
            tagName = tagName.Substring(tagName.LastIndexOf(':') + 1);

        return tagName;
    }

    static readonly Regex _startsAsNumeric = new Regex(@"^[0-9]", RegexOptions.Compiled);

    static bool TryCleanupAttributeName(string originalName, ref int xmlnsIndex, out string result) {

        result = null;
        string attributeName = originalName;

        if (string.IsNullOrEmpty(originalName))
            return false;

        if (_startsAsNumeric.IsMatch(originalName))
            return false;

        //
        // transform xmlns attributes so they don't actually create any XML namespaces
        //
        if (attributeName.ToLower().Equals("xmlns")) {

            attributeName = "xmlns_" + xmlnsIndex.ToString(); ;
            xmlnsIndex++;
        }
        else {
            if (attributeName.ToLower().StartsWith("xmlns:")) {
                attributeName = "xmlns_" + attributeName.Substring("xmlns:".Length);
            }   

            //
            // trim trailing \"
            //
            attributeName = attributeName.TrimEnd(new char[] { '\"' });

            attributeName = attributeName.Replace(":", "_");
        }

        result = attributeName;

        return true;
    }

    static Regex _weirdTag = new Regex(@"^<!\[.*\]>$");       // matches "<![if !supportEmptyParas]>"
    static Regex _aspnetPrecompiled = new Regex(@"^<%.*%>$"); // matches "<%@ ... %>"
    static Regex _shortHtmlComment = new Regex(@"^<!-.*->$"); // matches "<!-Extra_Images->"

    static XElement ParseTagNode(Majestic12.HTMLchunk m12chunk, string originalHtml, ref int xmlnsIndex) {

        if (string.IsNullOrEmpty(m12chunk.sTag)) {

            if (m12chunk.sParams.Length > 0 && m12chunk.sParams[0].ToLower().Equals("doctype"))
                return new XElement("doctype");

            if (_weirdTag.IsMatch(originalHtml))
                return new XElement("REMOVED_weirdBlockParenthesisTag");

            if (_aspnetPrecompiled.IsMatch(originalHtml))
                return new XElement("REMOVED_ASPNET_PrecompiledDirective");

            if (_shortHtmlComment.IsMatch(originalHtml))
                return new XElement("REMOVED_ShortHtmlComment");

            // Nodes like "<br <br>" will end up with a m12chunk.sTag==""...  We discard these nodes.
            return null;
        }

        string tagName = CleanupTagName(m12chunk.sTag, originalHtml);

        XElement result = new XElement(tagName);

        List<XAttribute> attributes = new List<XAttribute>();

        for (int i = 0; i < m12chunk.iParams; i++) {

            if (m12chunk.sParams[i] == "<!--") {

                // an HTML comment was embedded within a tag.  This comment and its contents
                // will be interpreted as attributes by Majestic-12... skip this attributes
                for (; i < m12chunk.iParams; i++) {

                    if (m12chunk.sTag == "--" || m12chunk.sTag == "-->")
                        break;
                }

                continue;
            }

            if (m12chunk.sParams[i] == "?" && string.IsNullOrEmpty(m12chunk.sValues[i]))
                continue;

            string attributeName = m12chunk.sParams[i];

            if (!TryCleanupAttributeName(attributeName, ref xmlnsIndex, out attributeName))
                continue;

            attributes.Add(new XAttribute(attributeName, m12chunk.sValues[i]));
        }

        // If attributes are duplicated with different values, we complain.
        // If attributes are duplicated with the same value, we remove all but 1.
        var duplicatedAttributes = attributes.GroupBy(A => A.Name).Where(G => G.Count() > 1);

        foreach (var duplicatedAttribute in duplicatedAttributes) {

            if (duplicatedAttribute.GroupBy(DA => DA.Value).Count() > 1)
                throw new Exception("Attribute value was given different values");

            attributes.RemoveAll(A => A.Name == duplicatedAttribute.Key);
            attributes.Add(duplicatedAttribute.First());
        }

        result.Add(attributes);

        return result;
    }

    static HTMLparser OpenParser() {
        HTMLparser oP = new HTMLparser();

        // The code+comments in this function are from the Majestic-12 sample documentation.

        // ...

        // This is optional, but if you want high performance then you may
        // want to set chunk hash mode to FALSE. This would result in tag params
        // being added to string arrays in HTMLchunk object called sParams and sValues, with number
        // of actual params being in iParams. See code below for details.
        //
        // When TRUE (and its default) tag params will be added to hashtable HTMLchunk (object).oParams
        oP.SetChunkHashMode(false);

        // if you set this to true then original parsed HTML for given chunk will be kept - 
        // this will reduce performance somewhat, but may be desireable in some cases where
        // reconstruction of HTML may be necessary
        oP.bKeepRawHTML = false;

        // if set to true (it is false by default), then entities will be decoded: this is essential
        // if you want to get strings that contain final representation of the data in HTML, however
        // you should be aware that if you want to use such strings into output HTML string then you will
        // need to do Entity encoding or same string may fail later
        oP.bDecodeEntities = true;

        // we have option to keep most entities as is - only replace stuff like &nbsp; 
        // this is called Mini Entities mode - it is handy when HTML will need
        // to be re-created after it was parsed, though in this case really
        // entities should not be parsed at all
        oP.bDecodeMiniEntities = true;

        if (!oP.bDecodeEntities && oP.bDecodeMiniEntities)
            oP.InitMiniEntities();

        // if set to true, then in case of Comments and SCRIPT tags the data set to oHTML will be
        // extracted BETWEEN those tags, rather than include complete RAW HTML that includes tags too
        // this only works if auto extraction is enabled
        oP.bAutoExtractBetweenTagsOnly = true;

        // if true then comments will be extracted automatically
        oP.bAutoKeepComments = true;

        // if true then scripts will be extracted automatically: 
        oP.bAutoKeepScripts = true;

        // if this option is true then whitespace before start of tag will be compressed to single
        // space character in string: " ", if false then full whitespace before tag will be returned (slower)
        // you may only want to set it to false if you want exact whitespace between tags, otherwise it is just
        // a waste of CPU cycles
        oP.bCompressWhiteSpaceBeforeTag = true;

        // if true (default) then tags with attributes marked as CLOSED (/ at the end) will be automatically
        // forced to be considered as open tags - this is no good for XML parsing, but I keep it for backwards
        // compatibility for my stuff as it makes it easier to avoid checking for same tag which is both closed
        // or open
        oP.bAutoMarkClosedTagsWithParamsAsOpen = false;

        return oP;
    }
}
}  
9
ответ дан Frank Schwieterman 24 November 2019 в 14:48
поделиться

Можно сделать много, не сходя с ума на сторонних продуктах и mshtml (т.е. interop). используйте Систему. Windows. Формы. WebBrowser. Оттуда, можно сделать такие вещи как "GetElementById" на HtmlDocument или "GetElementsByTagName" на HtmlElements. Если Вы хотите на самом деле взаимодействовать через интерфейс с браузером (моделируйте нажатия кнопок, например), можно использовать немного отражения (imo меньшее зло, чем Interop), чтобы сделать это:

var wb = new WebBrowser()

... говорят браузеру перейти (тангенциальный к этому вопросу). Тогда на событии Document_Completed можно моделировать щелчки как это.

var doc = wb.Browser.Document
var elem = doc.GetElementById(elementId);
object obj = elem.DomElement;
System.Reflection.MethodInfo mi = obj.GetType().GetMethod("click");
mi.Invoke(obj, new object[0]);

можно сделать подобный отражательный материал, чтобы утверждать, что формы, и т.д.

Обладают.

10
ответ дан musefan 24 November 2019 в 14:48
поделиться

Вы могли использовать TidyNet. Опрятный, чтобы преобразовать HTML в XHTML, и затем использовать синтаксический анализатор XML.

Другая альтернатива должна была бы использовать встроенный механизм mshtml:

using mshtml;
...
object[] oPageText = { html };
HTMLDocument doc = new HTMLDocumentClass();
IHTMLDocument2 doc2 = (IHTMLDocument2)doc;
doc2.write(oPageText);

Это позволяет Вам использовать подобные JavaScript функции как getElementById ()

27
ответ дан Erlend 24 November 2019 в 14:48
поделиться

Используйте WatiN, если вам нужно увидеть влияние JS на страницу [и вы готовы запустить браузер]

0
ответ дан 24 November 2019 в 14:48
поделиться

Я нашел проект под названием Fizzler, который использует подход jQuery / Sizzler для выбора элементов HTML. Он основан на HTML Agility Pack. В настоящее время он находится в стадии бета-тестирования и поддерживает только подмножество селекторов CSS, но чертовски круто и освежающе использовать селекторы CSS вместо неприятного XPath.

http://code.google.com/p/fizzler/

16
ответ дан 24 November 2019 в 14:48
поделиться

В зависимости от ваших потребностей вы можете использовать более функциональные библиотеки. Я попробовал большинство/все предложенные решения, но что выделялось, так это Html Agility Pack. Это очень прощающий и гибкий парсер.

0
ответ дан 24 November 2019 в 14:48
поделиться

Попробуйте этот сценарий.

http://www.biterscripting.com/SS_URLs.html

Когда я использую его с этим URL,

script SS_URLs.txt URL("http://stackoverflow.com/questions/56107/what-is-the-best-way-to-parse-html-in-c")

он показывает мне все ссылки на странице этого потока.

http://sstatic.net/so/all.css
http://sstatic.net/so/favicon.ico
http://sstatic.net/so/apple-touch-icon.png
.
.
.

Вы можете изменить этот сценарий, чтобы проверять изображения, переменные и т. Д.

0
ответ дан 24 November 2019 в 14:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: