Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

有一些同学在写爬虫的时候,过于依赖 Selenium,觉得只要使用模拟浏览器,在不被网站屏蔽的情况下,就可以爬到任何内容。

今天我们不讨论字体反爬虫和 CSS 反爬虫这两种情况。我们来看一段非常简单的网页。这个网页只有一个HTML 文件,不加载特殊字体,不加载 CSS 文件。

 

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

这个网页的奇怪之处在哪里呢?我们试一试使用 XPath Helper 来提取网页上面的红色文字,发现XPath 竟然无法找到这段文字,如下图所示:

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

然后我们使用 Selenium 来试一试:

 

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

Selenium果然无法获取 红字到内容。我们再打印一下网页的源代码:

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

这一次,Selenium 获取到的源代码,竟然跟 Chrome 开发者工具里面显示的源代码不一样?

这个问题的关键,就在开发者工具里面的这样一段文字:

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

因为这个节点是一个shadow DOM[1]。shadow DOM 的行为跟 iframe很像,都是把一段HTML 信息嵌入到另一个 HTML 中。但不同的是,iframe被嵌入的地址需要额外再搭建一个 HTTP服务,而 shadow DOM 可以只嵌入一段 HTML 代码,所以它比 iframe 更节省资源。

 

在上面的截图中,通过下面这三行代码,我们把一个新的

标签嵌入到了原来的 HTML 中:

var content = document.querySelector('.content'); 
var root = content.attachShadow({mode: 'open'}); 
root.innerHTML = '<p class="real_content" style="color: red">你抓不到这段文字的!</p>'

 

而这个被嵌入的影子标签,就像 iframe 一样,是无法直接使用 Selenium 提取的。如果强行提取,那么,我们需要使用 JavaScript 获取 shadow DOM,然后再进行提取。我们来看一段可以正常工作的代码:

shadow = driver.execute_script('return document.querySelector(".content").shadowRoot') 
content = shadow.find_element_by_class_name('real_content') 
print(content.text)

 

运行效果如下图所示:

Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

这段代码,首先通过 JavaScript 找到shadow-root的父节点元素,然后返回这个元素的.shadowRoot属性。在 Python 里面拿到这个属性以后,使用.find_element_by_class_name()方法获取里面的内容。

要特别注意的是,拿到shadow-root节点以后,只能通过 CSS 选择器进一步筛选里面的内容,不能用 XPath,否则会导致报错。

 

 

[selenium元素定位]Python使用selenium来定位shado root的元素,shadoroot

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<article>
    <h2 class="title">抓取元素</h2>
    <div class="content">
        <p class>文章正文</p>
    </div>

</article>
<script>
    var content = document.querySelector('.content');
    var root = content.attachShadow({mode: 'open'});
    root.innerHTML = '<p class="real_content" style="color: red"> 抓不到的文字!</p>'
</script>
</body>
</html>

 

问题:想获取文字:抓不到的文字!
# 思路一:
# 一般采取selenium的xpath来获取元素
from selenium import webdriver

web = webdriver.Chrome('../chromedriver.exe')
web.get('http://localhost:63342/test01/h1.html')

content = web.find_element_by_xpath('//p[@class="real_content"]')
print(content)
print(content.text)
web.close()
web.quit()


# 结果没有获取到元素对应的值
控制台打印信息为:
<selenium.webdriver.remote.webelement.WebElement (session="510e3aeb9de727a880169c1450f94756", element="700174b1-8ccd-4608-8804-8b67d4af8bb2")>


# 思路二:使用lxml
from lxml import etree

f = open('h1.html', 'r', encoding='utf-8')
html = f.read()
tree = etree.HTML(html)

content = tree.xpath('//p[@class="real_content"]/text()')
print(content)

# 结果也是没有获取到对应的内容
控制台打印信息:
[]

 

到页面来查看元素
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
  • 并没有找到该元素对应的值,因为这个节点是一个shadow DOM。shadow DOM的行为跟iframe很像,都是把一段html信息嵌入到另一个HTML中,但是不同的是。 iframe 被嵌入的地址需要额外再搭建一个HTTP服务,而shadow DOM可以只嵌入一段HTML代码,所以它比 iframe 更节省资源。
  • 所以要想获取到shadow DOM的元素,首先先拿到 shadow-root 节点,然后返回这个元素的 .shadowRoot 属性。拿到属性后就可以直接使用 .find_element_by_class_name() 方法获取对应的内容。
  • 注意:这个拿到 shadow-root 节点之后,只能通过css选择器来进一步筛选内容,不能使用xpath(会报错:NoSuchElementException)

 

结果方案
# 使用js来获取shado-root节点
from selenium import webdriver

web = webdriver.Chrome('C:\Program Files\Python39\chromedriver.exe')
web.get('http://localhost:63342/test01/h1.html?_ijt=c29nnauam27sd8i6kufkbpq6tv')
shadow = web.execute_script('return document.querySelector(".content").shadowRoot')
content = shadow.find_element_by_class_name('real_content')
print(content)
print(content.text)
web.close()
web.quit()

# 结果就可以获取到shadow-root的内容

抓不到的文字!
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom
Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

 

 

 

本文:Python 处理 Shadow, Python使用selenium来定位shado root的元素, selenium元素定位,shadoroot, 使用Selenium访问shadow dom

Add a Comment

Your email address will not be published.

Time limit is exhausted. Please reload CAPTCHA.