【问题标题】:Selenium find element by class name two parametersSelenium 通过类名两个参数查找元素
【发布时间】:2019-11-08 03:03:24
【问题描述】:

如何在不重复输出的情况下按类名查找元素?我有两个班级要刮hdrlnkresults-price。我写的代码是这样的:

x = driver.find_elements_by_class_name(['hdrlnk','result-price'])

它给了我一些错误。我尝试了另一个代码,这里是:

x = driver.find_elements_by_class_name('hdrlnk'),
y = driver.find_elements_by_class_name('result-price')
for xs in x:
    for ys in y:
        print(xs.text + ys.text)   

但是我得到了这样的结果

sony 5 disc cd changer$40
sony 5 disc cd changer$70
sony 5 disc cd changer$70
sony 5 disc cd changer$190
sony 5 disc cd changer$190
sony 5 disc cd changer$190
sony 5 disc cd changer$190
sony 5 disc cd changer$10

我试图抓取的 HTML 结构部分

<p class="result-info">
    <span class="icon icon-star" role="button" title="save this post in your favorites list">
        <span class="screen-reader-text">favorite this post</span>
    </span>
    <time class="result-date" datetime="2019-11-07 18:20" title="Thu 07 Nov 06:20:56 PM">Nov  7</time>
    <a href="https://vancouver.craigslist.org/rch/ele/d/chandeliers/7015824686.html" data-id="7015824686" class="result-title hdrlnk">CHANDELIERS</a>
    <span class="result-meta">
        <span class="result-price">$800</span>
        <span class="result-hood"> (Richmond)</span>
        <span class="result-tags">
            <span class="pictag">pic</span>
        </span>
        <span class="banish icon icon-trash" role="button">
            <span class="screen-reader-text">hide this posting</span>
        </span>
        <span class="unbanish icon icon-trash red" role="button" aria-hidden="true"></span>
        <a href="#" class="restore-link">
            <span class="restore-narrow-text">restore</span>
            <span class="restore-wide-text">restore this posting</span>
        </a>
    </span>
</p>

第一个元素是重复的,但我得到了第二个元素的正确值。如何更正此错误?

【问题讨论】:

  • 你能添加你试图抓取的 HTML 代码吗?
  • 好的,请稍等
  • HTML 中是否有许多 &lt;p class="result-info"&gt; 元素?它们每个都包含一个hdrlnk 和一个result-price?
  • 是的,先生。你是对的@Code-Apprentice
  • @Vince 在下面我的回答中查看两个选项。

标签: python python-3.x selenium-webdriver web-scraping


【解决方案1】:

.find_elements_by_class_name() 只需要一个类名。我建议使用 CSS 选择器来完成这项工作,例如.hdrlnk .result-price。代码看起来像

prices = driver.find_elements_by_css_selector('.hdrlnk .result-price')

这会打印所有价格。如果您还需要标签,则必须编写更多代码。

for heading in driver.find_elements_by_css_selector('.hdrlnk'):
    print(heading.text)
    for price in heading.find_elements_by_xpath('./following::span[@class="result-price"]'):
        print('  ' + price.text)

有关查找元素的所有选项,请参阅 the docs

CSS 选择器参考:
W3C reference
Selenium Tips: CSS Selectors
Taming Advanced CSS Selectors

【讨论】:

  • 我支持这种方法。
  • x 这里的迭代有点挑战性,因为您需要一次获取两个元素。我并不是说它不能完成,但它需要一些来自 itertools 配方的技巧。
  • @Code-Apprentice 是的,我发布了答案,然后返回并重新阅读了问题,并从 OP 打印的内容中意识到,他可能想要的东西可能不同于我根据他的原始定位器想到的东西。我已经更新了我的答案,也包括了第二种方法。
【解决方案2】:

我认为您不需要nested loop,尝试按对象长度进行迭代,使用len 方法:

x = driver.find_elements_by_class_name('hdrlnk'),
#y = driver.find_elements_by_class_name('result-price')
y = driver.find_elements_by_xpath('//p[@class="result-info"]/span[@class="result-meta"]//span[@class="result-price"]')

print(len(x))
print(len(y))

for i in range(len(x)) :
    print(x[i].text + y[i].text)

更新

实际上我只是想像你想将成员x 与成员y 结合起来,它看起来像这样:

x[0] with y[0]
x[1] with y[1]
etc....

所以我确定您在 xy 之间有相同的号码。由于这个原因,我认为,我只需要x 来代表loop(虽然,你也可以使用y 代替)。

如果要将它们都包含在loop 中,可以使用zip。请从这个帖子中的其他答案中学习。

对于xpath,您可以在此处查看:Locator Strategies

通过从检查元素复制xpath,它将为您提供绝对路径。我不推荐它,因为它很容易改变。

请看这个帖子:Absolute vs Relative Xpath

【讨论】:

  • 它对我有用,先生。非常感谢。您能否更深入地向我解释为什么您只在 len 循环中使用 x 。为什么下面的答案对我不起作用?
  • 哦,先生,它有一些缺点。它有点工作,但价格在更新之前重复了两次。就像这样ipad (6th gen) 32gb wifi+cellular BNIB , $425 drag 2 platinum with smok tfv12 tank , $425 Brand New Lightning Cable , $100drag 2 铂金价格正在获得 ipad 的价格,即 425 美元而不是 100 美元。
  • @Vince 使用上面的代码,我假设你有相同数量的xy。因此,如果您面临重复两次price,您的y 可能比x 多。我已更新代码,请再试一次,结果如何,我已将定位器更改为 xpath 并使其首先打印两个列表的长度。
  • 现在这对我有用。但是我有一个问题,你为什么用xpath而不是classname?在你的循环语句中为什么你只使用x 变量而不是y 我有点困惑
  • 你是怎么想出那个 xpath 先生的?我尝试从检查元素复制 xpath,它给我一个像这样的 xpath //*[@id="sortable-results"]/ul/li[1]/p/span[2]/span[1]
【解决方案3】:

看起来您的元素的类 hdrlnkresult-price 成对出现。所以你需要与zip()并行迭代列表:

xs = driver.find_elements_by_class_name('hdrlnk'),
ys = driver.find_elements_by_class_name('result-price')
for x, y in zip(xs, ys):
    print(x.text, y.text)

这假定两个列表包含相同数量的元素,并且顺序正确,因此它们与zip() 正确匹配。通过遍历父 &lt;p&gt; 元素直接从 HTML 解析它们可能更安全:

ps = driver.find_elements_by_class_name('result-info')
for p in ps:
    x = p.find_element_by_class_name('hdrlnk'),
    y = p.find_element_by_class_name('result-price')
    print(x.text, y.text)

【讨论】:

  • 我尝试了第一个并得到了'list' object has no attribute 'text' 的错误,错误在print(x.text, y.text) 中。我尝试通过print(xs.text, ys.text)修改它并得到"message": "Instance of 'tuple' has no 'text' member"的错误
  • 第二个也出现了'tuple' object has no attribute 'text' 的错误,在我的终端中我得到了这样的输出[15768:7180:1108/132750.647:ERROR:page_load_metrics_update_dispatcher.cc(166)] Invalid first_paint 2.392 s for first_image_paint 2.388 s。先生,我该怎么办?
  • @Vince 你似乎在做一些与我在这里做的事情略有不同的事情。我看不出我给出的代码是如何给出这些错误的。
  • @Vince 如果您需要更多帮助,请使用您当前的代码及其错误发布一个新问题。
  • @Vince 我在第二个示例中发现了一个错误。我认为更改不会影响您看到的错误,但它确实修复了逻辑错误。
【解决方案4】:

如果您的用例是使用find_elements_by _classname(),更好的方法是为visibility_of_all_elements_located() 引入WebDriverWait,您可以使用以下Locator Strategies 之一:

  • 使用CLASS_NAME

    items = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.CLASS_NAME, "hdrlnk")))
    prices = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.CLASS_NAME, "result-price")))
    for i,j in zip(items, prices):
        print(i.text + j.text)
    

但是,规范的方法是使用以下任一方法:

  • CSS_SELECTOR:

    items = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, "p.result-info a.hdrlnk")))
    prices = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.CSS_SELECTOR, "p.result-info span.result-meta>span.result-price")))
    for i,j in zip(items, prices):
        print(i.text + j.text)
    
  • XPATH:

    items = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.XPATH, "//p[@class='result-info']//a[contains(@class, 'hdrlnk')]")))
    items = WebDriverWait(driver, 20).until(EC.visibility_of_all_elements_located((By.XPATH, "//p[@class='result-info']//span[@class='result-meta']/span[@class='result-price']")))
    for i,j in zip(items, prices):
        print(i.text + j.text)
    
  • 注意:您必须添加以下导入:

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    

【讨论】:

  • 在导入先生时遇到了一些错误。喜欢unable to import selenium.webdriver.support
  • @Vince import 应该与底层代码块无关。重新启动您的 IDE。如果错误仍然存​​在,您可能需要重新安装 selenium
猜你喜欢
  • 2018-05-19
  • 2021-11-19
  • 2014-03-09
  • 2021-03-05
  • 2023-01-03
  • 2016-03-12
  • 2017-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多