Beijing: ☁️ 🌡️+18°C 🌬️↓4km/h

写在前面

日常工作中,在使用网页爬虫抓取网页数据受到限制时,我们可能考虑会对一些无需登录验证的网页,使用模拟浏览器点击,例如获取网页的检索结果、批量下载文件等,Selenium 模块提供 API 调用 webdriver,可以在一定程度上满足我们的需求,但同时需要下载设置路径等操作,因此还需要配合 webdrvier Manager 才可以对 webdriver 进行便捷化管理。

通过 pip install selenium webdriver-manager 完成相关 Python 库的安装。

driver 安装

Chrome 安装

from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install())

Firefox 安装

from selenium import webdriver
from webdriver_manager.firefox import GeckoDriverManager
from selenium.webdriver.firefox.service import Service

ser = Service(GeckoDriverManager().install())
driver = webdriver.Firefox(service=ser)

Edge 安装

from selenium import webdriver
from webdriver_manager.microsoft import EdgeChromiumDriverManager

driver = webdriver.Edge(EdgeChromiumDriverManager().install())

在多次尝试使用过程中,Firefox体验相对较好。

基本使用

获取 driver 后,我们需要使用 driver.get(url) 使得浏览器访问我们需要的页面,结束时使用 driver.close() 关闭即可。

定位元素

在对网页的元素定位中,最常使用的是 xpath,因为我们只要确定浏览器网页的操作顺序,进入浏览器开发者模式,复制对应的页面元素 xpath 路径即可,例如:

# 通过xpath定位并点击元素
driver.find_element('xpath', '//*[@id="exportToExcelButton"]').click()

页面交互

有时候我们在选择对应的页面元素后,还需要填写相应的内容或敲击对应的键盘按键,这里使用比较直接的键盘输入方法:

from selenium.webdriver.common.keys import Keys

# 找到对应元素并敲击对应按键
start = driver.find_element('name', 'markFrom')
start.send_keys(Keys.BACKSPACE)

# 填写对应的文本内容
driver.find_element('name', 'markFrom').send_keys('123456')

时间等待

当今许多页面加载中使用 AJAX 技术,当网页在浏览器中载入时,其中的许多元素并非是同一时间段进行加载,因此需要经过等待,slenium 提供了 Explicit Waits 以及 Implicit Waits 两种方式。

显式等待(Explicit Waits)

代码在执行前必须满足某一条件,否则将抛出异常。下面这段代码表示给予 driver 最多 10 秒时间去定位 ID 为 myDynamicElement 的元素(PS:即使网页其它元素未加载完成也会继续执行下一步),如果 10 秒内未能定位到,则抛出异常。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.ID, "myDynamicElement"))
    )
finally:
    driver.quit()

隐式等待(Implicit Waits)

是的 driver 作用于全局,webdriver 被要求等待一段特定的时间直至整个页面加载完成,然后进行下一步操作。

from selenium import webdriver
from webdriver_manager.firefox import GeckoDriverManager
from selenium.webdriver.firefox.service import Service

ser=Service(GeckoDriverManager().install())
driver = webdriver.Firefox(service=ser)

driver.implicitly_wait(1) # seconds
driver.get("https://www.baidu.com/")

driver.find_element('xpath', '/html/body/div[1]/div[1]/div[5]/div/div/div[3]/div/a[2]/span').click()
driver.quit()

多进程

通常我们会有同时打开多个浏览器窗口的需求,这里我们结合 Python multiprocessing 模块进行演示:

import time
import os
from multiprocessing import Pool
from selenium import webdriver
from webdriver_manager.firefox import GeckoDriverManager
from selenium.webdriver.firefox.service import Service

def browser():
    driver = webdriver.Chrome()
    return driver

def task(url):
    driver = browser()
    driver.get(url)
    time.sleep(3)
    driver.close()

if __name__ =='__main__':
    links = ['https://www.baidu.com', 'https://bilibili.com']
    print('Parent process %s.' % os.getpid())
    pool = Pool(processes=4)
   
    for i in range(len(links)):
        pool.apply_async(task, args={links[i]})

    print('Waiting for all subprocesses done...')
    pool.close()
    pool.join()
    print('All subprocesses done.')

参考文献

[1] Se Document

[2] Easily Manage Install Webdrivers for Selenium

[3] What are the differences between implicit and explicit waits in Selenium with python?

[4] 廖雪峰 Python 教程——多进程

[5] How To use multiprocess pool With Python Selenium