我正在使用Selenium
从此site,检索数据,当我尝试单击foreach
中的元素时遇到了一个小问题。
我要做什么
我正在尝试将表格与特定赔率类别相关联,在上面的链接中,我们有不同的类别:
从图像中可以看到,我单击了Asian handicap -1.75
,该站点已通过javascript生成了一个表,因此在我的代码中,我试图获取该表以查找相应的元素并单击它。 / p>
代码
实际上,我有两种方法,第一种称为GetAsianHandicap
,它会遍历所有类别的赔率:
public List<T> GetAsianHandicap(Uri fixtureLink)
{
//Contains all the categories displayed on the page
string[] categories = new string[] { "-1.75", "-1.5", "-1.25", "-1", "-0.75", "-0.5", "-0.25", "0", "+0.25", "+0.5", "+0.75", "+1", "+1.25", "+1.5", "+1.75" };
foreach(string cat in categories)
{
//Get the html of the table for the current category
string html = GetSelector("Asian handicap " + asian);
if(html == string.Empty)
continue;
//other code
}
}
然后是方法GetSelector
,它单击了搜索到的元素,这就是设计:
public string GetSelector(string selector)
{
//Get the available table container (the category).
var containers = driver.FindElements(By.XPath("//div[@class='table-container']"));
//Store the html to return.
string html = string.Empty;
foreach (IWebElement container in containers)
{
//Container not available for click.
if (container.GetAttribute("style") == "display: none;")
continue;
//Get container header (contains the description).
IWebElement header = container.FindElement(By.XPath(".//div[starts-with(@class, 'table-header')]"));
//Store the table description.
string description = header.FindElement(By.TagName("a")).Text;
//The container contains the searched category
if (description.Trim() == selector)
{
//Get the available links.
var listItems = driver.FindElement(By.Id("odds-data-table")).FindElements(By.TagName("a"));
//Get the element to click.
IWebElement element = listItems.Where(li => li.Text == selector).FirstOrDefault();
//The element exist
if (element != null)
{
//Click on the container for load the table.
element.Click();
//Wait few seconds on ChromeDriver for table loading.
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(20);
//Get the new html of the page
html = driver.PageSource;
}
return html;
}
return string.Empty;
}
问题和异常详细信息
foreach
到达以下行:
var listItems = driver.FindElement(By.Id("odds-data-table")).FindElements(By.TagName("a"));
我收到此异常:
WebDriver.dll中的'OpenQA.Selenium.StaleElementReferenceException' 旧元素参考:元素未附加到页面文档
搜索错误意味着html页面源已更改,但是在这种情况下,我将元素存储为要单击的变量,而将html本身存储为另一个变量,因此我无法摆脱这一问题。 / p>
有人可以帮助我吗?
谢谢。
答案 0 :(得分:2)
正如您在相关Post中所提到的,此问题是因为网站执行了自动刷新。
解决方案1:
我建议是否有明确的刷新方法,定期执行刷新,或者(如果确定,何时需要刷新)。
解决方案2:
为FindElement
和FindElements
创建一个扩展方法,以便它尝试获取给定超时的元素。
public static void FindElement(this IWebDriver driver, By by, int timeout)
{
if(timeout >0)
{
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until(ExpectedConditions.ElementToBeClickable(by));
}
return driver.FindElement(by);
}
public static IReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By by, int timeout)
{
if(timeout >0)
{
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeout)).Until(ExpectedConditions.PresenceOfAllElementsLocatedBy(by));
}
return driver.FindElements(by);
}
因此您的代码将使用以下代码:
var listItems = driver.FindElement(By.Id("odds-data-table"), 30).FindElements(By.TagName("a"),30);
解决方案3:
使用扩展方法处理StaleElementException:
public static void FindElement(this IWebDriver driver, By by, int maxAttempt)
{
for(int attempt =0; attempt <maxAttempt; attempt++)
{
try
{
driver.FindElement(by);
break;
}
catch(StaleElementException)
{
}
}
}
public static IReadOnlyCollection<IWebElement> FindElements(this IWebDriver driver, By by, int maxAttempt)
{
for(int attempt =0; attempt <maxAttempt; attempt++)
{
try
{
driver.FindElements(by);
break;
}
catch(StaleElementException)
{
}
}
}
您的代码将使用以下代码:
var listItems = driver.FindElement(By.Id("odds-data-table"), 2).FindElements(By.TagName("a"),2);
答案 1 :(得分:2)
我查看了您的代码,我认为您正在使它变得比所需的更加复杂。我假设您要刮擦单击残障链接之一时显示的表。这是一些简单的代码可以做到这一点。它会转储元素的文本,这些文本最终会以无格式显示,但是您可以以此为起点并根据需要添加功能。运行此代码时,我没有遇到任何StaleElementExceptions,并且我从未看到页面刷新过,所以我不确定其他人在看什么。
string url = "http://www.oddsportal.com/soccer/europe/champions-league/paok-spartak-moscow-pIXFEt8o/#ah;2";
driver.Url = url;
// get all the (visible) handicap links and click them to open the page and display the table with odds
IReadOnlyCollection<IWebElement> links = driver.FindElements(By.XPath("//a[contains(.,'Asian handicap')]")).Where(e => e.Displayed).ToList();
foreach (var link in links)
{
link.Click();
}
// print all the odds tables
foreach (var item in driver.FindElements(By.XPath("//div[@class='table-container']")))
{
Console.WriteLine(item.Text);
Console.WriteLine("====================================");
}
我建议您花一些时间来学习定位器。定位器功能非常强大,可以省去堆叠嵌套循环以查找一件事……然后是该事物的子项……然后是该事物的子项……等等的麻烦。正确的定位器可以在页面的一部分中找到所有内容,从而节省了大量代码和时间。
答案 2 :(得分:-1)
使用此:
string description = header.FindElement(By.XPath("strong/a")).Text;
代替您的
string description = header.FindElement(By.TagName("a")).Text;