如何在 C# 中使用 HTMLNode 和 HtmlAgility-Pack 从网站获取值
How to get values from website using HTMLNode and HtmlAgility-Pack in C#
我正在尝试从中获取数据 website
我想从 table 获得:级别、职业和姓名。
它们直接位于 tr class -> td。我怎样才能得到这些信息?
数据是这样的:
<table width="100%" class="tabi">
<tr>
<td colspan=7>
Characters
</td>
</tr>
<tr>
<td height='30' style='background-color:#9f8f6d;'>
<a href=?page=whoisonline&ord=name&sort=DESC&id=1>↑Name</a>
</td>
<td width='240' style='background-color:#9f8f6d;'>
<a href=?page=whoisonline&ord=voc&sort=DESC&id=1>Vocation</a>
</td>
<td width='120' style='background-color:#9f8f6d;'>
<a href=?page=whoisonline&ord=lvl&sort=DESC&id=1>Level</a>
</td>
</tr>
<tr class='hover'>
<td>
<a href='?page=character&name=Abe' class='menulink_hs'>Abe</a>
</td>
<td>
Elder Druid
</td>
<td>
19
</td>
</tr>
现在我卡住了 使用节点从 tds 中获取这些数据,结果很糟糕。我的 htmlNodes 要么是 NULL,要么它提供了多个节点(由于某种原因我实际上无法摆脱它)。 对此有什么好的解决方案?
我的代码:
var html = @"https://tibiantis.online/?page=whoisonline";
HtmlWeb web = new HtmlWeb();
var htmlDoc = web.Load(html);
HtmlNode htmlNodes = htmlDoc.DocumentNode.SelectSingleNode("/html/body/div[2]/table/tbody/tr[1]/td[3]/div[2]/div[2]/table/tbody/tr[3]");
foreach (var node in htmlNodes)
{
foreach (var cell in htmlNodes.SelectNodes(".//td"))
{
listBox1.Items.Add(cell.InnerText);
}
}
** 我被这个 .SelectNodes 问题困住了,不管是什么给我 null 或太多节点。我尝试了很多与 .SelectSingleNode 和 .SelectNode 的组合 **
其次,我不知道如何获得我将收到的物品数量。
我在堆栈上寻找答案,google 得到了一些结果,但没有一个接近我的情况
试试这个:
public class Person
{
public string Name { get; set; }
public string Vocation { get; set; }
public int Level { get; set; }
public static List<Person> LoadPersons(HtmlAgilityPack.HtmlDocument doc)
{
var persons = new List<Person>();
var rowsNodes = doc.DocumentNode.SelectNodes("//table//tr[contains(@class, 'hover')]");
if (rowsNodes == null)
{
return persons;
}
foreach (var rowNode in rowsNodes)
{
var cells = rowNode.SelectNodes(".//td");
if (cells != null && cells.Count >= 3)
{
var name = cells[0].InnerText;
var vocation = cells[1].InnerText;
var levelText = cells[2].InnerText;
if (int.TryParse(levelText, out int level))
{
persons.Add(new Person
{
Name = name,
Vocation = vocation,
Level = level
});
}
}
}
return persons;
}
}
这个 class 代表一个人(table 中的一行)并包含一个废弃 table 的方法。当你进行抓取时,你必须尝试变得有点笼统,因为将所有标签放入查询中会使查询失败并有一点 HTML 更改。
我只是在文档 (//) 中搜索 table 并且在 table 中搜索(// 因为可能某些浏览器会自动添加 tbody),select 所有带有“悬停”的行 (tr) class(您的人)。
迭代每一行获取 3 个单元格文本。最后一个(级别),转换为整数。然后,创建人。
现在,您可以创建一个 class 来定义列表中的每个项目。当我从 ListBox 中获取一个项目时,我几乎总是创建一个 class 来使用 class(将 selected 项目作为 PersonItem 并对其进行任何处理......):
public class PersonItem
{
public PersonItem(Person person)
{
this.Person = person;
}
public Person Person { get; }
public override string ToString()
{
return $"{this.Person.Name} ({this.Person.Level})";
}
}
它只是 Person 的包装器。用要在列表框中显示的文本覆盖 ToString。
测试一下:
var web = new HtmlWeb();
var doc = web.Load("https://tibiantis.online/?page=whoisonline");
var persons = Person.LoadPersons(doc);
foreach (var person in persons)
{
var item = new PersonItem(person);
listBox1.Items.Add(item);
}
// In any moment, you may do things like this:
var personItem = listBox1.SelectedItem as PersonItem;
if (personItem != null)
{
var person = personItem.Person;
// ...
}
我正在尝试从中获取数据 website
我想从 table 获得:级别、职业和姓名。 它们直接位于 tr class -> td。我怎样才能得到这些信息? 数据是这样的:
<table width="100%" class="tabi">
<tr>
<td colspan=7>
Characters
</td>
</tr>
<tr>
<td height='30' style='background-color:#9f8f6d;'>
<a href=?page=whoisonline&ord=name&sort=DESC&id=1>↑Name</a>
</td>
<td width='240' style='background-color:#9f8f6d;'>
<a href=?page=whoisonline&ord=voc&sort=DESC&id=1>Vocation</a>
</td>
<td width='120' style='background-color:#9f8f6d;'>
<a href=?page=whoisonline&ord=lvl&sort=DESC&id=1>Level</a>
</td>
</tr>
<tr class='hover'>
<td>
<a href='?page=character&name=Abe' class='menulink_hs'>Abe</a>
</td>
<td>
Elder Druid
</td>
<td>
19
</td>
</tr>
现在我卡住了 使用节点从 tds 中获取这些数据,结果很糟糕。我的 htmlNodes 要么是 NULL,要么它提供了多个节点(由于某种原因我实际上无法摆脱它)。 对此有什么好的解决方案?
我的代码:
var html = @"https://tibiantis.online/?page=whoisonline";
HtmlWeb web = new HtmlWeb();
var htmlDoc = web.Load(html);
HtmlNode htmlNodes = htmlDoc.DocumentNode.SelectSingleNode("/html/body/div[2]/table/tbody/tr[1]/td[3]/div[2]/div[2]/table/tbody/tr[3]");
foreach (var node in htmlNodes)
{
foreach (var cell in htmlNodes.SelectNodes(".//td"))
{
listBox1.Items.Add(cell.InnerText);
}
}
** 我被这个 .SelectNodes 问题困住了,不管是什么给我 null 或太多节点。我尝试了很多与 .SelectSingleNode 和 .SelectNode 的组合 **
其次,我不知道如何获得我将收到的物品数量。
我在堆栈上寻找答案,google 得到了一些结果,但没有一个接近我的情况
试试这个:
public class Person
{
public string Name { get; set; }
public string Vocation { get; set; }
public int Level { get; set; }
public static List<Person> LoadPersons(HtmlAgilityPack.HtmlDocument doc)
{
var persons = new List<Person>();
var rowsNodes = doc.DocumentNode.SelectNodes("//table//tr[contains(@class, 'hover')]");
if (rowsNodes == null)
{
return persons;
}
foreach (var rowNode in rowsNodes)
{
var cells = rowNode.SelectNodes(".//td");
if (cells != null && cells.Count >= 3)
{
var name = cells[0].InnerText;
var vocation = cells[1].InnerText;
var levelText = cells[2].InnerText;
if (int.TryParse(levelText, out int level))
{
persons.Add(new Person
{
Name = name,
Vocation = vocation,
Level = level
});
}
}
}
return persons;
}
}
这个 class 代表一个人(table 中的一行)并包含一个废弃 table 的方法。当你进行抓取时,你必须尝试变得有点笼统,因为将所有标签放入查询中会使查询失败并有一点 HTML 更改。
我只是在文档 (//) 中搜索 table 并且在 table 中搜索(// 因为可能某些浏览器会自动添加 tbody),select 所有带有“悬停”的行 (tr) class(您的人)。
迭代每一行获取 3 个单元格文本。最后一个(级别),转换为整数。然后,创建人。
现在,您可以创建一个 class 来定义列表中的每个项目。当我从 ListBox 中获取一个项目时,我几乎总是创建一个 class 来使用 class(将 selected 项目作为 PersonItem 并对其进行任何处理......):
public class PersonItem
{
public PersonItem(Person person)
{
this.Person = person;
}
public Person Person { get; }
public override string ToString()
{
return $"{this.Person.Name} ({this.Person.Level})";
}
}
它只是 Person 的包装器。用要在列表框中显示的文本覆盖 ToString。
测试一下:
var web = new HtmlWeb();
var doc = web.Load("https://tibiantis.online/?page=whoisonline");
var persons = Person.LoadPersons(doc);
foreach (var person in persons)
{
var item = new PersonItem(person);
listBox1.Items.Add(item);
}
// In any moment, you may do things like this:
var personItem = listBox1.SelectedItem as PersonItem;
if (personItem != null)
{
var person = personItem.Person;
// ...
}