本节继续进行selenium源码学习,上一节介绍了浏览器对应得模块,本节记录公共模块
目录
一、remote目录
1.remote_connection.py
这个模块主要包含其他浏览器remote模块继承类RemoteConnection
包含一个全局类变量_timeout,用于标记链接超时时间,使用得是python socket库中全局默认超时时间
_timeout = socket._GLOBAL_DEFAULT_TIMEOUT
关于这个类全局变量包含四个函数,其中函数中得cls表示Class,与self不同,self参数需要创建类对象后方可使用,cls则表示类本身,所以可以不用创建对象直接调用
@classmethod
def get_timeout(cls):
"""
:Returns:
Timeout value in seconds for all http requests made to the Remote Connection
"""
return None if cls._timeout == socket._GLOBAL_DEFAULT_TIMEOUT else cls._timeout
@classmethod
def set_timeout(cls, timeout):
"""
Override the default timeout
:Args:
- timeout - timeout value for http requests in seconds
"""
cls._timeout = timeout
@classmethod
def reset_timeout(cls):
"""
Reset the http request timeout to socket._GLOBAL_DEFAULT_TIMEOUT
"""
cls._timeout = socket._GLOBAL_DEFAULT_TIMEOUT
@classmethod
def get_remote_connection_headers(cls, parsed_url, keep_alive=False):
"""
Get headers for remote request.
:Args:
- parsed_url - The parsed url
- keep_alive (Boolean) - Is this a keep-alive connection (default: False)
"""
system = platform.system().lower()
if system == "darwin":
system = "mac"
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json;charset=UTF-8',
'User-Agent': 'selenium/{} (python {})'.format(__version__, system)
}
if parsed_url.username:
base64string = base64.b64encode('{0.username}:{0.password}'.format(parsed_url).encode())
headers.update({
'Authorization': 'Basic {}'.format(base64string.decode())
})
if keep_alive:
headers.update({
'Connection': 'keep-alive'
})
return headers
2.webdriver.py
包含WebDriver类也就是其他浏览器所继承的模块,对于该类的参数以及初始化讲解在selenium学习(二)中以及提到过,就不详细介绍了,主要介绍有哪些函数
(1)元素相关
首先就是搜索元素的方法,也就是八种类型对应每个类型两种的方法,一个返回一个,一个返回列表
def find_element_by_id(self, id_):
return self.find_element(by=By.ID, value=id_)
def find_elements_by_id(self, id_):
return self.find_elements(by=By.ID, value=id_)
(2)窗口相关
窗口相关的操作有尺寸、多窗口切换、刷新、前进后退等
例如:
# 执行当前窗口刷新操作,等于左上角刷新/F5
driver.refresh()
# 执行当前窗口的前进/后退操作,等于左上角的前进/后退
driver.forward()
driver.back()
# 执行窗口的切换,本质调用的都是driver.switch_to(),且用的是switch_to.py模块,后续介绍
driver.switch_to_alret()
driver.switch_to_window()
# 获取当前web对象包含的windows列表
driver.window_handles()
此时就可以通过连用,例如:
# 切换到第0个窗口
lists = driver.window_handles()
driver.switch_to_window(lists[0])
# 还有获取尺寸、放大、缩小等操作
driver.maximize_window()
driver.minimize_window()
driver.get_window_size(lists[0])
driver.set_window_size('111', '111')
(3) 截图相关
# 截图当前页面,并保存,save_screenshot本质调用的也是get_screenshot_as_file
driver.get_screenshot_as_file(filename)
driver.save_screenshot()
# 截图当前页面,并且转化为ascii
driver.get_screenshot_as_png()
driver.get_screenshot_as_base64()
(4) cookie相关
# 获取所有的cookie或者指定的cookie
driver.get_cookies()
driver.get_cookie(name)
# 添加对应的cookie
driver.add_cookie(cookie_dict)
# 删除所有cookie或者指定cookie
driver.delete_all_cookies()
driver.delete_cookie(name)
3.switch_to.py
包含SwitchTo类,主要作用是切换浏览器下的各个窗口
@property
def alert(self):
alert = Alert(self._driver)
alert.text
return alert
def default_content(self):
self._driver.execute(Command.SWITCH_TO_FRAME, {'id': None})
def frame(self, frame_reference):
if isinstance(frame_reference, basestring) and self._driver.w3c:
try:
frame_reference = self._driver.find_element(By.ID, frame_reference)
except NoSuchElementException:
try:
frame_reference = self._driver.find_element(By.NAME, frame_reference)
except NoSuchElementException:
raise NoSuchFrameException(frame_reference)
self._driver.execute(Command.SWITCH_TO_FRAME, {'id': frame_reference})
def parent_frame(self):
self._driver.execute(Command.SWITCH_TO_PARENT_FRAME)
def window(self, window_name):
if self._driver.w3c:
self._w3c_window(window_name)
return
data = {'name': window_name}
self._driver.execute(Command.SWITCH_TO_WINDOW, data)
4.webelement.py
当你获取到一个元素时返回的就是WebElement这个对象,主要对这个对象进行一系列的操作获取爬取对应的类型,介绍常用的
# 进行元素点击
element.click()
# 判断元素是否被选中,主要用于复选框/勾选框等
element.is_selected()
# 判断元素是否启用
element.is_enabled()
# 返回元素的li tag列表,一般常用于ul下的li遍历,同样也包含八种16个函数
element.find_elements_by_tag_name('li')
# 对元素进行输入操作,一般用户input标签
element.send_keys('input')
# 获取元素的属性一般有以下两种方法
# 区别在于get_attribute一般用于获取编写html时输入的属性
# get_property一般用于获取编写html加载时DOM树生成的属性
# 不过目前基本大部分都是交合的,所以都可以使用
element.get_property('input')
element.get_attribute('input')
二、common目录
介绍部分常用模块
1.alert.py
Alert模块,控制浏览器的弹窗,初始化只需传入web对象
常用的有一个text属性,获取弹窗信息
@property
def text(self):
"""
Gets the text of the Alert.
"""
if self.driver.w3c:
return self.driver.execute(Command.W3C_GET_ALERT_TEXT)["value"]
else:
return self.driver.execute(Command.GET_ALERT_TEXT)["value"]
还有弹窗的接受accept以及取消dismiss,如果有输入框的弹窗还有send_keys函数
def accept(self):
"""
Accepts the alert available.
Usage::
Alert(driver).accept() # Confirm a alert dialog.
"""
if self.driver.w3c:
self.driver.execute(Command.W3C_ACCEPT_ALERT)
else:
self.driver.execute(Command.ACCEPT_ALERT)
2.action_chains.py
包含类ActionChains主要用户实现元素的操作,初始化函数如下
def __init__(self, driver):
"""
Creates a new ActionChains.
:Args:
- driver: The WebDriver instance which performs user actions.
"""
self._driver = driver
self._actions = []
if self._driver.w3c:
self.w3c_actions = ActionBuilder(driver)
perform函数表示执行所有的存储操作,后续举例时介绍
reset_actions函数则表示清空所有存储的操作
click函数表示执行点击
click_and_hold表示长按
等等各类鼠标操作,这里就不一一举例了,可以自行查看
例如:
ActionChains(driver).move_to_element(el).click().pause(1).send_keys('input').pause(1).perform()
# 表示先移动到el元素然后点击,等待一秒后输入input再等待一秒,这是存储的所有操作
# 最后添加perform()则进行存储释放,执行上述描述的步骤
3.By.py
By类,主要用户元素爬取时对应的方法,也就是我们常说的八种爬取类型,使用时直接By.ID即可
class By(object):
"""
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"
4.Key.py
Key类,主要存放键盘特殊的输入,使用时直接调用即可
class Keys(object):
"""
Set of special keys codes.
"""
NULL = '\ue000'
CANCEL = '\ue001' # ^break
HELP = '\ue002'
BACKSPACE = '\ue003'
BACK_SPACE = BACKSPACE
例如:
ActionChains(driver).move_to_element(el).send_keys(By.ENTER).perform()
# 表示先移动到el元素然后输入回车
三、support目录
介绍部分常用模块
1.abstract_event_listener.py
该模块只包含一个类AbstractEventListener,主要用于事件侦听器,但是类中所有得方法均未实现,所以需要进行继承后挑选需要得部分进行实现
class AbstractEventListener(object):
"""
Event listener must subclass and implement this fully or partially
"""
def before_navigate_to(self, url, driver):
pass
def after_navigate_to(self, url, driver):
pass
def before_navigate_back(self, driver):
pass
def after_navigate_back(self, driver):
pass
2.color.py
该模块下包含一个Color类、匹配各类颜色字符得正则以及一个Colors字典,存储常用得颜色类型
用于显示颜色。输入一定得可解析得文本,例如‘#00ff33’/rgb(0, 255, 51)/'blue'
正则匹配分为RGB/RGBA/HEX/HEX3/HSL/HSLA
RGB_PATTERN = r"^\s*rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)\s*$"
RGB_PCT_PATTERN = r"^\s*rgb\(\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*\)\s*$"
RGBA_PATTERN = r"^\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0|1|0\.\d+)\s*\)\s*$"
RGBA_PCT_PATTERN = r"^\s*rgba\(\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(\d{1,3}|\d{1,2}\.\d+)%\s*,\s*(0|1|0\.\d+)\s*\)\s*$"
HEX_PATTERN = r"#([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})"
HEX3_PATTERN = r"#([A-Fa-f0-9])([A-Fa-f0-9])([A-Fa-f0-9])"
HSL_PATTERN = r"^\s*hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)\s*$"
HSLA_PATTERN = r"^\s*hsla\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*(0|1|0\.\d+)\s*\)\s*$"
Color类中,初始化一般接受四个函数,r,g,b,a
def __init__(self, red, green, blue, alpha=1):
self.red = int(red)
self.green = int(green)
self.blue = int(blue)
self.alpha = "1" if float(alpha) == 1 else str(float(alpha) or 0)
主要使用得是静态form_string(str_)方法(匹配得过长,可自行前往源码查看)
@staticmethod
def from_string(str_):
import re
class Matcher(object):
def __init__(self):
self.match_obj = None
def match(self, pattern, str_):
self.match_obj = re.match(pattern, str_)
return self.match_obj
@property
def groups(self):
return () if self.match_obj is None else self.match_obj.groups()
m = Matcher()
例如:调用Color.from_string("rgba(11, 22, 33, 0.1)").rgba,此时开始执行匹配机制
执行上面列出来得所有匹配,当匹配返回不为空时再执行对应返回,返回得均为Color得对象
if m.match(RGB_PATTERN, str_):
return Color(*m.groups)
elif m.match(RGB_PCT_PATTERN, str_):
rgb = tuple([float(each) / 100 * 255 for each in m.groups])
return Color(*rgb)
elif m.match(RGBA_PATTERN, str_):
return Color(*m.groups)
elif m.match(RGBA_PCT_PATTERN, str_):
rgba = tuple([float(each) / 100 * 255 for each in m.groups[:3]] + [m.groups[3]])
return Color(*rgba)
elif m.match(HEX_PATTERN, str_):
rgb = tuple([int(each, 16) for each in m.groups])
return Color(*rgb)
elif m.match(HEX3_PATTERN, str_):
rgb = tuple([int(each * 2, 16) for each in m.groups])
return Color(*rgb)
elif m.match(HSL_PATTERN, str_) or m.match(HSLA_PATTERN, str_):
return Color._from_hsl(*m.groups)
elif str_.upper() in Colors.keys():
return Colors[str_.upper()]
else:
raise ValueError("Could not convert %s into color" % str_)
此时后缀.rgba则表示该对象得rgba属性,包含如下三种
@property
def rgb(self):
return "rgb(%d, %d, %d)" % (self.red, self.green, self.blue)
@property
def rgba(self):
return "rgba(%d, %d, %d, %s)" % (self.red, self.green, self.blue, self.alpha)
@property
def hex(self):
return "#%02x%02x%02x" % (self.red, self.green, self.blue)
最后会打印如下
from selenium.webdriver.support.color import Color
print(Color.from_string("rgba(11, 22, 33, 0.1)").rgba)
>rgba(11, 22, 33, 0.1)
3.expected_conditions.py
该模块主要用于判断,例如元素是否可点击,页面标题是否为需求字符等等类型
(1)title相关
title_is用于判断当前页面得title,也就是html中head里面得title,返回self.title == driver.title
例如:
import selenium
from selenium.webdriver.support import expected_conditions
web_driver = selenium.webdriver.Chrome()
web_driver.get("https://mp.csdn.net/")
print(expected_conditions.title_is('首页-CSDN博客')(web_driver))
>True
类似还有一个title_contains,不过返回的是self.title in driver.title
(2)url相关
url_to_be用于判断当前页面得url是否与输入得一致,返回self.url == driver.current_url
url_changes则与之相反,判断是否是不一致
url_matches则匹配浏览器得url中是否包含输入得正则,返回match is not None
url_contains则判断是否包含输入字符
例如:
import selenium
from selenium.webdriver.support import expected_conditions
web_driver = selenium.webdriver.Chrome()
web_driver.get("https://mp.csdn.net/")
print(expected_conditions.url_to_be("https://mp.csdn.net/")(web_driver))
# 这里是False是因为CSDN没有登录得情况下会默认跳转到登陆页面,所以url不为输入得
>False
(3)元素相关
剩余就不一一举例了,与元素相关得如果找到元素就返回元素,否则就返回false
"""
方法 描述
title_is(‘百度一下’) 判断当前页面的 title 是否等于预期
title_contains(‘百度’) 判断当前页面的 title 是否包含预期字符串
presence_of_element_located(locator) 判断元素是否被加到了 dom 树里,并不代表该元素一定可见
visibility_of_element_located(locator) 判断元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0
text_to_be_present_in_element_attribute(locator,attribute,text)
visibility_of(element) 跟上一个方法作用相同,但传入参数为 element
text_to_be_present_in_element(locator , ‘百度’) 判断元素中的 text 是否包含了预期的字符串
text_to_be_present_in_element_value(locator , ‘某值’) 判断元素中的 value 属性是否包含了预期的字符串
frame_to_be_available_and_switch_to_it(locator) 判断该 frame 是否可以 switch 进去,True 则 switch 进去,反之 False
invisibility_of_element_located(locator) 判断元素中是否不存在于 dom 树或不可见
element_to_be_clickable(locator) 判断元素中是否可见并且是可点击的
staleness_of(element) 等待元素从 dom 树中移除
element_to_be_selected(element) 判断元素是否被选中,一般用在下拉列表
element_selection_state_to_be(element, True) 判断元素的选中状态是否符合预期,参数 element,第二个参数为 True/False
element_located_selection_state_to_be(locator, True) 跟上一个方法作用相同,但传入参数为 locator
alert_is_present() 判断页面上是否存在 alert
"""
4.wait.py
包含WebDriverWait类,主要用于web操作的等待判断,包含两个函数until以及until_not,一个表示内容是否存在,一个表示内容是否不存在初始化函数如下,接受参数
driver:操控的web对象
timeout:等待时间
poll_frequency:轮询间隔时间,默认为0.5
ignored_exceptions:忽略的错误类型,接受类型为元组,碰到该错误时提示为Timeout错误,也就是超时未检测到,
默认忽略NoSuchElementException
def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
self._driver = driver
self._timeout = timeout
self._poll = poll_frequency
# avoid the divide by zero
if self._poll == 0:
self._poll = POLL_FREQUENCY
exceptions = list(IGNORED_EXCEPTIONS)
if ignored_exceptions is not None:
try:
exceptions.extend(iter(ignored_exceptions))
except TypeError: # ignored_exceptions is not iterable
exceptions.append(ignored_exceptions)
self._ignored_exceptions = tuple(exceptions)
until函数
def until(self, method, message=''):
"""Calls the method provided with the driver as an argument until the \
return value is not False."""
screen = None
stacktrace = None
end_time = time.time() + 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.time() > end_time:
break
raise TimeoutException(message, screen, stacktrace)
举例:
WebDriverWait(driver, 20, 2).until(expected_conditions.alert_is_present())
表示在20s内每隔2s进行一次轮询,直到出现弹窗则结束,20s未出现弹窗则提示TimeoutException