IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python Selenium 抓取Shadow Dom内部元素方法更新 -> 正文阅读

[Python知识库]Python Selenium 抓取Shadow Dom内部元素方法更新

By Mejias

背景:

应团队的PMP的要求,为自己的Team开发了一个内部网站信息抓取的工具(整体代码展示见文章末尾,可能稍微有点长)。上上周周写完测试后推给了大家,没有什么问题。今天Team的一个小伙伴突然告诉我报错,显示是Chrome Driver与Chrome版本不对,搜索Chrome://version,才发现是Chrome自动升级了。这样原来版本的Chrome driver就不支持了,导致程序报错。

?

在Chrome Driver官网here重新搜索了版本匹配的Chrome Driver并下载安装好之后,再次运行程序,发现前面都运行的很好直到后面首尾部分报错如下:

报错显示的是下面这里出了问题:

def control_in_shadow(driver,js):

? ? shadow = driver.execute_script(js)

? ? return shadow #返回的对象在这里

js = 'return document.querySelector("#ra-shadow-root").shadowRoot'

shadow= control_in_shadow(driver,js)

shadow.find_element(By.ID,'ra-asin-list-count-input').clear()

shadow.find_element(By.ID,'ra-asin-list-count-input').send_keys('1000')

shadow.find_element(By.ID,'ra-asin-list-load-btn').click()

上面的代码访问下列Shadow Doml里的元素。

?

采用的方法就是常说的三步法:

定位到Shadow Dom的Host节点 =》 使用.shadowRoot属性定位到根节点 =》

直接通过页面Element的方法访问Shadow Dom内部的元素。这种方法在未更新chrome driver 的版本之前一直用的很好的。

但是在更新了Chrome版本和chrome driver版本之后就会报错了。原来的方法在新的chrome driver并不适用。于是在收到小伙伴的反馈后,就需要测试代码问题以及修改和Refine了。

测试&发现问题:

首先根据上面的报错可以定位到是下面的代码出了问题。

?
def control_in_shadow(driver,js):

? ? shadow = driver.execute_script(js)

? ? return shadow #返回的对象在这里

js = 'return document.querySelector("#ra-shadow-root").shadowRoot'

shadow= control_in_shadow(driver,js)

shadow.find_element(By.ID,'ra-asin-list-count-input').clear()

shadow.find_element(By.ID,'ra-asin-list-count-input').send_keys('1000')

shadow.find_element(By.ID,'ra-asin-list-load-btn').click()

?

因为.execute_script(js)是driver对象自带的方法,这一段执行JS语句也没有报错,所以肯定不是定义的新方法的问题。而且看代码报错是说函数返回的对象是一个dict,而不是原来应该是的remote controlled web element元素,导致web element的查找元素的方法.find_element(By.)使用报错。所以我这里定位到这个返回对象“shadow”是否问题。

为了测试它是否只是一个dict还是说代表了shadowRoot这个节点,我们可以使用shadowRoot的一些属性和方法去测试他是否是一个shadowRoot。

shadowDom的shadowRoot有许多的属性,例如:

shadowRoot.host(返回根节点的宿主节点的引用);

shadowRoot.innerHTML(返回对shadowRoot内的DOM树的引用)

shadowRoot.mode(返回shadowRoot的模式 -open或 -closed)

测试代码如下:

def control_in_shadow(driver,js):

? ? shadow = driver.execute_script(js)

? ? return shadow #返回的对象在这里

?js = 'return document.querySelector("#ra-shadow-root").shadowRoot'

?shadow= control_in_shadow(driver,js)

?shadow.host

?shadow.mode

?shadow.innerHTML

测试结果如下:

可以看出返回的shadow对象已经不是一个shadowRoot元素,所以也不能使用他的一些属性,包括上述代码的.find_element(By.Id)方法了。

解决bug的尝试:

这里我们可以看到直接用driver.execute_script()返回值不能再进行页面的一些操作了,但是在代码里运行js语言依然没有问题,所以我们想到的解决办法就是直接书写JS语言在Python代码中运行。比如上述的几个属性可以通过下面的代码得到返回值。

?def control_in_shadow(driver,js):

? ? shadow = driver.execute_script(js)

? ? return shadow #返回的对象在这里

js = 'return document.querySelector("#ra-shadow-root").shadowRoot'

shadow= control_in_shadow(driver,js)

js1 = 'return document.querySelector("#ra-shadow-root").shadowRoot.host'

js2 = 'return document.querySelector("#ra-shadow-root").shadowRoot.mode'

js3 = 'return document.querySelector("#ra-shadow-root").shadowRoot.innerHTML'

host_res = ?control_in_shadow(driver,js1)

mode_res = control_in_shadow(driver,js2)

inner_HTML_res = control_in_shadow(driver,js3)

print(host_res)

print(mode_res)

print(inner_HTML_res)

?

解决测试的运行结果:

可以看出通过直接运行JS可以成功的找到所有的属性返回值,以及访问到宿主节点的IP为一个web element对象。

代码修复:

根据以上的探索,我们可以知道可以通过直接在Python中运行JS语句实现成功的访问shadow Dom里面的元素。基于此我们可以把原始代码修改如下(原始代码不需要的行已经注释起来了):

?
def control_in_shadow(driver,js):

? ? shadow = driver.execute_script(js)

? ? return shadow #返回的对象在这里

js1 = 'return document.querySelector("#ra-shadow-root").shadowRoot.getElementById("ra-asin-list-count-input")'

#shadow= control_in_shadow(driver,js)

input_id = control_in_shadow(driver,js1)

input_id.clear()

input_id.send_keys('1000')

? ? ? ?

#shadow.find_element(By.ID,'ra-asin-list-count-input').clear()

#shadow.find_element(By.ID,'ra-asin-list-count-input').send_keys('1000')

#shadow.find_element(By.ID,'ra-asin-list-load-btn').click()

js2 = 'document.querySelector("#ra-shadow-root").shadowRoot.getElementById("ra-asin-list-load-btn").click()'

load_id = control_in_shadow(driver,js2)

load_id

js3 = 'return document.querySelector("#ra-shadow-root").shadowRoot.getElementById("ra-asin-list-csv-btn")'

? save_id = control_in_shadow(driver,js3)

?

上面的代码运行起来就没有问题了。而且这是通过直接操作JS语句的,速度上也是可以的。

初始代码展示:

以下为整段代码(可能稍微有点长)。后续有机会可以和大家分享下面代码的编写的逻辑。

import pandas as pd

import numpy as np

import os



from selenium import webdriver

from selenium.webdriver.chrome.service import Service

from selenium.webdriver.chrome.options import Options

from selenium.webdriver.common.by import By

from selenium.webdriver.common.action_chains import ActionChains

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support.wait import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC



import selenium.common.exceptions

import requests

import time

import json

import mitmproxy

import pyautogui



def open_chrome_driver(option):

? ? driver = webdriver.Chrome(options = option)

? ? return driver



def open_new_window(driver,url):

? ? new = 'window.open(%s)'%url

? ? driver.execute_script(new)

? ? handles = driver.window_handles

? ? driver.switch_to.window(handles[-1])

? ? return driver

? ?

def open_url(driver,url):

? ? driver.get(url)



def wait_by_clickable(driver,wait_time,ID):

? ? wait = WebDriverWait(driver,wait_time)

? ? wait_name = wait.until(EC.element_to_be_clickable(driver.find_element(By.ID,ID)))

? ? return wait_name



def wait_by_presence(driver,wait_time,ID):

? ? wait = WebDriverWait(driver,wait_time)

? ? wait_name = wait.until(EC.presence_of_element_located((By.ID,ID)))

? ? return wait_name

? ?

def keyboard_perform(driver,ID):

? ? logo = driver.find_element(By.ID,ID)

? ? rightClick = ActionChains(driver)

? ? rightClick.context_click(logo).perform()

? ? time.sleep(4)

? ?

? ? pyautogui.typewrite(['down','down','down','down','down','down','down','down','down'])

? ? time.sleep(4)

? ?

? ? pyautogui.typewrite(['enter'])

? ? time.sleep(5)

? ? pyautogui.typewrite(['enter'])

? ? time.sleep(5)

? ?

def control_in_shadow(driver,js):

? ? shadow = driver.execute_script(js)

? ? return shadow



def find_key_words(file_name):

? ? pecos = pd.read_excel(file_name)

? ? key_words = list(pecos.loc[:,'title'])

? ? print(key_words)

? ? return key_words



def find_index(list,element):

? ? for i in range (0,len(list)):

? ? ? ? if list[i] == element:

? ? ? ? ? ? return i

? ? ? ? else:

? ? ? ? ? ? pass

? ?

def concatenate_file(path):

? ? os.chdir(r'%s./Peco_File_Download'%path)

? ? filelist = []

? ? list_link=[]

? ? filelist2 = []

? ? key_content_list = []



? ? for root, dirs,files in os.walk(".",topdown = False):

? ? ? ? for name in files:

? ? ? ? ? ? str = os.path.join(root,name)

? ? ? ? ? ? if str.split('.')[-1] == 'csv':

? ? ? ? ? ? ? ? filelist.append(str)

? ?

? ? for each_file in filelist:

? ? ? ? name = each_file.split('\\')[1]

? ? ? ? filelist2.append(name)

? ? ? ? key = name.split('-ASIN')[0]

? ? ? ? key_content_list.append(key)



? ? for each_range in range(len(filelist2)):

? ? ? ? list_count = pd.read_csv(r'%s'%filelist2[each_range])

? ? ? ? list_link.append(list_count)

? ? ? ?

? ? for each_file in filelist:

? ? ? ? inx = find_index(filelist,each_file)

? ? ? ? current_list = list_link[inx]

? ? ? ? current_list['key_words'] = key_content_list[inx]

? ? ? ?

? ? df1 = pd.concat(list_link,ignore_index = True)

? ? df2 = df1[['asin','key_words']]

? ? df2.to_csv('合并后的表格.csv')

? ?

def find_one_B_one(keywords,final_sleep_time):

? ? #open_existed_chrome_option

? ? option = webdriver.ChromeOptions()

? ? option.add_experimental_option("debuggerAddress","127.0.0.1:9999")

? ? driver = open_chrome_driver(option)

? ? for i in keywords:

? ? ? ? url = "https://www.amazon.com/"

? ? ? ? open_url(driver,url)

? ? ? ?

? ? ? ? #search&submit keywords

? ? ? ? search_box = wait_by_clickable(driver,1200,'twotabsearchtextbox')

? ? ? ? search_box

? ? ? ?

? ? ? ? search_box.send_keys(i)

? ?

? ? ? ? submit_button = wait_by_clickable(driver,1200,'nav-search-submit-button')

? ? ? ? submit_button

? ? ? ? submit_button.click()

? ? ? ? time.sleep(0.01)



? ? ? ? #retail_assistance

? ? ? ? keyboard_perform(driver,'nav-search-submit-button')

? ? ? ? shadow_root = wait_by_presence(driver,1200,'ra-shadow-root')

? ? ? ? shadow_root

? ? ? ? time.sleep(4)



? ? ? ? js = 'return document.querySelector("#ra-shadow-root").shadowRoot'

? ? ? ? shadow= control_in_shadow(driver,js)



? ? ? ? shadow.find_element(By.ID,'ra-asin-list-count-input').clear()

? ? ? ? shadow.find_element(By.ID,'ra-asin-list-count-input').send_keys('1000')

? ? ? ? shadow.find_element(By.ID,'ra-asin-list-load-btn').click()

? ? ? ? time.sleep(final_sleep_time)

? ? ? ?

? ? ? ? wait = WebDriverWait(driver,4000)

? ? ? ? tag = shadow.find_element(By.ID,'ra-asin-list-csv-btn')

? ? ? ? save_button = wait.until(EC.element_to_be_clickable(tag))

? ? ? ? save_button

? ? ? ? time.sleep(4)

? ? ? ? save_button.click()

? ? ? ? time.sleep(4)



if __name__ == "__main__":

? ? path = os.getcwd()

? ? wait_sleep_time = int(input('请输入您需要等待pecos load的时长/second:'))

? ? keywords = find_key_words('PECO Keywords.xlsx')

? ? find_one_B_one(keywords,wait_sleep_time)

? ? #concatenate_file(path)

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-11-24 07:53:56  更:2021-11-24 07:55:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:18:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码