selenium&selenium-wire使用

1. 为什么使用selenium

  前面爬虫大多使用的是urllib 库, 另外还有requests 库。urllib 可以解决好多问题,包括代理、自定义请求体、cookie 设置等。 这些库都是模拟浏览器进行访问,毕竟是模拟,有些网站针对反爬会造成爬取的数据不准确。

​  这时候就需要引入selenium, selenuim 是真实的驱动浏览器,然后模拟人工操作去进行一系列的操作。

​  举个简单的例子: 京东官网 https://www.jd.com/ 首页,如果通过网页访问我们能拿到秒杀模块; 如果是通过urllib 访问拿到首页源码,发现没有该模块。该模块所在区域的xpath 选择器是: //*[@id='J_seckill']

2. selenium 安装以及简单测试

1. 下载浏览器驱动

​ selenium 是驱动浏览器,然后去进行一系列的操作,所以我们需要下载对应的驱动。

​ chrome 驱动: http://chromedriver.storage.googleapis.com/index.html 根据自己的chrome 版本下载对应的驱动, 大版本一致即可,windows 安装包32 位也可以的。

​ 驱动下载完成后将解压后的exe驱动程序拷贝到运行程序同级目录,或者就放到python 目录然后加入到path 换变量中也可以。需要程序能找到该exe。

2. pycharm 安装selenium

File→Settings→Project:项目名称→python Interpreter->添加selenium

pycharm 安装完成后可以到项目的venv\lib 目录查看现有的模块

3. 本地安装selenium

如果是想python 跑对应脚本,需要本地安装selenium

pip install selenium

测试可以在python 窗口输入:

from selenium import webdriver

4. 简单测试

from selenium import webdriver

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.jd.com/")

# 获取网页源码
content = browser.page_source
print(content)
# 打印url
print(browser.current_url)

# 保存为图片(注意这里只能保存为png,不支持jpg)
browser.save_screenshot("jd.png")

# 打印title
print(browser.title)
# 窗口最大化
browser.maximize_window()

# 关闭
browser.close()

​ 会自动打开浏览器,然后访问京东的首页。然后打印网页源码(从源码可以搜索到id 为 J_seckill 的div)。最后将网页保存为图片后关闭浏览器。

3. selenium 解析以及获取

  1. 获取元素

获取元素主要有下面六个API:当然返回的元素还可以继续调用find_element 元素向下查找。

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.baidu.com/")

# 获取网页源码
# content = browser.page_source
# print(content)

'''
元素定位(改版后主要有两个方法)。 
两个方法参数一样
第一个指定模式: 参考 selenium.webdriver.common.by.By; 
class By:
    """
    Set of supported locator strategies.
    """

    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"
第二个指定属性的值 
find_element(by=By.ID, value=None)    查找单个元素,WebElement。 内部调用查找集合方法,然后返回第一个元素
find_elements(by=By.ID, value=None)     查找集合, WebElement 集合
'''

# 根据ID查找 (百度首页的百度一下按钮)
# button = browser.find_element(by=By.ID, value="su")
# print(button)

# 根据name属性查找 (查找百度首页的输入框)
# input = browser.find_element(by=By.NAME, value="wd")
# print(input)

# 根据xpath 语法查找 (查找百度首页的输入框)
# input = browser.find_element(by=By.XPATH, value="//input[@name='wd']")
# print(input)

# 根据标签的名字查找
# inputs = browser.find_elements(by=By.TAG_NAME, value="input")
# print(len(inputs))

# 根据link 内容查找 也就是根据<a>测试</a> 内部的text 属性查找
# a = browser.find_element(by=By.LINK_TEXT, value="新闻")
# print(a.text)

# css 选择器查找(找百度首页输入框)
a = browser.find_element(by=By.CSS_SELECTOR, value="#kw")
print(a.get_attribute("name"))

# 关闭
browser.close()
  1. 访问元素信息
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.baidu.com/")

# 根据link 内容查找 也就是根据<a>测试</a> 内部的text 属性查找
a = browser.find_element(by=By.LINK_TEXT, value="新闻")
# 获取属性
print(a.get_attribute("href"))
# 获取text
print(a.text)
# 获取tag
print(a.tag_name)

# 关闭
browser.close()
  1. 交互
点击: click()
输入: send_keys()
清空输入: clear()
后退:browser.back()
快进:browser.forward()
模拟JS滚动: js = 'document.body.scrollTop=100000'
		js = 'document.documentElement.scrollTop=100000'
		browser.execute_script(js) # 执行JS代码
获取网页源码: browser.page_source		

4. selenium 爬取百度

一个简单的例子,过程如下:

1.打开百度首页,睡眠2s

2.输入java,睡眠2s

3.点击百度一下按钮,睡眠5s

4.滚动到底部,睡眠5s

5.翻到第二页,睡眠5s

6.点击后退回退到第一页,睡眠5s

7.点击前进再回去第二页, 睡眠5s

8.结束

代码:

import time

from selenium import webdriver
# 1.打开百度首页,睡眠2s
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
browser.get("https://www.baidu.com/")
time.sleep(2)

# 2.输入java,睡眠2s
input = browser.find_element(value="kw")
input.send_keys("java")
time.sleep(2)

# 3.点击百度一下按钮,睡眠5s
button = browser.find_element(value="su")
button.click()
time.sleep(5)

# 4.滚动到底部,睡眠5s
js = 'document.body.scrollTop=100000'
browser.execute_script(js)
time.sleep(5)

# 5.翻到第二页,睡眠5s
nextBtn = browser.find_element(by=By.XPATH, value="//a[@class='n']")
nextBtn.click()
time.sleep(5)

# 6. 点击后退回退到第一页,睡眠5s
browser.back()
time.sleep(5)

# 7. 点击前进再回去第二页, 睡眠5s
browser.forward()
time.sleep(5)

# 8. 关闭
browser.close()

5. selenium 不启动浏览器模式

打开浏览器再启动会浪费时间,对爬虫的性能也是个影响,还有一种就是不打开浏览器。

如下参数是针对chrome 的全局参数,不能自定义参数。

from selenium import webdriver

# 还有一些其他的参数
'''
# 添加UA
options.add_argument('user-agent="MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"')

# 设置代理
options.add_argument("--proxy-server=http://110.52.235.176:9999") # 设置代理,请求头等,以列表的形式传入多个参数

# 设置编码格式
options.add_argument('lang=zh_CN.UTF-8') # 设置编码格式

 # 启动时最大化窗口
options.add_argument('--start-maximized')

# 指定浏览器分辨率
options.add_argument('window-size=1920x3000') 

# 谷歌文档提到需要加上这个属性来规避bug
options.add_argument('--disable-gpu') 

 # 隐藏滚动条, 应对一些特殊页面
options.add_argument('--hide-scrollbars')

# 不加载图片, 提升速度
options.add_argument('blink-settings=imagesEnabled=false') 

# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
options.add_argument('--headless') 

# 以最高权限运行
options.add_argument('--no-sandbox')

# 手动指定使用的浏览器位置
options.binary_location = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" 

#添加crx插件
option.add_extension('d:\crx\AdBlock_v2.17.crx') 

# 禁用JavaScript
option.add_argument("--disable-javascript") 

# 设置开发者模式启动,该模式下webdriver属性为正常值
options.add_experimental_option('excludeSwitches', ['enable-automation']) 

# 禁用浏览器弹窗
prefs = {  
    'profile.default_content_setting_values' :  {  
        'notifications' : 2  
     }  
}  
options.add_experimental_option('prefs',prefs)
'''

option = webdriver.ChromeOptions()
# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
option.add_argument("--headless")
# 谷歌文档提到需要加上这个属性来规避bug
option.add_argument('--disable-gpu')
browser = webdriver.Chrome(executable_path="chromedriver.exe", options=option)
browser.get("https://www.jd.com/")

# 获取网页源码
print(browser.title)

# 关闭
browser.close()

6. 以不打开浏览器的方式设置代理获取当前的IP信息

from selenium import webdriver
from selenium.webdriver.common.by import By

option = webdriver.ChromeOptions()
# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
option.add_argument("--headless")
# 谷歌文档提到需要加上这个属性来规避bug
option.add_argument('--disable-gpu')
# 添加代理
option.add_argument("--proxy-server=http://221.11.233.111:4315")  # 设置代理,请求头等,以列表的形式传入多个参数

browser = webdriver.Chrome(executable_path="chromedriver.exe", options=option)
browser.get("http://www.ipdizhichaxun.com/")

# 获取网页源码
result = browser.find_element(by=By.XPATH, value='//*[@class="result"]')
print(result.text)

# 关闭
browser.close()

结果:

IP地址查询结果:221.11.233.111,IP地址位置:宁夏石嘴山市(联通)

7. 传cookie

import time

from selenium import webdriver

browser = webdriver.Chrome(executable_path="chromedriver.exe")
# 删除原来的cookie
browser.delete_all_cookies()
# 注意需要访问一次才能添加cookie。 selenium的默认域名为data,cookie中带域名,在设置cookie时发现当前域名不包含在cookie中,所以设置失败,一直都是data的这个页面
browser.get("http://127.0.0.1:8088/inner/t4")
# 携带cookie打开
browser.add_cookie({'name': 'ABC', 'value': 'DEF'})
browser.get("http://127.0.0.1:8088/inner/t4")
browser.refresh()

browser.close()
time.sleep(10)

8. 设置等待时间

​ 如果不设置等待时间,find_element会立即查找元素,找不到就会立即报错。一般会设置下时间,让JS渲染完成再找元素。

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
# 设置等待时间,最长等待20 s。 不设置等待事件, find_element 找不到元素会抛出异常,设置完成之后不会报异常,会阻塞直到拿取到对应的元素或者达到指定时间
browser.implicitly_wait(20)
browser.get("http://baidu.com")
print(browser.find_element(by=By.XPATH, value='//div[@class="content"]//img'))

# 关闭
browser.close()

继续改进,找不到元素返回None 或者其他处理:

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
# 设置等待时间,最长等待20 s。 不设置等待事件, find_element 找不到元素会抛出异常,设置完成之后不会报异常,会阻塞直到拿取到对应的元素或者达到指定时间
browser.implicitly_wait(20)
browser.get("http://baidu.com")

try:
    print(browser.find_element(by=By.XPATH, value='//div[@class="content"]//img'))
except Exception as e:
    # ignore
    print("没有元素")

# 关闭
browser.close()

9. selenuim 双击等事件

可以用ActionChains, 最后调用其perform() 方法就是提交事件

        # 找到第一个节点然后扩展
        node1 = browser.find_element(by=By.XPATH,value=' //*[name()="svg"]//*[name()="g"]/*[@class="node"][1]')
        # 调用ActionChains方法,生成一个动作
        # 创建一个ActionChains, 然后可以进行一系列事件、包括右键、双击、单击、拖拽、键盘按下、键盘抬起等事件
        action = ActionChains(browser)
        # 单击
        action.click(node1)
        # 双击
        # action.double_click(node1)
        # 右击
        action.context_click(node1)
        action.perform()

补充:
(1). 测试过程中发现browser 关闭之后,驱动程序不会关系,需要自己cmd 关闭下:

taskkill -im chromedriver.exe -f

(2). selenium 可以获取元素大小location、size、rect
这里需要注意获取到的元素的坐标是相对于浏览器内容区域左上角为原点,也就是不包含头部浏览器菜单栏高度。

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome("chromedriver.exe")
browser.maximize_window()
browser.get("https://www.baidu.com/")

# css 选择器查找(找百度首页输入框)
a = browser.find_element(by=By.CSS_SELECTOR, value="#kw")
print(a.get_attribute("name"))
print(a.location)
print(a.size)
# rect = location + size
print(a.rect)

# 关闭
browser.close()

结果:

wd
{'x': 633, 'y': 222}
{'height': 44, 'width': 550}
{'height': 44, 'width': 550, 'x': 633, 'y': 222}

(3) 上面提到获取的元素的location是相对于浏览器可视区域的左上角为原点,也就是不包括菜单栏的高度。所以当和pyautogui 结合,自动进行鼠标操作的时候需要加上菜单栏高度:

    @staticmethod
    def middle_location(element: WebElement):
        ele_location = element.location
        ele_size = element.size
        middle_x = ele_location.get('x') + ele_size.get('width') / 2
        middle_y = ele_location.get('y') + ele_size.get('height') / 2 + ChromeBrowserContext.browser_bar_height()
        return middle_x, middle_y

    @staticmethod
    def browser_bar_height():
        '''
        获取当前broswer菜单栏高度
        
        :return: 
        '''
        # 执行js 并且拿到结果
        innerHeight = ChromeBrowserContext.__browser.execute_script("return window.innerHeight")
        outerHeight = ChromeBrowserContext.__browser.execute_script("return window.outerHeight")
        return outerHeight - innerHeight

(4) 结合pyautogui 实现拖拽方法

    @staticmethod
    def dragToByXpath(xpath1: str, xpath2: str, time: int):
        '''
        拖拽元素

        :param xpath1: 元素1
        :param xpath2:  元素2
        :param time:    时间
        :return:
        '''
        browser = ChromeBrowserContext.getBrowser()
        xpath1Element = browser.find_element(by=By.XPATH, value=xpath1)
        x, y = ChromeBrowserContext.middle_location(xpath1Element)
        pyautogui.moveTo(x, y)
        xpath2Element = browser.find_element(by=By.XPATH, value=xpath2)
        x, y = ChromeBrowserContext.middle_location(xpath2Element)
        pyautogui.dragTo(x, y, time)

    @staticmethod
    def dragToByElement(element1: WebElement, element2: WebElement, time: int):
        '''
        拖拽元素

        :param element1: 元素1
        :param element2:  元素2
        :param time: 时间
        :return:
        '''
        x, y = ChromeBrowserContext.middle_location(element1)
        pyautogui.moveTo(x, y)
        x, y = ChromeBrowserContext.middle_location(element2)
        pyautogui.dragTo(x, y, time)

在自己的测试过程中发现,selenium不支持动态修改header , 而且好像不能自定义header 进行请求。可以通过插件或者其他机制实现。

=selenium-wire 简单使用=

官网: https://github.com/wkeeling/selenium-wire 强烈建议直接使用这个库

​ selenium-wire 对selenium 做了扩展,让程序有能力拿到requests 对象,然后获取请求和响应信息,并且可以修改请求头(包括自定义的头)、修改请求参数(包括param和请求体中的JSON数据)、获取修改响应头等信息。

下面演示增加自己的header:(请求拦截器与响应拦截器用法)

from seleniumwire import webdriver


# 拦截request
def interceptor_request(request):
    del request.headers['mykey']  # Remember to delete the header first
    request.headers['mykey'] = 'mykey-value'  # Spoof the referer


# 拦截response
def interceptor_response(request, response):
    if request.url == 'http://localhost:8088/inner/t4':
        response.headers['New-Header'] = 'Some Value'


browser = webdriver.Chrome("chromedriver.exe")
browser.request_interceptor = interceptor_request
browser.response_interceptor = interceptor_response
browser.get("http://localhost:8088/inner/t4")

for request in browser.requests:
    if request.response:
        print(
            request.url,
            request.response.status_code,
            request.response.headers
        )

需要注意,进程结束之后browser 也是close 状态的,无法进行后续页面刷新或请求数据。

补充:

  1. selenium open新窗口或跳转页面无法定位元素
    因为窗口句柄还停留在上一个页面,所以导致无法定位元素。报错 “元素不可交互”。
    因此要解决的问题就是先定位到当前页面(也就是跳转后的页面)然后再进行元素的定位。
handles = driver.window_handles          #获取当前浏览器的所有窗口句柄
driver.switch_to.window(handles[-1])     #切换到最新打开的窗口
driver.switch_to.window(handles[-2])     #切换到倒数第二个打开的窗口
driver.switch_to.window(handles[0])      #切换到最开始打开的窗口
posted @ 2022-07-26 22:49  QiaoZhi  阅读(10098)  评论(0编辑  收藏  举报