当前位置:   article > 正文

selenium爬虫翻页、刷新+循环的深坑_selenium进入第二页之后

selenium进入第二页之后

最近在用selenium开发爬虫,爬取网站就是51job,在爬虫时,总会在翻页后就遇到这类错误:

StaleElementReferenceException: Message: stale element reference: element is not attached to the page document

总之,就是这个StaleElementReferenceException,差点给我搞崩溃了。因为这个错误其实并不难理解,Stale就是“过期的、失效的、坏掉的”意思,而这个错误也说的很明确,当前你使用的这个selenium element,并未attach到网页上,是个错误的element

下面,我就讲讲我的分析,说说这个错误为什么会出现、可能会在什么情况下出现、以及该怎么解决,或者更准确地说,应该是怎么规避它

1. 发现问题

我的爬虫代码已经过于复杂,在这里用伪代码+注释的方式简介一下:

具体情况就是,我在逐页爬取各个职位链接(为了直观,把链接甩这儿,其实没啥特别意义)。由于职位链接有多页,所以我每爬完一页后要翻页。翻页后,再次对每个职位链接click时,出现上面所述的StaleElementReferenceException错误

  1. while True:
  2. job_elements_list = driver.find_elements(...) # 找到当前页面上所有的岗位链接
  3. for job_ele in job_elements_list : # 挨个链接点进去、获取页面信息
  4. job_ele.click() # when you in 2nd **while** loop -> Boom!!!!!!!!!!!!
  5. do_somt_analyze()
  6. # 当前页都爬完,翻页
  7. fanye_button = driver.find_element(By.CSS_SELECTOR, fanye_button_css) # 找到翻页按钮
  8. fanye_button.click() # 点它!
  9. if is_last_page: # 当走到最后一页,没法再翻页的时候,结束这罪恶的循环
  10. break

2. 定位与分析问题

经过我几次三番的尝试后,我基本可以把问题定位在“翻页”上。当然了,页面刷新、后退、前进等操作,只要是你在当前页面重新加载了,就都可以出现这个问题。

既然StaleElementReferenceException这个问题出自element的过期和失效,自然容易想到,我可能在第二页的页面上,仍然在使用第一页相同位置的元素进行click。这的确可能会导致问题,这也是不少其他博客提到的错误。但经排查,我这个问题还没这个简单。因为按理说如果真的是在爬取第二页时还在用上一页的element,那意味着我再打开第二页时,element没有重新获取,那就再获取一遍就完了呗?事实证明,我也并没有犯这个错误,因为如上代码,我每翻一页,元素都会再次获取

看到我上面标红的字了吗?“再次获取”,是否意味着获取到的元素,就是新的(第二页的)?

问题就是出在这里!!!!!

当我还在纠结,为何我单步调试似乎就没问题、一跑起来就出这个bug 的时候,我突然意识到,由于翻页操作并没有打开新的标签页,而代码的运行速度要比网页加载速度快上好多,这就导致了,我做了翻页、正在打开第二页后,while-loop内我再次获取新的职位链接element时,可能获取的还是第一页的网页中的对应element。所以我哪怕每次都重新获取页面element,但你没能保证你这个页面是新页面,所以自然也无法保证你这个element是前一页的stale element,还是下一页的新element。而我单步调试时,手速较慢,翻页后给了网页足够的加载时间,此时第二页加载好了,所以我获取的就都是第二页上的新element,自然也就没了这个bug

3. 解决问题

问题已经掰扯清楚了,那么说说咋解决。

其实这个问题,说白了就是“未等网页加载完毕就获取元素”的问题,强制sleep一会儿倒是也能解决,但显然既低效又可能出现少量的没等够时间的问题。所以有两个思路:

思路1:等待网页加载完成(按理说可行,但我没成功过...)

我们需确认翻页后的当前页已经完成全部加载,那么每次翻页后都先等待网页加载完毕就好了嘛。这方面,网上有不少例子,包括显式等待(driver.implicitly_wait(5))、隐式等待(等待网页最外层元素(我爬取的网站是html)加载完毕,代码很简单不再赘述),但我尝试都会出现偶尔不成功的现象,实属怪异。希望有成功的铁子们给我段样例代码,看看到底是我错了,还是别的什么原因

思路2:重新开个新标签页(打不过,我跑还不行吗?o(╥﹏╥)o)

emmm,这是被逼无奈下的选择——等待页面加载完,经常会等不到或等错,那直接新开的标签页吧,毕竟新标签页不会存在上一页的元素,也就不可能定位到和第二页位置一样、却是stale的元素了。最终这种方法尝试可行,现将我写得一小段代码留在下面:

  1. def my_click_and_jump(element, switch_new_tab=False):
  2. '''
  3. 单击,并跳转到新标签页
  4. :param element: 被点击的element
  5. :param switch_new_tab: 是否要在新标签页打开链接。默认不跳转
  6. :return: 新选项卡window对象
  7. '''
  8. time.sleep(1.5)
  9. element.click()
  10. if switch_new_tab:
  11. driver.execute_script(f'window.open("{driver.current_url}", "_blank");') # 注意js语句要带分号
  12. driver.close() # 关闭当前标签页(即第一页)
  13. # jump to new tab(即第二页)
  14. driver.switch_to.window(driver.window_handles[-1])
  15. return driver.current_window_handle
'
运行

总之,这个问题就到此为止了。其实,就算每个页面强行sleep它个10s,也能暴力解决问题。但我就是想把这个离谱的问题搞清楚。如此刨根问底,不知又有多少意义呢?

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/991470
推荐阅读
相关标签
  

闽ICP备14008679号