Разбор HTML с помощью пакета Agility HTML и Linq - PullRequest
12 голосов
/ 06 января 2011

У меня есть следующий HTML

(..)
<tbody>
 <tr>
  <td class="name"> Test1 </td>
  <td class="data"> Data </td>
  <td class="data2"> Data 2 </td>
 </tr>
 <tr>
  <td class="name"> Test2 </td>
  <td class="data"> Data2 </td>
  <td class="data2"> Data 2 </td>
 </tr>
</tbody>
(..)

У меня есть информация name => so "Test1" и "Test2".Я хочу знать, как я могу получить данные, которые находятся в «data» и «data2», основываясь на имени, которое у меня есть.

В настоящее время я использую:

var data =
    from
        tr in doc.DocumentNode.Descendants("tr")
    from   
        td in tr.ChildNodes.Where(x => x.Attributes["class"].Value == "name")
    where
        td.InnerText == "Test1"
    select tr;

Но я получаю {"Object reference not set to an instance of an object."}, когда пытаюсь посмотреть в data

Ответы [ 5 ]

15 голосов
/ 06 января 2011

Что касается вашей попытки, у вас есть две проблемы с вашим кодом:

  1. ChildNodes странно - он также возвращает пробельные текстовые узлы, которые не имеют атрибутов class (конечно, не могут иметь атрибутов).
  2. Как заметил Джеймс Уолфорд, пробелы вокруг текста значительны, вы, вероятно, хотите их обрезать.

С этими двумя исправлениями работают следующие:

var data =
      from tr in doc.DocumentNode.Descendants("tr")
      from td in tr.Descendants("td").Where(x => x.Attributes["class"].Value == "name")
     where td.InnerText.Trim() == "Test1"
    select tr;
5 голосов
/ 06 января 2011

Вот способ XPATH - хм ... все, кажется, забыли о мощном XPATH и сосредоточены исключительно на C # XLinq, в эти дни: -)

Эта функция получает все значения данных, связанные с именем:

public static IEnumerable<string> GetData(HtmlDocument document, string name)
{
    return from HtmlNode node in
        document.DocumentNode.SelectNodes("//td[@class='name' and contains(text(), '" + name + "')]/following-sibling::td")
        select node.InnerText.Trim();
}

Например, этот код выведет все данные 'Test2':

    HtmlDocument doc = new HtmlDocument();
    doc.Load(yourHtml);

    foreach (string data in GetData(doc, "Test2"))
    {
        Console.WriteLine(data);
    }
1 голос
/ 06 января 2011

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

HtmlWeb hw = new HtmlWeb();
HtmlDocument doc = hw.Load("http://jsbin.com/ezuge4");
HtmlNodeCollection nodes = doc.DocumentNode
                              .SelectNodes("//table[@id='MyTable']//tr");
var data = nodes.Select(
    node => node.Descendants("td")
        .ToDictionary(descendant => descendant.Attributes["class"].Value,
                      descendant => descendant.InnerText.Trim())
        ).ToDictionary(dict => dict["name"]);
string test1Data = data["Test1"]["data"];

Здесь я превращаю каждые <tr> в словарь, где класс <td> является ключом, а текстценность.Затем я превращаю список словарей в словарь словарей (совет - абстрагируйте их), где ключом является name каждого <tr>.

0 голосов
/ 28 августа 2013

Я могу порекомендовать один из двух способов:

http://htmlagilitypack.codeplex.com/,, который преобразует html в действительный xml, который затем можно запросить с помощью OOTB Linq.

Или,

Linq to HTML (http://www.superstarcoders.com/linq-to-html.aspx),, который, хотя и не поддерживается в CodePlex (это был намек, Кит), дает разумный рабочий набор функций для трамплина с.

0 голосов
/ 06 января 2011

вместо

td.InnerText == "Test1"

попробовать

td.InnerText == " Test1 "

или

d.InnerText.Trim() == "Test1"
...