遍历 HtmlNodes 并收集数据每次都会给我相同的结果
Looping through HtmlNodes and collecting data gives me the same result every time
我有一个 async
方法调用映射器将 HTML 字符串转换为 IEnumerable
:
public async Task<IEnumerable<MovieRatingScrape>> GetMovieRatingsAsync(string username, int page)
{
var response = await _httpClient.GetAsync($"/betyg/{username}?p={page}");
response.EnsureSuccessStatusCode();
var html = await response.Content.ReadAsStringAsync();
return new MovieRatingsHtmlMapper().Map(html);
}
...
public class MovieRatingsHtmlMapper : HtmlMapperBase<IEnumerable<MovieRatingScrape>>
{
// In reality, this method belongs to base class with signature T Map(string html)
public IEnumerable<MovieRatingScrape> Map(string html)
{
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
return Map(htmlDocument);
}
public override IEnumerable<MovieRatingScrape> Map(HtmlDocument item)
{
var movieRatings = new List<MovieRatingScrape>();
var nodes = item.DocumentNode.SelectNodes("//table[@class='list']/tr");
foreach (var node in nodes)
{
var title = node.SelectSingleNode("//td[1]/a")?.InnerText;
movieRatings.Add(new MovieRatingScrape
{
Date = DateTime.Parse(node.SelectSingleNode("//td[2]")?.InnerText),
Slug = node.SelectSingleNode("//td[1]/a[starts-with(@href, '/film/')]")?
.GetAttributeValue("href", null)?
.Replace("/film/", string.Empty),
SwedishTitle = title,
Rating = node.SelectNodes($"//td[3]/i[{XPathHasClass("fa-star")}]").Count
});
}
return movieRatings;
}
}
结果列表 movieRatings
包含相同 object 的副本,但是当我查看 HTML 以及调试和查看 HtmlNode node
时,它们的不同之处在于他们应该。
要么我对一些非常明显的事情视而不见,要么我遇到了一些我不理解的 async
问题。有任何想法吗?我应该从这个调用中得到 50 个唯一的 objects,现在我只得到前 50 次。
提前谢谢你,维克多。
编辑:添加一些截图来展示我的困境。查看本地 InnerHtml
(node
) 和 foreach
循环的第 1 项和第 2 项的标题。
编辑 2:设法在 .NET 上重现 Fiddle:https://dotnetfiddle.net/A2I4CQ
我不太确定如何描述这个,但你的问题就在这里(我认为)
//table[@class='list']/tr"
特别是 //
我在寻找跨度时遇到了同样的事情。我不得不使用类似的东西
var nodes = htmlDoc.DocumentNode.SelectNodes("//li[@class='itemRow productItemWrapper']");
foreach(HtmlNode node in nodes)
{
var nodeDoc = new HtmlDocument();
nodeDoc.LoadHtml(node.InnerHtml);
string name = nodeDoc.DocumentNode.SelectSingleNode("//span[@class='productDetailTitle']").InnerText;
}
您需要使用 .//
而不是 //
这里是固定的Fiddle:https://dotnetfiddle.net/dZkSRN
//
将搜索文档中的任意位置
.//
会搜索当前节点的任意位置
我有一个 async
方法调用映射器将 HTML 字符串转换为 IEnumerable
:
public async Task<IEnumerable<MovieRatingScrape>> GetMovieRatingsAsync(string username, int page)
{
var response = await _httpClient.GetAsync($"/betyg/{username}?p={page}");
response.EnsureSuccessStatusCode();
var html = await response.Content.ReadAsStringAsync();
return new MovieRatingsHtmlMapper().Map(html);
}
...
public class MovieRatingsHtmlMapper : HtmlMapperBase<IEnumerable<MovieRatingScrape>>
{
// In reality, this method belongs to base class with signature T Map(string html)
public IEnumerable<MovieRatingScrape> Map(string html)
{
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
return Map(htmlDocument);
}
public override IEnumerable<MovieRatingScrape> Map(HtmlDocument item)
{
var movieRatings = new List<MovieRatingScrape>();
var nodes = item.DocumentNode.SelectNodes("//table[@class='list']/tr");
foreach (var node in nodes)
{
var title = node.SelectSingleNode("//td[1]/a")?.InnerText;
movieRatings.Add(new MovieRatingScrape
{
Date = DateTime.Parse(node.SelectSingleNode("//td[2]")?.InnerText),
Slug = node.SelectSingleNode("//td[1]/a[starts-with(@href, '/film/')]")?
.GetAttributeValue("href", null)?
.Replace("/film/", string.Empty),
SwedishTitle = title,
Rating = node.SelectNodes($"//td[3]/i[{XPathHasClass("fa-star")}]").Count
});
}
return movieRatings;
}
}
结果列表 movieRatings
包含相同 object 的副本,但是当我查看 HTML 以及调试和查看 HtmlNode node
时,它们的不同之处在于他们应该。
要么我对一些非常明显的事情视而不见,要么我遇到了一些我不理解的 async
问题。有任何想法吗?我应该从这个调用中得到 50 个唯一的 objects,现在我只得到前 50 次。
提前谢谢你,维克多。
编辑:添加一些截图来展示我的困境。查看本地 InnerHtml
(node
) 和 foreach
循环的第 1 项和第 2 项的标题。
编辑 2:设法在 .NET 上重现 Fiddle:https://dotnetfiddle.net/A2I4CQ
我不太确定如何描述这个,但你的问题就在这里(我认为)
//table[@class='list']/tr"
特别是 //
我在寻找跨度时遇到了同样的事情。我不得不使用类似的东西
var nodes = htmlDoc.DocumentNode.SelectNodes("//li[@class='itemRow productItemWrapper']");
foreach(HtmlNode node in nodes)
{
var nodeDoc = new HtmlDocument();
nodeDoc.LoadHtml(node.InnerHtml);
string name = nodeDoc.DocumentNode.SelectSingleNode("//span[@class='productDetailTitle']").InnerText;
}
您需要使用 .//
而不是 //
这里是固定的Fiddle:https://dotnetfiddle.net/dZkSRN
//
将搜索文档中的任意位置
.//
会搜索当前节点的任意位置