使用Selenium(Python)迭代表格行

18

我有一个网页,其中一个表格只有在我点击“检查元素”后才会出现,并且在“查看源代码”页面中不可见。该表格仅包含两行,每行有几个单元格,类似于这样:

<table class="datadisplaytable">
<tbody>
<tr>
<td class="dddefault">16759</td>
<td class="dddefault">MATH</td>
<td class="dddefault">123</td>
<td class="dddefault">001</td>
<td class="dddefault">Calculus</td>
<td class="dddefault"></td>
<td class="dddead"></td>
<td class="dddead"></td>
</tr>
<tr>
<td class="dddefault">16449</td>
<td class="dddefault">PHY</td>
<td class="dddefault">456</td>
<td class="dddefault">002</td>
<td class="dddefault">Physics</td>
<td class="dddefault"></td>
<td class="dddead"></td>
<td class="dddead"></td>
</tr>
</tbody>
</table>

我想做的是遍历行并返回每个单元格中包含的文本。 我似乎无法使用Selenium完成它。元素没有ID,我不知道如何获取它们。我不太熟悉使用xpath之类的东西。

这里是一个调试尝试,它返回一个TypeError

def check_grades(self):
    table = []
    for i in self.driver.find_element_by_class_name("dddefault"):
        table.append(i)
    print(table)

如何轻松地从各行获取文本?


1
你能分享链接吗? - Padraic Cunningham
4个回答

26

XPath很脆弱,最好使用CSS选择器或类:

mytable = find_element_by_css_selector('table.datadisplaytable')
for row in mytable.find_elements_by_css_selector('tr'):
    for cell in row.find_elements_by_tag_name('td'):
        print(cell.text)

1
取决于您如何使用它,例如: "//span[contains(text(),'Some Text of Interest')]" 是强大的,并传达了正在交互的元素。 - Mindaugas Bernatavičius

19
如果您想使用xpath逐行进行操作,可以使用以下代码:
h  = """<table class="datadisplaytable">
<tr>
<td class="dddefault">16759</td>
<td class="dddefault">MATH</td>
<td class="dddefault">123</td>
<td class="dddefault">001</td>
<td class="dddefault">Calculus</td>
<td class="dddefault"></td>
<td class="dddead"></td>
<td class="dddead"></td>
</tr>
<tr>
<td class="dddefault">16449</td>
<td class="dddefault">PHY</td>
<td class="dddefault">456</td>
<td class="dddefault">002</td>
<td class="dddefault">Physics</td>
<td class="dddefault"></td>
<td class="dddead"></td>
<td class="dddead"></td>
</tr>
</table>"""

from lxml import html
xml = html.fromstring(h)
# gets the table
table =  xml.xpath("//table[@class='datadisplaytable']")[0]


# iterate over all the rows   
for row in table.xpath(".//tr"):
     # get the text from all the td's from each row
    print([td.text for td in row.xpath(".//td[@class='dddefault'][text()])

输出结果为:

['16759', 'MATH', '123', '001', 'Calculus']
['16449', 'PHY', '456', '002', 'Physics']

使用td[text()]可以避免返回空文本的元素。

因此,如果要在Selenium中执行同样的操作,则需要执行以下操作:

table =  driver.find_element_by_xpath("//table[@class='datadisplaytable']")

for row in table.find_elements_by_xpath(".//tr"):
    print([td.text for td in row.find_elements_by_xpath(".//td[@class='dddefault'][1]"])

对于多个表格:

def get_row_data(table):
   for row in table.find_elements_by_xpath(".//tr"):
        yield [td.text for td in row.find_elements_by_xpath(".//td[@class='dddefault'][text()]"])


for table in driver.find_elements_by_xpath("//table[@class='datadisplaytable']"):
    for data in get_row_data(table):
        # use the data

谢谢。但是,如果有多个具有相同类名的表格,即“datadisplaytable”,该怎么办?当我正在寻找下一个表格时(即下面的表格),我的函数似乎总是选择第一个具有该名称的表格。 - Fiery Phoenix
然后使用 tables = driver.find_elements_by_xpath("//table[@class='datadisplaytable']"),并且只需按表格应用逻辑,即 for table in tables... 如果数据是通过 ajax 填充的,则您可能能够模拟该 post 请求,请分享链接? - Padraic Cunningham
很不幸,该链接需要登录才能访问,而且并非公开可用。不过,我正在测试这个逻辑,并在有更新时会回报。再次感谢您。 - Fiery Phoenix
不用担心,一旦 HTML 完全渲染后再解析它,逻辑应该能正常工作。 - Padraic Cunningham
可以运行了!非常感谢! - Fiery Phoenix

4

更正 @Padraic Cunningham 答案中的 Selenium 部分:

table = driver.find_element_by_xpath("//table[@class='datadisplaytable']")

for row in table.find_elements_by_xpath(".//tr"):
    print([td.text for td in row.find_elements_by_xpath(".//td[@class='dddefault']")])

注意:末尾缺少一个圆括号;同时移除了 [1] 索引,以匹配第一个 XML 示例。
另外需要注意的是:即使具有索引 [1] 的示例也应保留,以展示如何提取单个元素。

1

另一个版本(由Padraic Cunningham修改和纠正的帖子): 已在Python 3.x上测试

#!/usr/bin/python

h  = """<table class="datadisplaytable">
<tr>
<td class="dddefault">16759</td>
<td class="dddefault">MATH</td>
<td class="dddefault">123</td>
<td class="dddefault">001</td>
<td class="dddefault">Calculus</td>
<td class="dddefault"></td>
<td class="dddead"></td>
<td class="dddead"></td>
</tr>
<tr>
<td class="dddefault">16449</td>
<td class="dddefault">PHY</td>
<td class="dddefault">456</td>
<td class="dddefault">002</td>
<td class="dddefault">Physics</td>
<td class="dddefault"></td>
<td class="dddead"></td>
<td class="dddead"></td>
</tr>
</table>"""

from lxml import html
xml = html.fromstring(h)
# gets the table
table =  xml.xpath("//table[@class='datadisplaytable']")[0]


# iterate over all the rows   
for row in table.xpath(".//tr"):
     # get the text from all the td's from each row
    print([td.text for td in row.xpath(".//td[@class='dddefault']")])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接