Разбор HTML-документа: регулярное выражение или LINQ? - PullRequest
8 голосов
/ 25 мая 2009

Попытка разобрать 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

Вопрос:

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


Обновление: Престижность Мэтью в предложении HTML Agility Pack. Работало просто отлично! Предложение XPath также работает. Хотелось бы пометить оба ответа как «Ответ», но я, очевидно, не могу. Оба они являются правильными решениями проблемы.

Вот консольное приложение C #, использующее регулярное выражение, предложенное Jeff . Он отлично читает строку и не будет содержать 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 ]

13 голосов
/ 25 мая 2009

Ни. Загрузите его в (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 /250923/kak-programmno-ispolzovat-funktsiy-xpath-v-ekzemplyare-xpathexpression
        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);
1 голос
/ 25 мая 2009

Я бы порекомендовал регулярное выражение. Почему?

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

Выражения регулярных выражений не будут трудно читать, если вы можете ЗАПИСАТЬ регулярные выражения.

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

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

Пояснение:

  • Скобки вокруг имя файла, что приведет к «захваченная группа», к которой вы можете получить доступ после каждого совпадения.
  • Он должен избежать "." используя escape-символ regex, обратный слеш.
  • Он должен соответствовать любому персонажу, КРОМЕ двойные кавычки: [^ "] пока не найдет
    ".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
}
0 голосов
/ 01 марта 2011

REGEX не быстрый, на самом деле он медленнее, чем собственный анализ строк в .NET. Не верьте мне, убедитесь сами.

Ни один из приведенных выше примеров не быстрее, чем прямой переход в DOM.

HTMLDocument doc = wb.Document;
var links = doc.Links;
0 голосов
/ 25 мая 2009

В качестве альтернативы предложению Мэтью Флашена, DOM (например, если вы страдаете от аллергии X? L)

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

...