Синтаксический анализ HTML-документа: регулярное выражение или LINQ?

попытка проанализировать HTML-документ и извлечь некоторые элементы (любые ссылки на текстовые файлы).

текущая стратегия заключается в загрузке HTML-документа в строку. Затем найдите все экземпляры ссылок на текстовые файлы. Это может быть любой тип файла, но для этого вопроса это текстовый файл.

конечная цель состоит в том, чтобы иметь IEnumerable список строковых объектов. Эта часть проста, но разбор данных-это вопрос.

<html>
<head><title>Blah</title>
</head>
<body>
<br/>
<div>Here is your first text file: <a href="http://myServer.com/blah.txt"></div>
<span>Here is your second text file: <a href="http://myServer.com/blarg2.txt"></span>
<div>Here is your third text file: <a href="http://myServer.com/bat.txt"></div>
<div>Here is your fourth text file: <a href="http://myServer.com/somefile.txt"></div>
<div>Thanks for visiting!</div>
</body>
</html>

первые подходы являются:

  • загрузите строку в XML-документ и атакуйте ее способом Linq-to-Xml.
  • создайте регулярное выражение, чтобы найти строку, начинающуюся с href= и заканчивая .txt

вопрос:

  • как будет выглядеть это регулярное выражение? Я новичок в regex, и это часть моего обучения тестирую.
  • какой метод вы бы использовали для извлечения списка тегов?
  • что бы самый эффективный способ?
  • какой метод будет наиболее читабельным/ремонтопригодным?


обновление: Слава Матвей по предложению пакета гибкости HTML. Это сработало просто отлично! Предложение XPath также работает. Я хотел бы отметить оба ответа как "ответ", но, очевидно, не могу. Они оба являются верными решениями проблемы.

вот консольное приложение C#, использующее регулярное выражение, предложенное Джефф. Он отлично читает строку и не будет включать в себя href, который не заканчивается .формат txt. С данным образцом он правильно не включает .txt.snarg файл в результатах (как указано в Строковой функции HTML).

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace ParsePageLinks
{
    class Program
    {
        static void Main(string[] args)
        {
            GetAllLinksFromStringByRegex();
        }

        static List<string> GetAllLinksFromStringByRegex()
        {
            string myHtmlString = BuildHtmlString();
            string txtFileExp = "href="([^\"]*.txt)"";

            List<string> foundTextFiles = new List<string>();

            MatchCollection textFileLinkMatches = Regex.Matches(myHtmlString, txtFileExp, RegexOptions.IgnoreCase);
            foreach (Match m in textFileLinkMatches)
            {
                foundTextFiles.Add( m.Groups[1].ToString()); // this is your captured group
            }

            return files;
        }

            static string BuildHtmlString()
            {
                return new StringReader(@"<html><head><title>Blah</title></head><body><br/>
<div>Here is your first text file: <a href=""http://myServer.com/blah.txt""></div>
<span>Here is your second text file: <a href=""http://myServer.com/blarg2.txt""></span>
<div>Here is your third text file: <a href=""http://myServer.com/bat.txt.snarg""></div>
<div>Here is your fourth text file: <a href=""http://myServer.com/somefile.txt""></div>
<div>Thanks for visiting!</div></body></html>").ReadToEnd();
            }       
        }
    }

4 ответов


Я бы рекомендовал regex. Почему?

  • гибкий (случай-нечувствительность, легко добавление новых расширений файлов, элементов в проверить, и т. д.)
  • быстро писать
  • быстрый запуск

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

используя это регулярное выражение:

href="([^"]*\.txt)"

объяснение:

  • в скобках вокруг имя файла, которое приведет к "захваченная группа", к которой вы можете получить доступ после каждого матча был найден.
  • он должен бежать "."используя regex escape-символ, обратная косая черта.
  • он должен соответствовать любому символу, кроме двойные кавычки: [^"] пока не найдет
    этот." txt"

это переводится в экранированную строку, как это:

string txtExp = "href=\"([^\\"]*\.txt)\"

затем вы можете перебирать свои матчи:

Matches txtMatches = Regex.Matches(input, exp, RegexOptions.IgnoreCase);
foreach(Match m in txtMatches) {
  string filename = m.Groups[1]; // this is your captured group
}

ни. Загрузите его в (X/HT)MLDocument и используйте XPath, который является стандартным методом управления XML и очень мощным. Функции для просмотра являются метод selectnodes и SelectSingleNode.

поскольку вы, по-видимому, используете HTML (не XHTML), вы должны использовать HTML Agility Pack. Большинство методов и свойств соответствуют связанным классам XML.

пример реализации с использованием XPath:

    HtmlDocument doc = new HtmlDocument();
    doc.Load(new StringReader(@"<html>
<head><title>Blah</title>
</head>
<body>
<br/>
<div>Here is your first text file: <a href=""http://myServer.com/blah.txt""></div>
<span>Here is your second text file: <a href=""http://myServer.com/blarg2.txt""></span>
<div>Here is your third text file: <a href=""http://myServer.com/bat.txt""></div>
<div>Here is your fourth text file: <a href=""http://myServer.com/somefile.txt""></div>
<div>Thanks for visiting!</div>
</body>
</html>"));
        HtmlNode root = doc.DocumentNode;
        // 3 = ".txt".Length - 1.  See http://stackoverflow.com/questions/402211/how-to-use-xpath-function-in-a-xpathexpression-instance-programatically
        HtmlNodeCollection links = root.SelectNodes("//a[@href['.txt' = substring(., string-length(.)- 3)]]");
    IList<string> fileStrings;
    if(links != null)
    {
        fileStrings = new List<string>(links.Count);
        foreach(HtmlNode link in links)
        fileStrings.Add(link.GetAttributeValue("href", null));
    }
    else
        fileStrings = new List<string>(0);

кроме того, Евангелие от Матфея предложение горнолыжный подъемник flaschen, а DOM (например, если вы страдаете от Х?L вспышка аллергии)

иногда он получает плохую репутацию-я думаю, потому что реализации are иногда смешно, и собственные com-интерфейсы немного громоздкие без некоторых (незначительных) умных помощников, но я нашел его надежным, стабильным и интуитивно понятным / объяснимым способом анализа и управления HTML.


регулярное выражение не быстрое, на самом деле оно медленнее, чем собственный синтаксический анализ строк .Сеть. Не верьте мне, убедитесь сами.

ни один из приведенных выше примеров не быстрее, чем переход к DOM напрямую.

HTMLDocument doc = wb.Document;
var links = doc.Links;