当前位置: 首页 > news >正文

python selenium(4+)+chromedriver最新版 定位爬取嵌套shadow-root(open)中内容

废话不多说,直接开始

本文以无界作为本文测试案例,抓取shadow-root(open)下的内容

shadow Dom in selenium

首先先讲一下shadow Dom in selenium 版本的区别,链接指向这里
在Selenium 4+版本 以及 chrome ver 96+中,有做出一下修改。摒弃了老版本的driver.find_element_by_css_selector。这点跟版本有关系,请自行查看目前使用的版本。

在这里插入图片描述

使用以及安装版本:

selenium 4.27.1 + chrome(131.0.6778.86)最新版本**
selenium pip install selenium==4.27.1即可(不指定版本也可以,目前默认安装此版本)
chromedriver 最新版本下载指向https://googlechromelabs.github.io/chrome-for-testing/#stable
由于selenium ,chrome+chromedriver 版本一直在迭代,所以也许过两年情况可能不一样了。具体更新见官方。

初入shadow-root:

先看shadow Dom in selenium提供的例子:

import osimport pytest
from selenium.webdriver import Chrome
from selenium.webdriver import Firefox
from selenium.webdriver import Remote
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.common.by import Bydef test_old_code_old_chrome():"""What most people use for Shadow DOM Elements.Still works for Chrome < v96, Edge < v96, Safari"""options = ChromeOptions()options.set_capability('browserVersion', '95.0')sauce_options = {'username': os.environ["SAUCE_USERNAME"],'accessKey': os.environ["SAUCE_ACCESS_KEY"]}options.set_capability('sauce:options', sauce_options)sauce_url = "https://ondemand.us-west-1.saucelabs.com/wd/hub"driver = Remote(command_executor=sauce_url, options=options)driver.get('http://watir.com/examples/shadow_dom.html')shadow_host = driver.find_element_by_css_selector('#shadow_host')shadow_root = driver.execute_script('return arguments[0].shadowRoot', shadow_host)shadow_content = shadow_root.find_element_by_css_selector('#shadow_content')assert shadow_content.text == 'some text'driver.quit()def test_old_code_new_chrome():"""Same code as above, but in Chromium 96+.Selenium 4.0 has same error as Selenium 3.Selenium 4.1 has AttributeError for using the old find_element_* method"""driver = Chrome()driver.get('http://watir.com/examples/shadow_dom.html')shadow_host = driver.find_element_by_css_selector('#shadow_host')shadow_root = driver.execute_script('return arguments[0].shadowRoot', shadow_host)with pytest.raises(AttributeError, match="'ShadowRoot' object has no attribute 'find_element_by_css_selector'"):shadow_root.find_element_by_css_selector('#shadow_content')driver.quit()def test_fix_old_code():"""Same code as above, but using the new By class for find_element()This works in Selenium 4.1."""driver = Chrome()driver.get('http://watir.com/examples/shadow_dom.html')shadow_host = driver.find_element(By.CSS_SELECTOR, '#shadow_host')shadow_root = driver.execute_script('return arguments[0].shadowRoot', shadow_host)shadow_content = shadow_root.find_element(By.CSS_SELECTOR, '#shadow_content')assert shadow_content.text == 'some text'driver.quit()def test_recommended_code():"""Please use this code."""driver = Chrome()driver.get('http://watir.com/examples/shadow_dom.html')shadow_host = driver.find_element(By.CSS_SELECTOR, '#shadow_host')shadow_root = shadow_host.shadow_rootshadow_content = shadow_root.find_element(By.CSS_SELECTOR, '#shadow_content')assert shadow_content.text == 'some text'driver.quit()def test_firefox_workaround():"""Firefox is special."""driver = Firefox()driver.get('http://watir.com/examples/shadow_dom.html')shadow_host = driver.find_element(By.CSS_SELECTOR, '#shadow_host')children = driver.execute_script('return arguments[0].shadowRoot.children', shadow_host)shadow_content = next(child for child in children if child.get_attribute('id') == 'shadow_content')assert shadow_content.text == 'some text'driver.quit()

主要看test_recommended_code这部分代码,可以发现主要抓取some text,并assert
在这里插入图片描述
同理于是按照官方给的示例代码,很容易抓到“nested text

def test_recommended_code():"""Please use this code."""driver = Chrome()driver.get('http://watir.com/examples/shadow_dom.html')shadow_host = driver.find_element(By.CSS_SELECTOR, '#shadow_host')shadow_root = shadow_host.shadow_rootnest_shadow_host = shadow_root.find_element(By.CSS_SELECTOR, '#nested_shadow_host')nest_shadow_root = nest_shadow_host.shadow_rootnest_shadow_content = nest_shadow_root.find_element(By.CSS_SELECTOR, '#nested_shadow_content')assert nest_shadow_content.text == 'nested text'driver.quit()

在控制台我们能得到同样的输出结果:
在这里插入图片描述
在这里插入图片描述

测试案例

打开本文开头指向的无界链接,进入F12。
在这里插入图片描述
这里主要看下,相比前一个简单例子的区别:

  1. 这里可以很明显看到wujie,这个词在之后抓取其他shadow-root网页会很常见。该测试案例网站也有介绍章节。
  2. shadow Dom内嵌<html>,同时注意到下方的script脚本,有些网站会写到是异步脚本动态加载js,该过程需要在selenium中使用 timesleep或者until 来解决动态加载问题。
    如下(异步加载js):在这里插入图片描述
    参考前一个例子来抓取仓库地址
import time
from selenium import webdriver
from selenium.webdriver.common.by import By# 设置 Chrome options
options = webdriver.ChromeOptions()
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36')# 初始化 WebDriver
driver = webdriver.Chrome(options=options)
# 在页面中执行自定义 JavaScript 代码try:# 打开目标网页url = "https://wujie-micro.github.io/demo-main-vue/vue3?vue3=%2Fdemo-vue3%2Fhome"driver.get(url)time.sleep(5)# 等待页面加载并找到目标元素所在的 Shadow DOM# wait = WebDriverWait(driver, 20)  # 等待最多20秒# shadow_host = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "wujie-app.wujie_iframe")))shadow_host = driver.find_element(By.CSS_SELECTOR,"wujie-app.wujie_iframe")shadow_root = shadow_host.shadow_roothtml_element = shadow_root.find_element(By.CSS_SELECTOR,'html')button = html_element.find_element(By.CSS_SELECTOR,".el-button")assert button.text == '仓库地址'except Exception as e:print("Error:", e)finally:driver.quit()

恭喜你,你会收到一份报错
在这里插入图片描述尝试F12控制台js获取,复制JS路径,控制台输出,说明没问题。
在这里插入图片描述
尝试在python selenium运行js

	#加不加return 返回都是noneresult = driver.execute_script('return document.querySelector("#app > div.content > div > wujie-app").shadowRoot.querySelector("#app > div:nth-child(2) > div.content > p:nth-child(4) > button > span")')

result返回是none
在这里插入图片描述

于是就开始查这个问题查了大半天,百度 csdn,stackoverflow什么的翻了个底朝天,几乎一度快放弃了。。。。到这里以为是被反爬了,但后想了一想该网站应该不是一个商用的客户网站,不涉及到用户信息,应该不会有反爬才对,所以决定再试试。
实在不行了,打算去看看chromedriver的开源代码查查到底为什么,查之前抱着试一试的态度在chromedriver的 issue中搜我的问题竟然找到了解决办法!

只能说十分感谢该老哥!链接指向

感兴趣的可以去看源码,我自己后面去看了下里面这个IsNodeReachable,这里就不贴图分析了。
在这里插入图片描述
问题大概是 Shadow DOM 的封装性导致节点在 Chromedriver 的验证中被误判为不可达。
所以解决思路: 通过修改 Shadow DOM 节点的行为(伪装其父节点为全局文档),绕过验证逻辑。
于是修改后代码如下:

import time
from selenium import webdriver
from selenium.webdriver.common.by import By# 设置 Chrome options
options = webdriver.ChromeOptions()
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36')# 初始化 WebDriver
driver = webdriver.Chrome(options=options)
# 在页面中执行自定义 JavaScript 代码try:# 打开目标网页url = "https://wujie-micro.github.io/demo-main-vue/vue3?vue3=%2Fdemo-vue3%2Fhome"driver.get(url)time.sleep(5)driver.execute_script("""Object.defineProperty(window.document.querySelector('wujie-app').shadowRoot.firstElementChild, "parentNode", {enumerable: true,configurable: true,get: () => window.document,});""")# 等待页面加载并找到目标元素所在的 Shadow DOM# wait = WebDriverWait(driver, 20)  # 等待最多20秒# shadow_host = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "wujie-app.wujie_iframe")))#方法1,直接复制js路径,更简单result = driver.execute_script('return document.querySelector("#app > div.content > div > wujie-app").shadowRoot.querySelector("#app > div:nth-child(2) > div.content > p:nth-child(4) > button > span")')#方法2 一步步的,我个人感觉这样更清晰,对于初学,我建议这样层层递进,并学会# find_element(By.CSS_SELECTOR 的使用shadow_host = driver.find_element(By.CSS_SELECTOR,"wujie-app.wujie_iframe")shadow_root = shadow_host.shadow_roothtml_element = shadow_root.find_element(By.CSS_SELECTOR,'html')button = html_element.find_element(By.CSS_SELECTOR,".el-button")#方法2 Ansassert button.text == '仓库地址'#方法1 Ansassert result.text == '仓库地址'print(button.text)print(result.text)
except Exception as e:print("Error:", e)finally:driver.quit()

最后成功拿到 仓库地址(好的,现在就偷仓库东西去了)
在这里插入图片描述

课后作业!

试试抓取 某企鹅下的 某moba手游论坛下的用户名,日期!
实验链接:彻底疯狂!

I got it ,代码和上述大差不差
在这里插入图片描述

相关文章:

python selenium(4+)+chromedriver最新版 定位爬取嵌套shadow-root(open)中内容

废话不多说&#xff0c;直接开始 本文以无界作为本文测试案例&#xff0c;抓取shadow-root&#xff08;open&#xff09;下的内容 shadow Dom in selenium&#xff1a; 首先先讲一下shadow Dom in selenium 版本的区别&#xff0c;链接指向这里 在Selenium 4版本 以及 chrom…...

React基础教程(11):useCallback记忆函数的使用

11、useCallback记忆函数 防止因为组件重新渲染,导致方法被重新创建,起到缓存作用,只有第二个参数变化了,才重新声明一次。 示例代码: import {useCallback, useState} from "react";const App = () =>...

arp-scan 移植到嵌入式 Linux 系统是一个涉及多个步骤的过程

将 arp-scan 移植到嵌入式 Linux 系统是一个涉及多个步骤的过程。arp-scan 是一个用于发送 ARP 请求以发现网络上设备的工具&#xff0c;它依赖于一些标准的 Linux 库和工具。以下是将 arp-scan 移植到嵌入式 Linux 系统的基本步骤&#xff1a; 1. 获取 arp-scan 源码 首先&a…...

【Linux】常用命令一

声明&#xff1a;以下内容均学习自《Linux就该这么学》一书。 Linux中的shell是一种命令行工具&#xff0c;它充当的作用是人与内核(硬件)之间的翻译官。 大多数Linux系统默认使用的终端是Bash解释器。 1、echo 用于在终端输出字符串或变量提取后的值。 echo "字符串…...

在鲲鹏麒麟服务器上部署MySQL主从集群

因项目需求需要部署主从MySQL集群&#xff0c;继续采用上次的部署的MySQL镜像arm64v8/mysql:latest&#xff0c;版本信息为v8.1.0。计划部署服务器192.168.31.100和192.168.31.101 部署MySQL主节点 在192.168.31.100上先创建好/data/docker/mysql/data和/data/docker/mysql/l…...

Siknhorn算法介绍

SiknHorn算法是一个快速求解离散优化问题的经典算法&#xff0c;特别适用于计算离散分布之间的**最优传输&#xff08;Optimal Transport&#xff09;**距离&#xff1b; 最优传输问题介绍 计算两个概率分布 P 和 Q 之间的传输成本&#xff0c;通常表示为&#xff1a; 是传输…...

群控系统服务端开发模式-应用开发-邮箱短信通道功能开发

邮箱短信通道主要是将邮箱及短信做归属的。具体见下图&#xff1a; 一、创建表 1、语句 CREATE TABLE cluster_control.nc_param_emailsms (id int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 编号,email_id varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci NO…...

[docker中首次配置git环境]

11月没写东西&#xff0c;12月初赶紧水一篇。 刚开始搭建docker服务器时&#xff0c;网上找一堆指令配置好git后&#xff0c;再次新建容器后忘记怎么配了&#xff0c;&#xff0c;这次记录下。 一、git ssh指令法&#xff0c;该方法不用每次提交时输入密码 前期准备&#xff0…...

书生浦语·第四期作业合集

目录 1. Linux基础知识 1.1-Linux基础知识 1.在终端通过ssh 端口映射连接开发机 2. 创建helloworld.py 3.安装相关包并运行 4.端口映射并访问相关网页...

5G学习笔记之PRACH

即使是阴天&#xff0c;也要记得出门晒太阳哦 目录 1. 概述 2. PRACH Preamble 3. PRACH Preamble 类型 3.1 长前导码 3.2 短前导码 3.3 前导码格式与小区覆盖 4. PRACH时频资源 4.1 小区所有可用PRACH资源 4.2 SSB和RACH的关系 4.3 PRACH时频资源配置 1. 概述 随机接入…...

Ubuntu24.04配置DINO-Tracker

一、引言 记录 Ubuntu 配置的第一个代码过程 二、更改conda虚拟环境的默认安装路径 鉴于不久前由于磁盘空间不足引发的重装系统的惨痛经历&#xff0c;在新系统装好后当然要先更改虚拟环境的默认安装路径。 输入指令&#xff1a; conda info可能因为我原本就没有把 Anacod…...

抓包之查看websocket内容

写在前面 本文看下websocket抓包相关内容。 1&#xff1a;正文 websocket基础环境搭建参考这篇文章。 启动后&#xff0c;先看chrome的network抓包&#xff0c;这里我们直接使用is:running来过滤出websocket的请求&#xff1a; 可以清晰的看到发送的内容以及响应的内容。在…...

【Leetcode Top 100】21. 合并两个有序链表

问题背景 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 数据约束 两个链表的节点数目范围是 [ 0 , 50 ] [0, 50] [0,50] − 100 ≤ N o d e . v a l ≤ 100 -100 \le Node.val \le 100 −100≤Node.val≤100 l 1 l_1 …...

账本模型

05-账本模型 1 账本模型 1.1 传统线性增长模型 传统的 MySQL 等系统采用线性增长的日志模型&#xff0c;通过一个 Leader 和多个 Follower 进行状态同步。这种方式有单点的带宽瓶颈问题。 1.2 区块链共享账本模型 共享账本&#xff1a;树形增长。在去中心化网络中&#xff0c;…...

openwrt利用nftables在校园网环境下开启nat6 (ipv6 nat)

年初写过一篇openwrt在校园网环境下开启ipv6 nat的文章&#xff0c;利用ip6tables控制ipv6的流量。然而从OpenWrt22版本开始&#xff0c;系统内置的防火墙变为nftables&#xff0c;因此配置方法有所改变。本文主要参考了OpenWRT使用nftables实现IPv6 NAT 这篇文章。 友情提示 …...

24.12.02 Element

import { createApp } from vue // 引入elementPlus js库 css库 import ElementPlus from element-plus import element-plus/dist/index.css //中文语言包 import zhCn from element-plus/es/locale/lang/zh-cn //图标库 import * as ElementPlusIconsVue from element-plus/i…...

记录QT5迁移到QT6.8上的一些问题

经常看到有的同学说网上的教程都是假的&#xff0c;巴拉巴拉&#xff0c;看看人家发布时间&#xff0c;Qt官方的API都会有所变动&#xff0c;多搜索&#xff0c;多总结&#xff0c;再修改记录。 下次遇到问题多这样搜索 QT 4/5/6 xxx document&#xff0c;对比一下就知道…...

清理Linux/CentOS7根目录的思路

在使用Linux服务器过程中&#xff0c;经常会遇到磁盘空间不足的问题&#xff0c;好多应用默认安装在根目录下&#xff0c;记录一下如何找到问题所在&#xff0c;清理根目录&#xff08;/&#xff09; 1. 检查空间使用情况 1.1 查看分区占用&#xff1a; df -h输出&#xff1…...

【LInux】kvm添加u盘启动引导

前提&#xff1a;要有一个u盘的启动盘 1、查看u盘设备信息 # lsusb ....忽略其他设备信息&#xff0c;查看到u盘设备 Bus 005 Device 005: ID 0951:1666 Kingston Technology DataTraveler 100 G3/G4/SE9 G2## 主要记住ID 0951:1666确认id为ID 0951:1666 2、修改配置文件 如…...

.net XSSFWorkbook 读取/写入 指定单元格的内容

方法如下&#xff1a; using NPOI.SS.Formula.Functions;using NPOI.SS.UserModel;using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric;/// <summary>/// 读取Excel指定单元格内容/// </summa…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构

React 实战项目&#xff1a;微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇&#xff01;在前 29 篇文章中&#xff0c;我们从 React 的基础概念逐步深入到高级技巧&#xff0c;涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...