0


Python+selenium

Selenium

XPath

使用文档匹配的方式(查找所有函数some text文本的button元素):driver.find_elements(By.XPATH, ‘//button[text()=“some text”]’)

使用JS的方式去执行查找:driver.execute_script(“document.getElementById(‘web_id’)”)

有用的路径类型:

以下为比较复杂的有用的路径类型,xpath是我觉得适用性最高的方法:

  1. //div[ contains(text(),“确定”) and contains(@id,‘MessageDialog_’)] ,查找包含确定文本和ID为messagedialog的div元素。
  2. ./…/…//input[@type or @id] | ./…/…//textarea | ./…/…//select
  3. //button[@name=“submit-btn-saverecord”]
  4. ./…/…//label[@class and not(@id)]
  5. ./…/…//table | ./…/…//div[@role] | ./…/…//button[contains(@data-qa,‘ok-button-visible’)] | ./…/…//ul[@role=‘listbox’]

selenium通常可使用的查找方法

class:driver.find_element(By.XPATH, “class name”) :class name为web中需要的classname,使用这个的时候如果classname中含有空格分离的多个classname,那么取一个就可以,但是使用xpath的时候,就需要全部的classname才行。除非具体路径中含有contains()这样函数,那么路径中含有匹配的类名的都可以,可以不是空格分割,可以是一个class-name-type这种形式,使用第一行的查找语句的时候,只要包含class-name就可以,类名后面还有的-type不在匹配范围,但是class-name-type才是一个类名。
tag:driver.find_elements(By.XPATH, “tag name”) : 同上

实际的项目处理中:

会需要灵活运用到while True: 和try:except两种方法灵活处理,让程序可以在无限循环中让脚本可以处理各种突发问题。

有用的知识点:

  1. 判断浏览器是否是关闭了,如果关闭了就执行数据库连接和driver等全部关闭。
if driver isnotNone:try:
        driver.execute_script('javascript:void(0);')except:try:
            c.close()
            conn.close()
            driver.quit()
            sys.stdout.write("浏览器应用已关闭,浏览器驱动已结束。脚本结束!!")
            sys.stdout.flush()returnfinally:
            driver =None
  1. 如果元素没有显示,那么,元素的宽高会是0,并且is_didplayed()方法是false,并且元素的text属性为"",即空。但是不是None,元素找不到的时候会是None。但是这个none是find_element()才会有,使用find_elements()的时候,就只能通过len()长度去判断。

三种点击方法:

ele:找到的input元素:使用find_element()或find_elements()找到。结合这篇文章

ele.click():

这种方法在ele。is_displayed() is true的时候才可以用,一旦false了,执行就会报错。

driver.execute_script(“arguments[0].click();”,ele)

这种方法呢,即使在is_displayed()false的情况下,还是可以正常使用,这个方法就不受is_displayed的影响。我的项目都是ele.is_enable()使能,但是有些网页,使能不可见的情况下,也是不能正常点击,不过没有报错。后续有结论继续补充。。。

ActionChains(driver).move_to_element(ele).click(ele).perform()

在is_displayed()false的情况下也可以正常点击,情况基本和使用JS方式点击的一样,就是有些网页的那句开始,同情况下,是会报错的,并且也不能点击。

综上:使用第二种点击方式会更好一点。但是如果说,点击不成功还需要进行进一步处理的话,第三种是最好的,但是一类网址,元素is_displayed()is false is_enabled()is true的情况下,是第二种才能点击成功,第三种不能点击成功,第三种点击不成功的提示是没有size and location,有可能的影响是什么呢?但是后来我发现,其实,第二种也是点击成功了的,只不多,他页面上的按钮样式是个label,里面有个onclick()方法,是点击这个label的onclick()方法,然后在去点击第二种方法的那种点击效果,只不过第二种方法直接点input是不会显示的,显示出来的只有label。
综上:如果朋友们碰到第二种方法点击失效的情况,可以去看看是不是第二种方法点击的input或是什么是个hidden,你去掉这个属性之后,就会发现,第二种方法也是点击成功了,然后自信去看,是由哪里的onclick()方法让这个按钮显示的,直接去点击这个元素,然后让网页自动调用js点击即可。(不知道有没有懂,但是如果你也碰到这个情况,去看看网页,重点就hidden和onclick)
!。如果是我,就算做特殊处理,也会使用第二种点击方式。第一种一般不使用,限制太大。

点击失效的处理方法:

有些情况,会需要聚焦才能成功点击,使用js的聚焦方法:driver.execute_script(“arguments[0].focus();”,element)

ActionChains()和driver.execute_script()一些理解:

首先,如果点击的是input这种类型,execute_script无论元素是否是hidden,都是可以成功点击的。但是如果点击的是hidden类型的话,ActionChains这种方式就有可能不会点击成功。我的理解是,ActionChains是模拟点击,是需要一个假想的鼠标移动的动作(他和pyautogui包的区别是并没有把真实的鼠标进行移动,如果是多线程或进程操作鼠标的时候,pyautogui对真实鼠标的操作会造成意想不到的结果。),所以如果点击的元素如果是鼠标无法到达的,或是元素没有display的,都是会造成错误。但是如果我们使用的是css_selector进行元素查找,比如我们浏览器插件中的CSS选择器,那么这个元素就一定是display和鼠标可到达的,此时我们就可以使用ActionChains。

三种等待方法:

首先,需要等待主要就是因为网速或是网页的渲染速度影响到元素的查找。在元素还没有加载进入dom的时候,我们find_element()查找元素是会直接报错的。

第一种:time.sleep(number seconds):

这种方法是固定的等待时间,如果我们确认等待这么多秒可以直接使用这个,但是使用这个的话,就意味着如果元素加载快了也是要等这么多时间,会影响到代码的执行效率。而且稳定性很差,因为会出现一种情况就是,我们等待了20S,我们以为这个时间充足了,但是由于其他原因,我们需要等待30S才能加载完这个元素,这样就会出现错误,所以,这个其实,只是我们主观臆测的可控。其实,并不是真的可可控。一般情况下是不建议使用这种方法。

第二种:driver.implicitly_wait(number seconds):

这种等待方式的话就是每次find_element()都是固定的这么多时间,无论加载快慢,都会停止固定的时间。在我的项目中,这种方法会使得网页的执行非常的慢,甚至比time.sleep()还要慢,time.sleep()我可以自行考虑代码的位置进行等待,可以控制一下时间。但是这个方法的haul,每次的查找都要固定这么长的时间,已经加载了的也是要查找等待这么久。结合第一种方法说的情况,要是某一个元素出现需要等待30S的情况,如果我们为了保险设置为30S,那么加载快的也需要等待30S,查找2次就需要一分钟,这个的执行效率就非常的低。这种方法一般情况下是不用的,没有尽量不用一说,根本就不考虑用。

第三种WebDriverWait():

语法举例:WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located((By.XPATH, res_QuestionAndNext[0][1])))
,需要在每个需要检查元素前面执行等待。
这个方法属于显示等待,可以设置最长的等待时间,并且可以设置隔多久检查一次,如果有了就可以退出等待,非常的灵活,比如上面的等待30S,(我们去找网页的最底部的一个元素,判断这个元素是否加载进来了,就可以判断界面是否已经全部加载完毕)我们可以设置方法的最长等待时间是30S,如果元素加载很快的话,其实就不需要等待30S,而是间隔*倍数的时间。这种方法是最灵活也是最能提高代码效率的。

补充:可以使用stalecess_of()方法去判断元素是否过期,配合等待加载。完美加载完全加载一个网页再执行,不会出现找不到元素的情况。语法举例:WebDriverWait(driver, 20, 0.5).until(staleness_of(yuansu))

参考链接:三种等待比较详细的链接

实现拖拽的几种方法:

首先第一点,就是批量拖拽的时候不要没执行一次就释放动作,这样子,动作不会保存下来。但是我的测试结果是,ActionChains()执行完动作组后不需要释放也是可以正常的,我们可以在看到还是处于hold()的状态的时候再release(),盲猜是perform()之后就已经释放了。

进入正题:

第一种:使用import pyautogui,这个包是控制鼠标的包,就是直接拖动我们的鼠标去操作,所以,我们对鼠标的移动都会影响到这个包的执行,但是我们的项目是要求多开的并且需要人工操作的效率所以不采用。不过这里补充该方法:

pyautogui.moveTo(list_select_div_.location['x']+20, list_select_div_.location['y'])
pyautogui.dragTo(list_select_div_.location['x']+20, list_select_div_.location['y']-dict_option_li[list_select_div_]* wid,duration=1)

第二种:使用js的方式,印象助中这个方法是可以正常使用的,但是不知道为什么后来我再次执行的时候就不行了,不深究原因,直接使用try…except…两个都用。driver.execute_script(“arguments[0].simulateDragDrop({dropTarget: arguments[1]})”, list_select_div_, question)

第三种:点击,保持点击,移动。思路是这个,执行的代码有多种。重点就是执行完动作组再释放,如果for循环中执行完一个就释放的话,动作组会不保存,在我的项目中是这样,具体项目可能项目背景有所区别,自行测试。
ActionChains(driver).drag_and_drop_by_offset(list_select_div_, 0, -1 * dict_option_li[list_select_div_] * wid - 50).perform() 这个是使用drag_anddrop_by_offset()方法,还有一种方法是 drag_and_drop(),直接把元素移动到目标元素重合的位置。我的项目中两种方法都适用。还有一种就是==ActionChains(driver).move_to_element(list_select_div_).click_and_hold().move_by_offset(xoffset=0, yoffset=-1 * dict_option_li[list_select_div_] * wid-100).perform()==移动到元素,点击并保持。再移动,实现拖拽。

错误提示:

  1. StaleElementReferenceException:页面刷新后,旧元素会失效,引发此错误。selenium给每个页面dom元素分配一个id,如果页面刷新id就会发生变化,所以我们就可以通过页面元素is变量来判断页面是否发生变化。
  2. 这个异常并不是代码异常,而是,我执行:
zoom =1
driver.execute_script("document.body.style.transform='scale({0})'".format(zoom))

这个代码段的时候,我再使用==driver.execute_script(“arguments[0].scrollIntoView(false);”, question.find_element(By.XPATH, “./…/…”))把元素移动到可视区域的时候,发生了地步footer跟随移动的这个错误。换用方法driver.execute_script(“document.body.style.zoom=‘{0}’”.format(zoom)) ==时执行正常,就是移动元素的时候还是会出现不符合预期的移动效果。

防检测方法

import time
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options

chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--headless")
chrome_options.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36')

driver = Chrome(options=chrome_options)withopen('/Users/kingname/test_pyppeteer/stealth.min.js')as f:
    js = f.read()

driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{"source": js
})
driver.get('https://bot.sannysoft.com/')
time.sleep(5)
driver.save_screenshot('walkaround.png')# 你可以保存源代码为 html 再双击打开,查看完整结果
source = driver.page_source
withopen('result.html','w')as f:
    f.write(source)

使用selenium出现的特征值,不只是webdriver,单单是隐藏一个是没有意义的,需要尽可能的全部隐藏起来。stealth.min.js是在github上跟随版本同步更新的。可以自行去github查询。

附:

以下为一些附录的相关知识:

  1. 缩小界面之后,拖拽类移动的距离也是要缩放的。
  2. 浏览器缩放长宽都有限制,最小值都适合固定的。

本文转载自: https://blog.csdn.net/qq_31749395/article/details/126697208
版权归原作者 TangCoder 所有, 如有侵权,请联系我们删除。

“Python+selenium”的评论:

还没有评论