我正在尝试为一个名为Tibia的游戏添加插件。
在他们的网站Tibia.com上,你可以搜索人们并查看他们的死亡情况。
例如:
http://www.tibia.com/community/?subtopic=characters&name=Kixus
现在我想通过在我的C#应用程序中使用Regex来读取死亡数据。
但我似乎无法解决,我已经在上花了好几个小时了
http://myregextester.com/index.php
我使用的表达式是:
<tr bgcolor=(?:"#D4C0A1"|"#F1E0C6") ><td width="25%" valign="top" >(.*?)?#160;CET</td><td>((?:Died|Killed) at Level ([^ ]*)|and) by (?:<[^>]*>)?([^<]*).</td></tr>
但我无法让它发挥作用。
我想要时间戳、生物/玩家等级和生物/玩家名称
提前谢谢。
-问候
使用正则表达式解析HTML是个坏主意。对于这份工作来说,它们是一个非常糟糕的工具。如果您正在解析HTML,请使用HTML解析器。
对于.NET,通常的建议是使用HTML敏捷包。
正如Joe White所建议的,如果您使用HTML解析器来完成此任务,那么您将拥有一个更加健壮的实现。StackOverflow对此有很多支持:例如,请参阅此处。
如果您真的必须使用regexs
我建议将您的解决方案分解为更简单的regexs,可以使用自上而下的解析方法来应用该regexs以获得结果。
例如:
-
在整个页面上使用与字符表匹配的正则表达式
我建议在表前后匹配最短的唯一字符串,而不是表本身,并使用组捕获表,因为这样可以避免处理嵌套表的可能性。
-
在与表行匹配的字符表上使用正则表达式
- 在第一个单元格上使用正则表达式来匹配日期
- 在第二个单元格中使用正则表达式来匹配链接
- 在第二个单元格中使用正则表达式来匹配玩家级别
- 如果是生物,在第二个单元格上使用正则表达式匹配杀手名称(单元格中没有链接)
如果站点显著更改其Html结构,这将更易于维护。
使用HtmlAgilityKit的完整工作实现
您可以从CodePlex上的HtmlAgilityKit站点下载库。
// This class is used to represent the extracted details
public class DeathDetails
{
public DeathDetails()
{
this.KilledBy = new List<string>();
}
public string DeathDate { get; set; }
public List<String> KilledBy { get; set; }
public int PlayerLevel { get; set; }
}
public class CharacterPageParser
{
public string CharacterName { get; private set; }
public CharacterPageParser(string characterName)
{
this.CharacterName = characterName;
}
public List<DeathDetails> GetDetails()
{
string url = "http://www.tibia.com/community/?subtopic=characters&name=" + this.CharacterName;
string content = GetContent(url);
HtmlDocument document = new HtmlDocument();
document.LoadHtml(content);
HtmlNodeCollection tables = document.DocumentNode.SelectNodes("//div[@id='characters']//table");
HtmlNode table = GetCharacterDeathsTable(tables);
List<DeathDetails> deaths = new List<DeathDetails>();
for (int i = 1; i < table.ChildNodes.Count; i++)
{
DeathDetails details = BuildDeathDetails(table, i);
deaths.Add(details);
}
return deaths;
}
private static string GetContent(string url)
{
using (System.Net.WebClient c = new System.Net.WebClient())
{
string content = c.DownloadString(url);
return content;
}
}
private static DeathDetails BuildDeathDetails(HtmlNode table, int i)
{
DeathDetails details = new DeathDetails();
HtmlNode tableRow = table.ChildNodes[i];
//every row should have two cells in it
if (tableRow.ChildNodes.Count != 2)
{
throw new Exception("Html format may have changed");
}
HtmlNode deathDateCell = tableRow.ChildNodes[0];
details.DeathDate = System.Net.WebUtility.HtmlDecode(deathDateCell.InnerText);
HtmlNode deathDetailsCell = tableRow.ChildNodes[1];
// get inner text to parse for player level and or creature name
string deathDetails = System.Net.WebUtility.HtmlDecode(deathDetailsCell.InnerText);
// get player level using regex
Match playerLevelMatch = Regex.Match(deathDetails, @" level ([d]+) ", RegexOptions.IgnoreCase);
int playerLevel = 0;
if (int.TryParse(playerLevelMatch.Groups[1].Value, out playerLevel))
{
details.PlayerLevel = playerLevel;
}
if (deathDetailsCell.ChildNodes.Count > 1)
{
// death details contains links which we can parse for character names
foreach (HtmlNode link in deathDetailsCell.ChildNodes)
{
if (link.OriginalName == "a")
{
string characterName = System.Net.WebUtility.HtmlDecode(link.InnerText);
details.KilledBy.Add(characterName);
}
}
}
else
{
// player was killed by a creature - capture creature name
Match creatureMatch = Regex.Match(deathDetails, " by (.*)", RegexOptions.IgnoreCase);
string creatureName = creatureMatch.Groups[1].Value;
details.KilledBy.Add(creatureName);
}
return details;
}
private static HtmlNode GetCharacterDeathsTable(HtmlNodeCollection tables)
{
foreach (HtmlNode table in tables)
{
// Get first row
HtmlNode tableRow = table.ChildNodes[0];
// check to see if contains enough elements
if (tableRow.ChildNodes.Count == 1)
{
HtmlNode tableCell = tableRow.ChildNodes[0];
string title = tableCell.InnerText;
// skip this table if it doesn't have the right title
if (title == "Character Deaths")
{
return table;
}
}
}
return null;
}
还有一个使用中的例子:
CharacterPageParser kixusParser = new CharacterPageParser("Kixus");
foreach (DeathDetails details in kixusParser.GetDetails())
{
Console.WriteLine("Player at level {0} was killed on {1} by {2}", details.PlayerLevel, details.DeathDate, string.Join(",", details.KilledBy));
}
您还可以使用Espresso工具来计算正确的正则表达式。
要正确地转义所有不是正则表达式组成部分的特殊字符,可以使用Regex。转义方法:
string escapedText = Regex.Escape("<td width="25%" valign="top" >");
试试这个:
http://jsbin.com/atupok/edit#javascript,html
并从那里继续。。。。我在这里做得最多:)
编辑
http://jsbin.com/atupok/3/edit
并开始使用这个工具
http://regexr.com?2vrmf
不是你的那个。