Спарсить таблицу Html Agility pack c#

147
26 декабря 2019, 23:10

Есть вот такая таблица

<table cellspacing="0" cellpadding="0" cols="4" alt="Таблица с разным большая и красивая N1" title="Таблица с разным большая и красивая N1" border="0" style="border-collapse:collapse;WIDTH:135.73mm;min-width: 135.73mm;" class="a76">
    <tbody>
        <tr height="0">
            <td style="WIDTH:36.78mm;min-width: 36.78mm"></td>
            <td style="WIDTH:36.78mm;min-width: 36.78mm"></td>
            <td style="WIDTH:36.78mm;min-width: 36.78mm"></td>
            <td style="WIDTH:25.40mm;min-width: 25.40mm"></td>
        </tr>
        <tr valign="top">
            <td style="HEIGHT:11.24mm;" class="a40c">
                <div class="a40">n</div>
            </td>
            <td class="a44c">
                <div class="a44">DCRF</div>
            </td>
            <td class="a48c">
                <div class="a48">DCRI</div>
            </td>
            <td class="a52c">
                <div class="a52">DCRO</div>
            </td>
        </tr>
        <tr valign="top">
            <td style="HEIGHT:11.24mm;" class="a57cr">
                <div class="a57">5122</div>
            </td>
            <td class="a61cl">
                <div class="a61">Алла</div>
            </td>
            <td class="a65cl">
                <div class="a65">должна</div>
            </td>
            <td class="a69cl">
                <div class="a69">ехать</div>
            </td>
        </tr>
    </tbody>
</table>

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

Вот то что я написал:

var query = (from table in webGet.DocumentNode.SelectNodes("//table").Cast<HtmlNode>()
                    from row in table.SelectNodes("tr").Cast<HtmlNode>()
                    from cell in row.SelectNodes("th|td").Cast<HtmlNode>()
                    where table.Attributes["alt"] != null
                    select new { Table = table.Attributes["alt"].Value,Row = row.InnerText, CellText = cell.InnerText });
foreach (var cell in query)
{
    Console.WriteLine("{0}.{1}: {2}", cell.Table, cell.Row, cell.CellText);
}

Результат такой:

Таблица с разным большая и красивая N1.:
Таблица с разным большая и красивая N1.:
Таблица с разным большая и красивая N1.:
Таблица с разным большая и красивая N1.:
Таблица с разным большая и красивая N1.nDCRFDCRIDCRO: n
Таблица с разным большая и красивая N1.nDCRFDCRIDCRO: DCRF
Таблица с разным большая и красивая N1.nDCRFDCRIDCRO: DCRI
Таблица с разным большая и красивая N1.nDCRFDCRIDCRO: DCRO
Таблица с разным большая и красивая N1.5122АллаДолжнаехать: 5122
Таблица с разным большая и красивая N1.5122АллаДолжнаехать: Алла
Таблица с разным большая и красивая N1.5122АллаДолжнаехать: Должна
Таблица с разным большая и красивая N1.5122АллаДолжнаехать: ехать

А надо так :

Таблица с разным большая и красивая N1.n: 5122
Таблица с разным большая и красивая N1.DCRF: Алла
Таблица с разным большая и красивая N1.DCRI: Должна
Таблица с разным большая и красивая N1.DCRO: ехать
Answer 1

Можно попробовать такой вариант:

var tableNode = webGet.DocumentNode.SelectSingleNode("//table");
var result = tableNode
    .SelectNodes(".//tr").Where(x => x.GetAttributeValue("valign", null) == "top")
    .Select(x => x.SelectNodes(".//td").Select(s => s.InnerText.Trim()))
    .Cast<IEnumerable<dynamic>>()
    .Aggregate((first, second) => first.Zip(second, (f, s) => new { Table = tableNode.GetAttributeValue("alt", null), First = f, Second = s }));
foreach (var item in result)
    Console.WriteLine($"{item.Table}.{item.First}: {item.Second}");

Поясню:

  • webGet.DocumentNode.SelectSingleNode("//table") - тут мы берем первую таблицу из HTML. Так, как вы предоставили всего лишь ее код, предположу что она у вас одна на всю страницу, если нет - ищите нужную.

  • .SelectNodes(".//tr").Where(x => x.GetAttributeValue("valign", null) == "top") - Берем все tr элементы у которых атрибут valign равен top.

  • .Select(x => x.SelectNodes(".//td").Select(s => s.InnerText.Trim())) - берем все td элементы и делаем из них string (забирая только внутренний текст).
  • .Cast<IEnumerable<dynamic>>() - Переводим все в коллекцию динамичных типов для возможности в дальнейшем использовать анонимные типы.
  • .Aggregate((first, second) => first.Zip(second, (f, s) => new { Table = tableNode.GetAttributeValue("alt", null), First = f, Second = s })); - берем из полученного результата (а на этом этапе у нас 2 коллекции) первую и последующую коллекцию и "сшиваем" их в одну. На выход отдаем анонимные типы с нужными дам данными.

Результатом у нас будет примерно следующее:

Остается только вывести с нужным видом (foreach):

Таблица с разным большая и красивая N1.n: 5122
Таблица с разным большая и красивая N1.DCRF: Алла
Таблица с разным большая и красивая N1.DCRI: должна
Таблица с разным большая и красивая N1.DCRO: ехать

P.S. Я не уверен в результате, если у нас больше 2-х tr (скорей всего будет вложение анонимного типа в анонимный), код проверен только на предоставленном HTML!

READ ALSO
Организация запроса к MS SQL Server (EF 6) используя LINQ

Организация запроса к MS SQL Server (EF 6) используя LINQ

при проектировании собственного проекта на языке С # технологии WPF и запроса к данным на Entity Framework 6 встал вопрос как реализовать один запросУ...

104
Обработка произвольного название метода

Обработка произвольного название метода

Не знаю как правильно объяснить, суть вот в чем

131
Запись несколько переменных в ByteArray

Запись несколько переменных в ByteArray

записываю число в байтовый массив и далее массив отправляю в сокет клиенту

141