我正在尝试使用C#中的Selenium抓取一个页面,其中有几页我可以通过单击" Next"页面上的按钮。我通常会收到错误,即存在陈旧元素引用,只有在没有断点的情况下运行它才会发生。如果我一步一步完成该程序,它的工作完全正常。我假设Selenium在没有等待的情况下跳过重要的东西(即使我已经实施了等待方法)。
对于代码,这是问题的主要逻辑:
foundVacancies.AddRange(FindVacanciesOnPage());
const string nextBtnXPath = "//*[@id=\"ContainerResultList\"]/div/div[3]/nav/ul/li[8]/a";
if (Driver.FindElements(By.XPath(nextBtnXPath)).Count != 0)
{
while (TryClickingNextButton(nextBtnXPath))
{
foundVacancies.AddRange(FindVacanciesOnPage());
}
}
此方法首先获取第一页上的所有项目,然后将它们添加到foundVacancies
列表中。之后,它将尝试寻找" Next"按钮,如果没有足够的项目,它并不总是在那里。如果是,它将尝试单击它,刮擦页面,然后再次单击它,直到没有页面为止。调试时效果很好,但正常运行时出现了一些问题。
获取页面上所有项目的方法以及发生错误的位置:
private IEnumerable<string> FindVacanciesOnPage()
{
var vacancies = new List<string>();
var tableContainingAllVacancies = Driver.FindElement(By.XPath("//*[@id=\"ContainerResultList\"]/div/div[2]/div/ul"));
var listOfVacancies = tableContainingAllVacancies.FindElements(By.XPath(".//li/article/div[1]/a"));
foreach (var vacancy in listOfVacancies)
{
vacancies.Add(vacancy.FindElement(By.XPath(".//h2")).Text);
}
return vacancies;
}
这些项目位于<ul>
HTML标记中并且有<li>
个孩子,我将逐个查看,并获取其内部文本。 foreach
循环中出现陈旧元素错误。我假设网络驱动程序没有时间重新加载DOM,因为它在断点时工作。但是,我确实有一种方法可以等到页面完全加载,这是我在下一页时使用的。
private bool TryClickingNextButton(string nextButtonXPath)
{
var nextButton = Driver.FindElement(By.XPath(nextButtonXPath));
var currentUrl = Driver.Url;
ScrollElementIntoView(nextButton);
nextButton.Click();
WaitUntilLoaded();
var newUrl = Driver.Url;
return !currentUrl.Equals(newUrl);
}
我正在比较新旧网址,以确定这是否是最后一页。 WaitUntilLoaded
方法如下所示:
var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30));
wait.Until(x => ((IJavaScriptExecutor) Driver).ExecuteScript("return document.readyState").Equals("complete"));
奇怪的是,有时网页驱动程序在加载第一页后立即关闭,没有任何错误或任何结果。我花了很多时间在SO上进行调试和搜索,但似乎无法找到任何信息,因为代码在通过它进行破坏时工作得非常好。
我只尝试使用Chrome,有无头模式,但我不知道这可能是Chrome问题。
&#34;下一步&#34;按钮具有以下HTML:
<a href="" data-jn-click="nextPage()" data-ng-class="{'disabled-element':currentPage === totalPages}" tabindex="0">
<span class="hidden-md hidden-sm hidden-xs">Next <span class="icon icon-pagination-single-forward"></span></span>
<span class="hidden-lg icon icon-pagination-forward-enable"></span>
</a>
我无法找出data-jn-click
是什么。我试图执行JavaScript nextPage();
,但是没有做任何事情。
答案 0 :(得分:0)
我对c#没有任何经验,所以如果错了请不要介意。
您正在使用findElements
并将其存储到var listOfVacancies
。我推荐了一些网站。你为什么不用ReadOnlyCollection<IWebElement>
。最好将所有元素存储为List并迭代它。
所以代码就变成了,
ReadOnlyCollection<IWebElement> listOfVacancies = tableContainingAllVacancies.FindElements(By.XPath(".//li/article/div[1]/a"));
答案 1 :(得分:0)
If the elements that are going into listOfVacancies are being populated via an ajax call, then document.readystate won't catch that. Try using:
wait.Until(x => ((IJavaScriptExecutor) Driver).ExecuteScript("return jQuery.active").Equals("0"));
答案 2 :(得分:0)
我终于找到了解决这个问题的方法。它很脏,但它有效。我尝试了许多不同的方法来等待页面完全加载,但没有一个工作。所以我走了Thread.Sleep
的黑暗道路,但它没有听起来那么糟糕:
private IEnumerable<string> FindVacanciesOnPage()
{
return FindVacanciesOnPage(new List<string>(), 0, 50, 15000);
}
private IEnumerable<string> FindVacanciesOnPage(ICollection<string> foundVacancies, long waitedTime, int interval, long maxWaitTime)
{
try
{
var list = Driver.FindElements(By.XPath("//*[@data-ng-bind=\"item.JobHeadline\"]"));
foreach (var vacancy in list)
{
foundVacancies.Add(vacancy.Text);
}
}
catch (Exception)
{
if (waitedTime >= maxWaitTime) throw;
Thread.Sleep(interval);
waitedTime += interval;
return FindVacanciesOnPage(foundVacancies, waitedTime, interval, maxWaitTime);
}
return foundVacancies;
}
这将尝试获取项目,如果抛出异常,则等待一段时间直到再次尝试。当等待指定的最大时间时,最终抛出异常。