目录
一、背景编辑
1.1、selenium的发展
1.2、在爬虫中的应用
1.3selenium执行原理图
1.4、WebDriver,与WebElement
二、准备编辑
2.1、下载驱动
2.2、安装Selenium库
2.3、简单使用
三、实用操作编辑
3.1、查找节点
3.1.1、查找元素在网页中的位置(网址为www.baidu.com,代码中的注释很详细)
3.1.2、实现方法
编辑
3.1.3、实现代码
3.2、获取节点属性,文本
3.3、节点交互
3.3.1、实现代码
3.3.2、实现效果编辑
3.3.3、常用交互操作
3.4、显示等待
3.4.1、等待页面元素
3.4.2、实现代码
3.4.3、等待条件
3.5、Cookie
3.5.3、实现效果编辑
3.6、截屏
3.6.1、WebDriver对象实现截屏方法
3.6.2、WebElement对象实现截屏方法
3.6.3、实现代码
3.6.4、实现效果编辑
3.7、反检测
3.7.1、适用场景
3.7.2、相关代码
3.8、设置无头
四、源码分析编辑
4.1、WebDriver的实例化源码探究
继承关系图
4.2、WebDrvierAPI原理探究
WebDrvierAPI在RemoteWebDrvier类的调用图
问题
解答
4.3、WebDriverWait的源码探究
问题
解答
总结
Selenium最初是由Jason Huggins测试工程师为减少手工测试量做的基于JavaScript语言的代码库(这时是通过js注入的方式操作),之后不断的发展完善并合并了WebDrive项目,使之可以直接调用浏览器和操作系统内置的方法,解决了JavaScript环境的沙盒限制,发展成了现在我们经常使用的Selenium WebDriver。现在的selenium就像百度百科说的一样,Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。
在爬取数据的时候,我们通常可以使用requests库,但是如果遇到加密的网站,就需要js逆向,相当复杂。但是我们还有一种方法,那就是使用自动化工具,实现可见即可爬,完成JavaScript动态渲染页面。
WebDriver的核心是是一个浏览器驱动程序,他负责启动浏览器,并将控制权交个程序,同时还提供了API,直接使用并控制浏览器的内置对象,可以使用它来模拟用户的行为,如点击提交,填写文本框,键入网址等。
WebElement表示 DOM 元素。 通常,所有与指定元素交互的操作都将通过此接口执行。如:如点击提交,填写文本框,键入网址,还包括获取文本属性等,这是不是很熟悉,原来WebElement属于WebDrvierAPI;它可以对指定网页元素进行操作。
总结
WebElement属于WebDriver API,通常使用于对指定可见元素进行操作,一般的实现如下:通过WebDriverAPI提供的方法(如:find_element())进行元素定位并返回WebElement对象,再调用此对象对指定元素进行操作。
查看浏览器版本,下载对应驱动,相关驱动的下载链接如下
Chrome驱动:chromedriver.storage.googleapis.com/index.html
Firefox驱动:Releases · mozilla/geckodriver (github.com)
Edge驱动:Microsoft Edge WebDriver - Microsoft Edge Developer
pip install selenium
from selenium import webdriver
import time
driver=webdriver.Chrome('./chromedriver.exe')#初始化WebDriver对象
driver.get('https://www.baidu.com')#访问百度
time.sleep(20)
Chrome的参数有很多,在这里我们初始化时传入的是executable_path,代表驱动的位置
驱动文件我放在了与代码同级目录下
方法 | 作用 | 参数说明 | |
find_element | 得到首个被匹配的节点 | By参数——By.ID通过标签ID属性匹配,By.CSS_SELECTOR,使用CSS选择器进行定位匹配,By.Name通过属性名进行匹配 Value参数——匹配的属性值 |
|
find_elements | 得到所有被匹配的节点,返回值为WebElement元素列表 | 同上 |
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('http://www.baidu.com')
#css选择器进行筛选
#如果有多个元素被匹配,find_element只会获取第一个
result=driver.find_element(by=By.CSS_SELECTOR,value='.mnav.c-font-normal.c-color-t')
print(result,'\n','---------------')
#匹配多个find_elements
result_list=driver.find_elements(by=By.CSS_SELECTOR,value='.mnav.c-font-normal.c-color-t')
for item in result_list:
print(item)
print('---------------')
result_byid=driver.find_element(by=By.NAME,value='tj_more')
print(result_byid)
---------------
---------------
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('http://www.baidu.com')
result=driver.find_element(by=By.CSS_SELECTOR,value='.mnav.c-font-normal.c-color-t')
print(result.text,result.get_attribute('href'),'\n','------------------------------------')
result_list=driver.find_elements(by=By.CSS_SELECTOR,value='.mnav.c-font-normal.c-color-t')
for item in result_list:
print(item.text,item.get_attribute('href'))
print('---------------------------------')
#webdriver只与可见元素交互,如果该元素被隐藏,将不会被获取文本值,但是属性仍可获取到,可通过is_displayed()进行查看
result_byid=driver.find_element(by=By.NAME,value='tj_more')
print({'text':result_byid.text,'href':result_byid.get_attribute('href')})
print(result_byid.is_displayed())
新闻 http://news.baidu.com/
------------------------------------
新闻 http://news.baidu.com/
hao123 https://www.hao123.com/?src=from_pc
地图 http://map.baidu.com/
贴吧 http://tieba.baidu.com/
视频 https://haokan.baidu.com/?sfrom=baidu-top
图片 http://image.baidu.com/
网盘 https://pan.baidu.com/?from=1026962h
---------------------------------
{'text': '', 'href': 'http://www.baidu.com/more/'}
False
from selenium import webdriver
import time
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('https://www.baidu.com')
web_element=driver.find_element(By.CSS_SELECTOR,'.s_ipt')
web_element.send_keys('张三')
driver.find_element(By.CSS_SELECTOR,'[id="su"]').click()
time.sleep(100)
操作 | 含义 |
click() | 点击按钮 |
sendkeys() | 输入到单选框 |
clear() | 清空输入框 |
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('http://www.baidu.com')
wait=WebDriverWait(driver,10)#传入webdriver对象,设置等待时间
#等待节点,返回值是WebElement
result_waitelement=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'.mnav.c-font-normal.c-color-t')))
#等待节点并判断某个节点是否满足条件,返回值是bool类型
result_waittext=wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME, 'mnav'), '新闻'))#等待节点包含的文本值
result_waitattribute=wait.until(EC.text_to_be_present_in_element_attribute((By.CLASS_NAME,'mnav'),'href','http://'))#等待节点的属性值
print(result_waitelement.text,result_waittext,result_waitattribute)
新闻 True True
含义 | 返回类型 | |
title_is | 判断title是否出现 | bool |
title_contains | 判断title页面标题是否包含某些字符 | bool |
url_contains | 判断当前url是否包含某个url | bool |
url_matches | 判断当前url是否符合某种格式 | bool |
presence_of_element_located | 判断某个元素是否被加载到了dom树里,但是并不代表这个元素可见 | WebElement |
presence_of_all_elements_located | 判断至少有一个元素存在于dom树中,返回所有定位到的元素 | List[WebElement] |
visibility_of_all_elements_located | 判断是否所有元素都在页面中可见 | bool |
visibility_of_element_located | 某个节点可见 | bool |
text_to_be_present_in_element | 判断指定的节点文本值中是否包含了预期的字符串 | bool |
text_to_be_present_in_element_value | 判断指定的节点值中是否包含了预期的字符串 | bool |
text_to_be_present_in_element_attribute | 判断指定节点值中指定属性是否包含预期字符串 | bool |
alert_is_present | 是否出现警告提示框 | bool |
element_to_be_clickable | 按 | bool |
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('https://login.taobao.com/member/login.jhtml')
time.sleep(40)#手动登录获取cookies
cookies=driver.get_cookies()
print(cookies,type(cookies))
with open('cookies.txt','w',encoding='utf-8') as f:
json.dump(cookies,f)
driver=webdriver.Chrome('chromedriver.exe')
driver.get('https://www.taobao.com/')
with open(r'./cookies.txt','r',encoding='utf-8') as f:
cookie_data=json.load(f)
for item in cookie_data:
print(item)
driver.add_cookie(item)
driver.get('https://www.taobao.com/')
time.sleep(30)
方法 | 作用 | 返回值 |
get_screenshot_as_png() | 获取当前窗口的屏幕截图作为二进制数据。 | byte |
get_screenshot_as_file() | 将当前窗口的屏幕截图保存到 PNG 图像文件。 | bool |
get_screenshot_as_base64 | 获取当前窗口的屏幕截图作为 base64 编码字符串 | String |
方法 | 作用 | 返回值 |
screenshot_as_png | 获取当前窗口的屏幕截图作为二进制数据。 | byte |
screenshot() | 将当前窗口的屏幕截图保存到 PNG 图像文件。 | bool |
screenshot_as_base64 | 获取当前窗口的屏幕截图作为 base64 编码字符串 | String |
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('https://www.baidu.com')
driver.get_screenshot_as_file('./screenshot_png/1.png')#默认存储位置在当前同级目录下
driver.find_element(By.ID,'lg').screenshot('./screenshot_png/2.png')#截取选定的节点图片
很多网站防止被恶意爬虫爬取,对selenium进行检测
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
option=ChromeOptions()
#设置无头模式
# option.add_argument('--headless')
#反屏蔽处理
#以开发者模式启动调试chrome,可以去掉提示受到自动软件控制,隐藏提示条
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension',False)#去掉提示以开发者模式调用,自动拓展信息
driver = webdriver.Chrome('chromedriver.exe',options=option)
# 将webdriver属性制为空
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',{
'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefined})'
})
from selenium import webdriver
from selenium.webdriver import ChromeOptions
#设置无头模式
option=ChromeOptions()
option.add_argument('--headless')
driver=webdriver.Chrome('chromedriver.exe',options=option)
driver.get('https://www.baidu.com')
driver.get_screenshot_as_png('baidu.png')
from selenium import webdriver
driver=webdriver.Chrome('./chromedriver.exe')
webdriver.Chrome()实例化WebDrvier的原理如下:
webdriver.py的部分源码
在这个文件中导入了WebDriver类并将WebDriver重命名,追溯WebDriver类可找到如下图的继承关系,WebDriver是继承了RemoteWebDriver类。
下图是webdriverAPI 在 RemoteWebDriver类的调用关系, RemoteWebDriver类的作用是向远程服务器发送命令。
该类初始化了服务器连接对象,并发送执行的标准命令,如源码所示标准命令
在selenium的原理执行图中我们可以知道,其实selenium底层是通过http协议来实现的,但是这些字符串是怎么转化成http请求的呢?
RemoteWebDriver类是调用了RemoteConnect类实现发送http请求的
如下图在RemoteConnect类中的调用关系,在_commands字典中定义了每个命令对应的http请求,而且还是restful风格的,关于restful风格可以参考这篇文章Restful风格及实践_杜小白也想的美的博客-CSDN博客
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
driver=webdriver.Chrome('./chromedriver.exe')
driver.get('http://www.baidu.com')
wait=WebDriverWait(driver,10)#传入webdriver对象,设置等待时间
#等待节点,返回值是WebElement
result_waitelement=wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'.mnav.c-font-normal.c-color-t')))
在上面的一段代码中,实现了对cass属性值为mnav c-font-normal c-color-t元素的等待,如果此节点出现在doc元素树中,将此节点返回封装成WebElement对象返回,那这些是如何实现的呢?
查看WebDriver部分源码
def __init__(
self,
driver,
timeout: float,
poll_frequency: float = POLL_FREQUENCY,
ignored_exceptions: typing.Optional[WaitExcTypes] = None,
):
:Args:
- driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
- timeout - Number of seconds before timing out
- poll_frequency - sleep interval between calls
By default, it is 0.5 second.
- ignored_exceptions - iterable structure of exception classes ignored during
self._driver = driver
self._timeout = float(timeout)
def until(self, method, message: str = ""):
"""Calls the method provided with the driver as an argument until the \
return value does not evaluate to ``False``.
:param method: callable(WebDriver)
:param message: optional message for :exc:`TimeoutException`
:returns: the result of the last call to `method`
:raises: :exc:`selenium.common.exceptions.TimeoutException` if timeout occurs
"""
screen = None
stacktrace = None
end_time = time.monotonic() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, "screen", None)
stacktrace = getattr(exc, "stacktrace", None)
time.sleep(self._poll)
if time.monotonic() > end_time:
break
raise TimeoutException(message, screen, stacktrace)
在until方法中的method(self._driver)实际上使用了闭包将
EC.presence_of_element_located((By.CSS_SELECTOR,'.mnav.c-font-normal.c-color-t'))作为内部方法返回,而且还调用了driver对象。
那么什么是EC.presence_of_element_located((By.CSS_SELECTOR,'.mnav.c-font-normal.c-color-t'))呢?调用的这个driver对象是干嘛的呢?
源码如下:
def presence_of_element_located(locator):
"""An expectation for checking that an element is present on the DOM
of a page. This does not necessarily mean that the element is visible.
locator - used to find the element
returns the WebElement once it is located
"""
def _predicate(driver):
return driver.find_element(*locator)
return _predicate
这个方法调用了driver.find_element()
我们在看下其他等待方法,如:text_to_be_present_in_element
源码如下
def text_to_be_present_in_element(locator, text_):
"""An expectation for checking if the given text is present in the
specified element.
locator, text
"""
def _predicate(driver):
try:
element_text = driver.find_element(*locator).text
return text_ in element_text
except StaleElementReferenceException:
return False
return _predicate
它也是调用的drvier.find_element方法,但返回的是bool类型
WaitDriver类实际上调用的是find_element等 WebDriverAPI方法并加入时间等待,一旦在设置的时间内没有找到这个指定元素并抛出超时错误。
本文仅用于学习交流!!!