C# Selenium:Stale元素引用(调试时工作正常)

C# Selenium:Stale元素引用(调试时工作正常),c#,selenium,selenium-webdriver,web-scraping,selenium-chromedriver,C#,Selenium,Selenium Webdriver,Web Scraping,Selenium Chromedriver,我正在尝试用C#中的Selenium刮取一个页面,它有几个页面,我可以通过单击页面上的“下一步”按钮来浏览。我通常会得到这样一个错误,即存在一个陈旧的元素引用,只有在没有断点的情况下运行它时才会发生这种情况。如果我一步一步地完成这个程序,它工作得非常好。我假设Selenium在不等待的情况下跳过了重要的内容(即使我实现了等待方法) 对于代码而言,这是问题的主要逻辑: foundVacancies.AddRange(FindVacanciesOnPage()); const string next

我正在尝试用C#中的Selenium刮取一个页面,它有几个页面,我可以通过单击页面上的“下一步”按钮来浏览。我通常会得到这样一个错误,即存在一个陈旧的元素引用,只有在没有断点的情况下运行它时才会发生这种情况。如果我一步一步地完成这个程序,它工作得非常好。我假设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());
    }
}
此方法首先获取第一页上的所有项目,并将它们添加到
foundexpansions
列表中。之后,它将尝试查找“下一步”按钮,如果没有足够的项目,该按钮并不总是存在。如果是,它将尝试单击它,刮掉该页面,然后再次单击它,直到没有剩余页面。这在调试时非常有效,但正常运行时会出现一些严重问题

获取页面上所有项目的方法,以及错误发生的位置:

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;
}
我正在比较新的和旧的URL,以确定这是否是最后一页。
WaitUntilLoaded
方法如下所示:

var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30));
wait.Until(x => ((IJavaScriptExecutor) Driver).ExecuteScript("return document.readyState").Equals("complete"));
奇怪的是,有时web驱动程序在加载第一个页面后立即关闭,没有任何错误或结果。我花了很多时间调试和搜索,但似乎找不到任何信息,因为代码在中断时工作得非常好

我只尝试过Chrome,有无头模式,但我不认为这可能是Chrome的问题

“下一步”按钮具有以下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>


我无法找到单击时的
数据是什么。我试图执行JavaScript
nextPage(),但那没用。

我没有任何c#方面的经验,所以如果我错了,请不要介意。 您正在使用
findelelements
并将其存储到
var-listofexpansions
。我提到了一些网站。为什么不使用
ReadOnlyCollection
。最好将所有元素存储为一个列表,并对其进行迭代。 所以代码变成了

ReadOnlyCollection<IWebElement> listOfVacancies = tableContainingAllVacancies.FindElements(By.XPath(".//li/article/div[1]/a"));
ReadOnlyCollection listofSpaces=table包含所有空缺.FindElements(By.XPath(“.//li/article/div[1]/a”);

如果要进入空缺列表的元素是通过ajax调用填充的,则document.readystate将无法捕捉到这一点。尝试使用:

wait.Until(x => ((IJavaScriptExecutor) Driver).ExecuteScript("return jQuery.active").Equals("0"));

我终于找到了解决这个问题的办法。它很脏,但能用。我尝试了许多不同的方法来等待页面完全加载,但都没有成功。所以我走上了
线程睡眠的黑暗之路,但它并不像听起来那么糟糕:

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;
}
private IEnumerable FindVacanciesOnPage()
{
返回FindVacanciesOnPage(新列表(),0,50,15000);
}
私有IEnumerable FindVacanciesOnPage(ICollection查找空缺、长等待时间、整数间隔、长最大等待时间)
{
尝试
{
var list=Driver.FindElements(By.XPath(“/*[@data ng bind=\”item.JobHeadline\“]);
foreach(列表中的var空缺)
{
查找空缺。添加(空缺。文本);
}
}
捕获(例外)
{
如果(waitedTime>=maxWaitTime)抛出;
睡眠(间隔);
waitedTime+=间隔;
返回FindVanciesOnPage(查找空缺、等待时间、间隔、最大等待时间);
}
填补空缺;
}

这将尝试获取项目,如果抛出异常,只需等待一定时间,直到再次尝试。当等待指定的最长时间时,最终抛出异常。

我感觉当您试图查找下一个(,next…
//h2
)的
文本时,网页会动态更新。或者,当您开始查找第一个文本时,页面未完全加载。我建议给予一定的等待时间,使用
FindElement
而不是多个
FindElement
s,并将找到的每个元素添加到“空缺”列表中,我认为Selenium能够等待特定类的加载,这有助于您的测试更加健壮。我想你是对的,我编辑了我的问题并添加了按钮/链接HTML。我找不出“数据点击”到底是什么。我将尝试实现一些东西,等待一两秒钟,看看它能做什么。@sjmarsh我在问题中尝试使用JavaScript方法等待,但我也将尝试等待我需要的元素,看看它是否有效!将让您知道。您是否必须更改页面的状态以针对过时的元素?如果您必须翻页或单击任何链接/按钮,那么以前未显示的元素将变得过时,因为它们的css将发生更改。实际上,这些元素会返回一个IWebElement的列表。在C#中使用“var”只会提高易读性,前提是清楚变量的类型。谢谢你的回答@JohannesMols..请查看下面的链接。可能有一些参考资料将帮助您解决此问题。谢谢你的回答。不幸的是,这个事件没有被触发,WebDriverWait将超时,所以我猜这个网站没有使用Ajax。
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;
}