嘘~ 正在从服务器偷取页面 . . .

Selenium


浏览器操作

浏览器配置


from selenium import webdriver
from selenium.webdriver.chrome.service import Service

# 创建浏览器控制
options = webdriver.ChromeOptions()
# options.add_argument('--disable-gpu')  # 禁用gpu加速
# options.add_argument('--headless')    # 无头浏览器启动
options.add_argument("--disable-blink-features=AutomationControlled")  # 去掉webdriver痕迹
# options.add_argument('--incognito')  # 无痕模式启动 (如果要存储缓存请勿开启)
options.add_argument("--no-first-run")  # 跳过 Chromium 首次运行检查。(启动慢的问题)
options.add_argument('--ignore-certificate-errors')  # 禁用扩展插件
options.add_argument('lang=zh-CN,zh,zh-TW,en-US,en')  # 设置语言
options.add_experimental_option('prefs', {'profile.managed_default_content_settings.images': 2})  # 不加载图片
options.add_argument('start-maximized')  # 最大化显示窗口
# 有些网页会检测谷歌浏览器的版本,太低会无法进入网站(在这将UA换为最新的浏览器UA即可过掉)
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/999.0.4430.212 Safari/537.36')
options.add_experimental_option('useAutomationExtension', False)  # 关闭浏览器提示,自动化控制
options.add_experimental_option('excludeSwitches', ["ignore-certificate-errors"])  # 关闭chrome证书提醒
options.add_experimental_option('excludeSwitches', ['enable-automation'])  # 开启开发者模式
# Selenium 4.0开始驱动要以下面的方式进行传入 service=Service(r"chromedriver.exe")
driver = webdriver.Chrome(service=Service(r"chromedriver.exe"), options=options)  # 设置插件地址
# 重写原型,获取webdriver的时候返回undefined:新版本失效。
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": """Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"""})

神秘代码:$cdcasdjflasutopfhvcZLmcfl



  • 虽然不推荐,但有时候确实是需要使用 Selenium 配合 Scrapy,先登录,获取 Cookie,然后交由 Scrapy 进行处理。
  • 当 Selenium.get_cookies():返回的是一段代码,要经过处理;
cookie = [i['name'] + '=' + i['value'] for i in cookies]
cookie_str = '; '.join(i for i in cookie)
ck = {i.split('=')[0]: i.split('=')[1] for i in cookie_str.split('; ')}

Request:以下处理之后,可以给 Request 使用

# 推导式的cookies是获取到的cookie
cookie = [i['name'] + '=' + i['value'] for i in cookies]
ck = '; '.join(i for i in cookie)
request.cookies = {l.split("=", 1)[0]: l.split("=", 1)[1] for l in cookie.split("; ")}

页面操作


  • 访问页面:driver.get(“https://www.baidu.com")
  • 当前页面 URL:driver.current_url
  • 后退:driver.back()
  • 前进:driver.forward()
  • 刷新:driver.refresh()
  • 标题:driver.title

句柄


  • 获取当前操作的窗口句柄:driver.current_window_handle
  • 获取所有窗口句柄:driver.window_handles
  • 将页面切换到指定窗口句柄:driver.switch_to.window(handle)

窗口操作


窗口句柄操作

  • 创建之后会自动切换到当前窗口的句柄

    • 创建一个新的标签页:driver.switch_to.new_window(‘tab’)
    • 创建一个新的浏览器窗口:driver.switch_to.new_window(‘window’)
    • 切换到指定窗口:driver.switch_to.window(handle)
  • 关闭窗口:driver.close()

    • 关闭窗口之后需要手动切换窗口句柄:driver.switch_to.window(handle)
  • 退出浏览器:driver.quit()

    • 这里可以用 try 包括起来,以便在报错的情况下也可以正确退出浏览器
    • 支持 with 关键字,在结束之后自动退出:with webdriver.Chrome() as driver:

窗口坐标操作

  • 获取浏览器的宽高:driver.get_window_size()**

    • 返回的是一个字典
    • width = driver.get_window_size().get(“width”)
    • 设置浏览器宽高:driver.set_window_size(1024, 768)**

  • 获取浏览器左上角的坐标位置:

    • 返回的也是一个字典:driver.get_window_position()
  • x = driver.get_window_position().get(‘x’)**

  • 设置窗口位置:

    • driver.set_window_position(0, 0)
  • 推荐直接最大化窗口:driver.maximize_window()

  • 最小化窗口:driver.minimize_window()

  • 全屏:driver.fullscreen_window()

    • 这里相当于按 F11,是没有浏览器外部的,只有页面。

截屏


  • 截屏:driver.save_screenshot(‘image.png’)

  • 截取元素:element.screenshot(‘image.png’)

    • 这里截取到的是只包含元素的**

执行脚本


  • 执行脚本:driver.execute_script(‘return arguments[0].innerText’, header)

    • 前面可以理解为一个函数,后面是参数,arguments 内在 JS 中默认包含传入函数的参数
  • 也可以直接执行一段 JS 脚本

  • 滑动页面:driver.execute_script(‘window.scrollBy(0, -400)’)


iframe


  • 这个是许多新手找不到元素的原因,在一个网页中可以嵌套另外一个网页
  • 导致了这个页面浏览器是可以直接搜到,但 selenium 只对顶级元素进行操作
# 先通过定位,找到iframe标签
iframe = driver.find_element('***')

# 切换到iframe标签内
driver.switch_to.frame(iframe)

# 当你操作完毕之后,记得退回默认页
driver.switch_to.default_content()
  • 如果 iframe 有 ID 值那么可以直接传入 id 值进行切换:driver.switch_to.frame(‘id’)
  • 不推荐使用 name 名字进行切换,如果有多个,将会切换到找到的第一个。

元素操作


  • 定位单个元素:driver.find_element(By.Xpath, ‘button’)

  • 但这种方式只要没找到立马报错。非常非常不推荐直接使用用这种写法。

    • 下面的等待中,会搭配等待一起使用.
  • 你可以通过已经找到的一个元素,再次进行查找,这在处理多个元素内的内容时很有用

    element = driver.find_element(By.XPATH,"//*[@id='form']")
    element.find_element(By.XPATH,"./***")
  • 定位多个元素:driver.findelements(By.XPATH,”//[@id=’form’]”)_

    • 其实就是后面多了一个 s
    • 这里定位多个元素之后,返回的是一个列表
  • 切换到活动的元素:driver.switch_to.active_element

    • 比如按下了某个 button,那么driver.switch_to.active_element中就代表被按下的那个元素
  • 判断元素是否启用element.is_enabled()

  • 判断是否选择了元素:element.is_selected()**

    • 此方法确定引用的元素是否被选中。此方法广泛用于复选框、单选按钮、输入元素和选项元素。
  • 获取元素标签名:element.tag_name

  • 获取元素坐标信息:element.rect

    • 元素左上角的 X 轴位置
    • 从元素左上角的 y 轴位置
    • 元素的高度
    • 元素的宽度
  • 获取元素 css 样式:element.value_of_css_property(‘color’)

  • 获取元素文本:element.text

  • 获取元素的指定属性:element.get_attribute(‘value’)

selenium 提供的相对查找

  • 我是不会使用,感觉没有 xpath 的定位准确
from selenium.webdriver.common.by import By
from selenium.webdriver.support.relative_locator import locate_with

# 先找到一个元素
passwordField = driver.find_element(By.ID, "password")
# 相对这个元素,找到这个元素之前的内容
emailAddressField = driver.find_element(locate_with(By.TAG_NAME, "input").above(passwordField))

# above:指定元素之前
# below:指定元素之后
# toLeftOf:指定元素左边
# toRightOf:指定元素右边
# 返回最多50px远距离的元素:near
# 当然如果你懂得xpath,可以使用./进行相对查找

元素操作之——等待


显式等待

  • Selenium 客户端可以使用显式等待来处理命令式、过程式语言。它们允许您的代码停止程序执行或冻结线程,直到您传递的条件解决为止。以特定频率调用条件,直到等待超时结束。这意味着只要条件返回一个假值,它就会继续尝试和等待。
# 下面这段代码,在十秒内连续调用后面的lambda表达式,十秒内查找到就返回指定元素,如果没有查找到,就抛出异常
el = WebDriverWait(driver, timeout=10).until(lambda d: d.find_element(By.XPATH, "//*"))
# 传递给我们的匿名函数的第一个也是唯一的参数始终是对我们的驱动程序对象WebDriver的引用
# 如果需要查找多个元素,find_element改为find_elements即可。
# 我上面说过需要配合等待进行查找。

隐式等待

  • 有另一种不同于显式等待的等待类型, 称为隐式等待。通过隐式等待,WebDriver 在尝试查找任何元素时会在一段时间内轮询 DOM 。当网页上的某些元素无法立即使用并且需要一些时间来加载时,这会很有用。

  • 警告: 不要混合隐式和显式等待。这样做可能会导致不可预测的等待时间。例如,设置 10 秒的隐式等待和 15 秒的显式等待可能会导致在 20 秒后发生超时。

  • 可以使用:driver.implicitly_wait(10)

  • 来设置全局隐式等待。

灵活等待

  • FluentWait 实例定义了等待条件的最长时间,以及检查条件的频率。
    用户可以配置等待以在等待时忽略特定类型的异常,例如 NoSuchElementException 在页面上搜索元素时。
# 定义等待,并且在等待期间无视某些错误
wait = WebDriverWait(driver, 10, poll_frequency=1, ignored_exceptions=[ElementNotVisibleException, ElementNotSelectableException])
# 查找元素,并且元素是可以点击的。
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//div")))
# 上面的EC库在下面会有讲解

搭配等待一起使用的库:EC

from selenium.webdriver.support import expected_conditions as EC

  • 下面这段代码,等待查找元素,并且在元素可以点击,并且可见的情况下才返回真。

wait.until(EC.element_to_be_clickable((By.XPATH, "//div")))

  • EC 库的具体 API:
方法 说明
title_is 判断当前页面的 title 是否完全等于(==)预期字符串
title_contains 判断当前页面的 title 是否包含预期字符串
presence_of_element_located 判断某个元素是否被加到了 dom 树里,并不代表该元素一定可见
visibility_of_element_located 判断某个元素是否可见. 可见代表元素非隐藏,并且元素的宽和高都不等于 0
visibility_of 跟上面的方法一样,直接传定位到的 element
presence_of_all_elements_located 判断是否至少有 1 个元素存在于 dom 树中。举个例子,如果页面上有 n 个元素的 class 都是’column-md-3’,那么只要有 1 个元素存在,返回 True
text_to_be_present_in_element 判断某个元素中的 text 是否,包含预期的字符串
text_to_be_present_in_element_value 判断某个元素中的 value 属性是否,包含预期的字符串
frame_to_be_available_and_switch_to_it 判断该 frame 是否可以 switch 进去
invisibility_of_element_located 判断某个元素中是否不存在于 dom 树或不可见
element_to_be_clickable 判断某个元素中是否可见并且是否可以点击
staleness_of 等某个元素从 dom 树中移除
element_to_be_selected 判断某个元素是否被选中了,一般用在下拉列表
element_selection_state_to_be 判断某个元素的选中状态是否符合预期
element_located_selection_state_to_be 跟上面的方法作用一样,只是上面的方法传入定位到的 element
alert_is_present 判断页面上是否存在 alert
new_window_is_opened 判断窗口是否增加,传入窗口数量
number_of_windows_to_be 期望窗口为多少
frame_to_be_available_and_switch_to_it 判断是否切换到 iframe

键盘操作


键盘操作

  • 在可以输入的元素中键入内容:element.send_keys(“fuck”)
  • 并且你可以输入文本内容,再键入回车:element.send_keys(“webdriver” + Keys.ENTER)

键盘操作——动作链:AC

  • webdriver.ActionChains(driver).key_down(Keys.CONTROL).send_keys(“a”).perform()
    • 按下 ctrl+a:后面的**perform()**表示执行动作链
    • 注意:执行完动作链之后,键盘的 crtl 还是处于按下状态,并没有弹起
  • action.key_down(Keys.SHIFT).send_keys_to_element(search,”qw”).key_up(Keys.SHIFT).send_keys(“qw”).perform()
    • 上面的动作链执行按下 shift 并且输入内容到指定元素,再弹起 shitf 再输入内容到元素
    • 这只是一个例子,如果要键入大写内容直接写就行了,不要脱裤子放屁
  • 清除元素内容:SearchInput.clear() > + 这仅适用于可编辑和可交互的元素,否则 Selenium 返回错误 > + 我平常的开发中经常遇到无法清除的问题 > + 最好的解决办法是用动作链模拟全选,键入 delete 进行彻底清除。
    API 功能
    perform(self): 执行链中的所有动作
    reset_actions(self): 清除存储的动作
    click(self, on_element=None): 鼠标左键单击
    click_and_hold(self, on_element=None): 鼠标左键单击,不松开
    context_click(self, on_element=None): 鼠标右键单击
    double_click(self, on_element=None): 鼠标左键双击
    drag_and_drop(self, source, target): 拖拽到某个元素后松开
    drag_and_drop_by_offset(self, source, xoffset, yoffset): 拖拽到某个坐标后松开
    key_down(self, value, element=None): 按下某个键
    key_up(self, value, element=None): 松开某个键
    move_by_offset(self, xoffset, yoffset): 鼠标移动到某个坐标
    move_to_element(self, to_element): 鼠标移动到某个元素
    move_to_element_with_offset(self, to_element, xoffset, yoffset): 移动到距某个元素(左上角)多少的位置
    release(self, on_element=None): 松开鼠标
    send_keys(self, *keys_to_send): 发送某些值到当前焦点元素
    send_keys_to_element(self, element, *keys_to_send): 发送某些值到指定元素

动作链中可以使用的部分按键

  • BACKSPACE:退格、删除键
  • TAB :有时可用来切换 input 框的焦点
  • ENTER: 回车键
  • SHIFT:和其他按键同时发送,可发送大写字母或特殊符号
  • **CONTROL **:和其他按键同时发送可实现一些功能如( 就是 Ctrl )
  • ALT:和其他键一起使用
  • SPACE :输入空格,可以选中 checkbox、radio 框
  • PAGE_UP / PAGE_DOWN:通过按键可上下翻页

  • send_keys(Keys.CONTROL,’a’)   # 全选(Ctrl+A)
  • send_keys(Keys.CONTROL,’c’)   # 复制(Ctrl+C)
  • send_keys(Keys.CONTROL,’x’)   # 剪切(Ctrl+X)
  • **send_keys(Keys.CONTROL,’v’) **  # 粘贴(Ctrl+V)

处理弹出的窗口


消息

# 等待查找,中间的语法固定(等待窗口出现)
alert = wait.until(expected_conditions.alert_is_present())
# 查找到之后,将文本存贮在变量中
text = alert.text
# 点击确定按钮
alert.accept()

确认

# 这是处理窗口的另外一种方式

# 切换到弹出的窗口
alert = driver.switch_to.alert
# 获取文本内容
text = alert.text
# 点击取消
alert.dismiss()
# 也可以点击确定
alert.accept()

输入

# 还是等待窗口弹出
wait.until(expected_conditions.alert_is_present())
# 将警告存储在变量中
alert = Alert(driver)
# 窗口中输入信息
alert.send_keys("Selenium")
# 点击确定按钮
alert.accept()

开启浏览器代理


from selenium import webdriver

PROXY = "<HOST:PORT>"
webdriver.DesiredCapabilities.CHROME['proxy'] = {
    "httpProxy": PROXY,
    "ftpProxy": PROXY,
    "sslProxy": PROXY,
    "proxyType": "MANUAL",

}

with webdriver.Chrome() as driver:
    # Open URL
    driver.get("https://selenium.dev")

修改页面加载策略


  • normal:这将使 Selenium WebDriver 等待整个页面加载完毕。
  • eager:这将使 Selenium WebDriver 等到初始 HTML 文档完全加载和解析完毕,并放弃加载样式表等(也就是页面还在转圈圈就开始走下一步了)
  • none:当设置为 none 时, Selenium WebDriver 只会等到初始页面被下载。(可能你连页面都没看到,就已经开始了)
options = Options()
options.page_load_strategy = '参数修改'
driver = webdriver.Chrome(options=options)

处理文件选择(下载)


  • Selenium 原生没有提供文件选择
  • 如果是简单的 input 文件选择可以直接定位到 element 标签之后直接 send 内容:input.send_keys(“C:/1.jpg”)
  • 但现在大部分网站都采用弹出窗口式的选择,只能用操纵 Win 的键盘库来进行操作。

选择单文件

from pyperclip import copy
from pywinauto import Desktop
from pywinauto.keyboard import send_keys

def select_file(path: str) -> bool:
    """
    调用pywinauto来解决弹出的文件选择框
    :param path: 需要选择的文件路径
    :return: bool
    """
    app = Desktop()
    dlg = app["打开"]
    # 这里输入空,定位到输入框中
    dlg["文件名(&N):Edit"].type_keys('')
    # 使用剪切板进行键入,之前直接用上面方法键入内容会导致路径丢失
    copy(path)
    send_keys('^a')
    send_keys('^v')
    send_keys("{VK_RETURN}")
    return True

选择多文件

from pyperclip import copy
from pywinauto import Desktop
from pywinauto.keyboard import send_keys

def select_files(path: str, filenames: list) -> bool:
    """
    调用pywinauto来解决弹出的文件选择框
    :param path: 要选择多个文件的文件夹路径
    :param filenames: 要选择的文件名,格式如下 ['1.jpg', '2.jpg', '3.jpg', '4.jpg']
    :return: bool
    """
    # 处理所有文件名
    print(filenames)
    filename = "".join(['"' + c + '" ' for c in filenames])
    print(filenames)
    # 使用pywinauto来选择文件
    app = Desktop()
    # 选择文件上传的窗口
    dlg = app["打开"]
    # 选择文件地址输入框,点击激活
    dlg["Toolbar3"].click()
    # 键盘输入上传文件的路径
    copy(path)
    send_keys('^a')
    send_keys('^v')
    send_keys("{VK_RETURN}")
    # 选中文件名输入框,输入文件名
    dlg["文件名(&N):Edit"].type_keys('')
    # 键盘输入上传文件的路径
    copy(filename)
    send_keys('^v')
    send_keys("{VK_RETURN}")
    return True

下载文件处理:

  • 下载文件按理说可以直接使用选择单文件进行处理,但是记得开启浏览器的下载文件弹出窗口。


文章作者: 林木木
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 林木木 !
评论
  目录