Selenium - 如何获取页面中的所有 iframe(包括嵌套的)?
我正在尝试通过selenium webdriver搜索我访问的网页上的所有html内容。在selenium中,当我遇到一个iframe时,我必须先切换到这个iframe,然后再切换回主html,这样才能搜索其他的iframe。
不过,遇到嵌套的iframe时,这就变得相当复杂了。我需要先切换到一个iframe,搜索里面的iframe,然后再切换到找到的一个iframe,继续在里面搜索iframe。要去另一个iframe时,我还得先切换回主框架,然后记住之前的位置,以便能再切换回去,等等。
不幸的是,我发现很多网页都有嵌套的iframe,里面又有iframe,层层叠叠的。
有没有什么简单的办法来处理这个问题?或者有没有更好的方法?
4 个回答
你可以使用下面的代码来获取嵌套的框架层级……根据你的DOM结构调整getAttribute的部分。
static Stack<String> stackOfFrames = new Stack<>();
....
....
public static void getListOfFrames(WebDriver driver) {
List<WebElement> iframes = wd.findElements(By.xpath("//iframe|//frame"));
int numOfFrames = iframes.size();
for(int i=0; i<numOfFrames;i++) {
stackOfFrames.push(iframes.get(i).getAttribute("id"));
System.out.println("Current Stack => " + stackOfFrames);
driver.switchTo().frame(i);
getListOfFrames(driver);
driver.switchTo().parentFrame();
stackOfFrames.pop();
count++;
}
}
你可以把一个iFrame放进另一个iFrame里。只需要记住一段简单的代码来设置位置,然后再把光标移回到屏幕上的同一个区域。下面是完整的代码示例。记住,先放大iFrame,再定义小iFrame的位置,像下面这个完整的例子一样:---
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Daneiella Oddie, Austrailian Ballet Dancer, dancing to Bach-Gounod's Ave Maria</title>
</head>
<body bgcolor="#ffffcc">
<DIV style="position: absolute; top:0px; left:0px; width:0px; height:0px"></div>
<DIV style="position: absolute; top:10px; left:200px; width:900px; height:500px">
<iframe width="824" height="472" src="http://majordomoers.me/Videos/DanielaOddiDancingToBack_GounodsAveMaria.mp4" frameborder="0" allowfullscreen></iframe>
</div>
<DIV style="position: absolute; top:0px; left:0px; width:0px; height:0px"></div>
<DIV style="position: absolute; top:10px; left:0px; width:50px; height:50px">
<iframe src="http://majordomoers.me/Videos/LauraUllrichSingingBach_GounodsAveMaria.mp4" frameborder="0" allowfullscreen></iframe>
</div>
<DIV style="position: absolute; top:0px; left:0px; width:0px; height:0px"></div>
<DIV style="position: absolute; top:470px; left:10px; width:1050px; height:30px">
<br><font face="Comic Sans MS" size="3" color="red">
<li><b>Both Videos will START automatically...but the one with the audio will preceed the dancing by about 17 seconds. You should keep
<li>both videos at the same size as presented here. In all, just lean back and let it all unfold before you, each in its own time.</li></font>
</div>
<br>
</body>
</html>
仅仅通过HTML元素的标签或属性(包括ID)来查找iframe似乎不太可靠。
另一方面,通过iframe的索引进行递归搜索效果相对较好。
def find_all_iframes(driver):
iframes = driver.find_elements_by_xpath("//iframe")
for index, iframe in enumerate(iframes):
# Your sweet business logic applied to iframe goes here.
driver.switch_to.frame(index)
find_all_iframes(driver)
driver.switch_to.parent_frame()
我没能找到一个有很多层嵌套框架的网站来彻底测试这个概念,不过我在一个只有一层嵌套框架的网站上进行了测试。所以,如果你要处理更深层的嵌套,可能需要花点时间调试一下。此外,这段代码假设每个iframe都有一个名字属性。
我认为使用递归函数可以解决这个问题,下面是一个示例数据结构:
def frame_search(path):
framedict = {}
for child_frame in browser.find_elements_by_tag_name('frame'):
child_frame_name = child_frame.get_attribute('name')
framedict[child_frame_name] = {'framepath' : path, 'children' : {}}
xpath = '//frame[@name="{}"]'.format(child_frame_name)
browser.switch_to.frame(browser.find_element_by_xpath(xpath))
framedict[child_frame_name]['children'] = frame_search(framedict[child_frame_name]['framepath']+[child_frame_name])
...
do something involving this child_frame
...
browser.switch_to.default_content()
if len(framedict[child_frame_name]['framepath'])>0:
for parent in framedict[child_frame_name]['framepath']:
parent_xpath = '//frame[@name="{}"]'.format(parent)
browser.switch_to.frame(browser.find_element_by_xpath(parent_xpath))
return framedict
你可以通过调用:frametree = iframe_search([])
来开始,这样framedict
的结果大概会是这样的:
frametree =
{'child1' : 'framepath' : [], 'children' : {'child1.1' : 'framepath' : ['child1'], 'children' : {...etc}},
'child2' : 'framepath' : [], 'children' : {'child2.1' : 'framepath' : ['child2'], 'children' : {...etc}}}
需要注意的是,我之所以选择用框架的属性来识别它们,而不是直接使用find_elements方法的结果,是因为我发现有些情况下,Selenium在页面打开太久后会抛出过时数据的异常,这时候返回的结果就没用了。显然,框架的属性是不会改变的,所以用xpath会更稳定一些。希望这能帮到你。