Selenium库详解

  这个库是一个自动化测试工具,他可以用来模拟浏览器来完成点击,搜索,下拉等操作指令,可以模拟Firefox、Chrome、Safari等等包括一些安卓的浏览器,主要用来解决像urllib、request等无法做的JavaScript渲染问题。
Selenium库安装

pip install selenium

  具体方法参见前文。

用法详解

基本使用

  先看一下基本的用法,代码量有点大,我们慢慢分析:

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

browser = webdriver.Chrome()
try:
    browser.get('https://www.baidu.com')
    input = browser.find_element_by_id('kw')
    input.send_keys('python')
    input.send_keys(Keys.ENTER)
    wait = WebDriverWait(browser, 10)
    wait.until(EC.presence_of_element_located((By.ID,'content_left')))
    print(browser.current_url)
    print(browser.get_cookies())
    print(browser.page_source)
finally:
    browser.close()

  上面这个实例比较全面,先from声明了这个selenium然后用webdriver后面跟个点接浏览器的名称,之后再try里面用get来打开浏览器的链接,传入了百度的网址,查找名字叫做“kw”的元素,然后把它当成复制成input,晚了之后调用send_keys把内容传入到这个元素里面去。再send_keys敲入回车。再用wait等待,等待这个搜索的时间为10秒钟,然后等待一个id是content_left的元素加载完毕之后打印url、打印cookies、打印网页源代码,最后再关闭浏览器。
  举一反三,我们看看谷歌的,先分析谷歌搜索框的id或者class是什么:
ayjg.png
  点击之后看到谷歌浏览器这里的搜索框是class为gLFyf的值,然后在搜索一下看看搜索完之后的内容被定义成了什么样的div:
aIvO.png
  可以看到这个搜索结果可以是class为med的也可以是id为search的,那么我们不管选择什么要等待这个参数的其中一个加载完毕再打印那些内容,所以只需将刚才的代码修改一下,就可以是谷歌的形势了。

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

browser = webdriver.Chrome()
try:
    browser.get('https://www.google.com')
    input = browser.find_element_by_class_name('gLFyf')
    input.send_keys('python')
    input.send_keys(Keys.ENTER)
    wait = WebDriverWait(browser, 10)
    wait.until(EC.presence_of_element_located((By.ID,'search')))
    print(browser.current_url)
    print(browser.get_cookies())
    print(browser.page_source)
finally:
    browser.close()

  下面是运行结果
  adyT.png

声明浏览器对象

  下面列举一些可以调用的浏览器,前提是你要安装浏览器和它对应版本的驱动哟:

from selenium import webdriver

browser = webdriver.Chrome()
browser = webdriver.Edge()
browser = webdriver.Firefox()
browser = webdriver.Safari()
browser = webdriver.PhantomJS()

访问页面

  现在以打开淘宝然后关闭为例子:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
print(browser.page_source)
browser.close()

  上面的操作先是调用了webdriver然后get了淘宝的网站,打印了淘宝的源代码,最后关闭浏览器。
aiq1.png

查找元素

单个元素

  先看看单个元素的查找方法:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
first = browser.find_element_by_id('q')
second = browser.find_element_by_css_selector('#q')
third = browser.find_element_by_xpath('//*[@id="q"]')
print(first, second, third)
browser.close()

  上面这个方法就是查找了id为q的标签,并打印了出来,三个写法都是一样的结果,一个是直接用id,第二个是class选择器,第三个就是xpath。最后可以看到打印结果完全一致:
an9p.png
  下面列举一些常见的方法:

  • find_element_by_id
  • find_element_by_name
  • find_element_by_xpath
  • find_element_by_link_text
  • find_element_by_partial_link_text
  • find_element_by_tag_name
  • find_element_by_class_name
  • find_element_by_css_selector
      还有一种通用的方法:
from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
first = browser.find_element(By.ID, 'q')
print(first)
browser.close()

  这种方法就是在一开始调用了一个通用的参数commom.by,而后在后面的时候也是要用回来就可以了,打印结果是一样的:
aD79.png

多个元素

  我们看看多个元素的写法,原理是一样的,只是多加了个s

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
lis = browser.find_elements_by_css_selector('.service li')
print(lis)
browser.close()

  现在就是打印多个元素出来:
ahXN.png
  还有一种写法跟刚才单个元素类似:

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

browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
first = browser.find_elements(By.ID, 'q')
print(first)
browser.close()

  也就是加上了个s,打印结果是一样的。那么同理也有很多的多个元素方法:

  • find_elements_by_id
  • find_elements_by_name
  • find_elements_by_xpath
  • find_elements_by_link_text
  • find_elements_by_partial_link_text
  • find_elements_by_tag_name
  • find_elements_by_class_name
  • find_elements_by_css_selector
    元素交互操作

  现在就是当我们把这些元素获取完之后的交互操作问题了。

from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
input = browser.find_element_by_id('q')
input.send_keys('iphone')
time.sleep(1)
input.clear()
input.send_keys('ipad')
button = browser.find_element_by_class_name('btn-search')
button.click()

  上面演示代码的意思就是在淘宝找到id为q的位置然后输入iPhone之后等待1秒再清空,然后输入ipad,之后找到class为btn-search点击click()跳转过去,更多的元素交互操作api点我了解更多

交互动作

  将动作附加到动作链中串行执行

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
source = browser.find_element_by_css_selector('#draggable')
target = browser.find_element_by_css_selector('#droppable')
actions = ActionChains(browser)
actions.drag_and_drop(source, target)
actions.perform()

  上面例子里面调用了ActionChains,首先传入一个url链接,然后switch_to.frame方法就是说如果你的源码里面有frame那么就可以用switch_to.frame来调用,调用方法有三种,比如下面这段是源码:

<html lang="en">
<head>
    <title>FrameTest</title>
</head>
<body>
<iframe src="a.html" id="frame1" name="myframe"></iframe>
</body>
</html>

  我们想要定位到iframe里面就可以用下面的三种方式:

from selenium import webdriver
driver = webdriver.Firefox()
driver.switch_to.frame(0)  # 1.用frame的index来定位,第一个是0
# driver.switch_to.frame("frame1")  # 2.用id来定位
# driver.switch_to.frame("myframe")  # 3.用name来定位
# driver.switch_to.frame(driver.find_element_by_tag_name("iframe"))  # 4.用WebElement对象来定位

  所以其实我们刚才的那段源码也可以将“browser.switch_to.frame('iframeResult')”替换成“browser.switch_to.frame(0)”,运行结果是一样的,因为在源码中只有一段frame,如果有多段那么就要看index的定位了,数它是第几个。继续回到刚才这里的source和target就查找到了两个元素,一个是初始位置,一个是最终的位置,如图:
amz7.png
  刚才的代码里面第十行的意思就是开始用鼠标啦,ActionChains可以完成鼠标的所有操作。而drag_and_drop的意思就是移动某个元素,可以到另一个元素,也可以使用drag_and_drop_by_offset()到指定的坐标点,注意:拖拽使用时注意加等待时间,有时会因为速度太快而失败。最后perform()的意思就是执行链中的所有动作的。
  有了上面的学习,我们也可以手动改改代码,做一个自动登录淘宝的小程序了:

from selenium import webdriver
from selenium.webdriver import ActionChains
from time import sleep
browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
input = browser.find_element_by_class_name('h')
input.click()
login = browser.find_element_by_id('fm-login-id')
login.send_keys('输入你的账号')
sleep(2)
passwd = browser.find_element_by_id('fm-login-password')
passwd.send_keys('输入你的密码')
sleep(2)
source = browser.find_element_by_id('nc_1_n1z')
action = ActionChains(browser)
action.drag_and_drop_by_offset(source, 258, 258).perform()
sleep(2)
loging = browser.find_element_by_class_name('password-login')
loging.click()
browser.close()

  其他的一些方法点我查看

  • click(on_element=None) ——单击鼠标左键
  • click_and_hold(on_element=None) ——点击鼠标左键,不松开
  • context_click(on_element=None) ——点击鼠标右键
  • double_click(on_element=None) ——双击鼠标左键
  • drag_and_drop(source, target) ——拖拽到某个元素然后松开
  • drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某个坐标然后松开
  • key_down(value, element=None) ——按下某个键盘上的键
  • key_up(value, element=None) ——松开某个键
  • move_by_offset(xoffset, yoffset) ——鼠标从当前位置移动到某个坐标
  • move_to_element(to_element) ——鼠标移动到某个元素
  • move_to_element_with_offset(to_element, xoffset, yoffset) ——移动到距某个元素(左上角坐标)多少距离的位置
  • perform() ——执行链中的所有动作
  • release(on_element=None) ——在某个元素位置松开鼠标左键
  • send_keys(*keys_to_send) ——发送某个键到当前焦点的元素
  • send_keys_to_element(element, *keys_to_send) ——发送某个键到指定元素

执行JavaScript

  现在我们用JavaScript的看看:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')

  上面代码就是用JavaScript下拉到最底部的操作。

获取元素信息

获取属性

  现在把本站的logo信息和属性打印出来看看:

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'https://www.yuanpeng666.top'
browser.get(url)
logo = browser.find_element_by_class_name('navbar-brand')
print(logo)
print(logo.get_attribute('class'))

  可以给大家看一下运行结果,就是显示了网站的logo信息:
atC5.png

获取文本值

  现在看看怎么获取到文本内容。

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'https://www.yuanpeng666.top'
browser.get(url)
logo = browser.find_element_by_class_name('post-meta')
print(logo.text)

  现在做的是获取文本值,获取的是文章的一些信息:
aFDF.png

获取ID、位置、标签名、大小

  看看下面的例子吧:

from selenium import webdriver

browser = webdriver.Chrome()
url = 'https://www.yuanpeng666.top'
browser.get(url)
input = browser.find_element_by_class_name('app-header')
print(input.id)
print(input.location)
print(input.tag_name)
print(input.size)
browser.close()

  上面这里就说明了本站中clas为app-header处的id信息和在浏览器的位置,还有tag名称和大小。

Frame

  我们还是以下面这个网站为例
aXjJ.png
  如果你在frame里面取了东西,那么你想要去拿frame外面的内容是不行的,会报错,要层层切开,示例代码:

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

browser = webdriver.Chrome()
url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame(0)
source = browser.find_element_by_css_selector('#draggable')
print(source)
try:
    logo = browser.find_element_by_class_name('navbar-header')
except NoSuchElementException:
    print('No logo')
browser.switch_to.parent_frame()
logo = browser.find_element_by_class_name('navbar-header')
print(logo)
print(logo.text)

  可以看到这个运行结果:
aMvA.png
  这里面就是有一个browser.switch_to.parent_frame()这里就是回退到了frame外面了。其它的操作基本之前都学过。

等待

隐式等待

  隐式等待的意思就是说在做一些ajax请求的时候我们需要等待某些元素完全加载完毕再爬取,隐式等待设置一个时间,如果在设置的时间里元素加载好了,那么就进行下一步,如果在设置的时间里没有加载好,超时了还没加载好,就会直接抛出异常。

from selenium import webdriver

browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('http://yuanpeng666.top')
input = browser.find_element_by_class_name('blog-post')
print(input)

  这里就不演示了,10是能加载文章列表的,可以将数字改小,然后试试国外的网站,就会报错。

显示等待

  指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常。只有该条件触发,才执行后续代码,这个使用更灵活。比如我们可以说加载某个网站,让他的搜索框出来了,让他可以点击了再执行其他的操作:

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

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
wait = WebDriverWait(browser, 10)
input = wait.until(EC.presence_of_all_elements_located((By.ID, 'q')))
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search')))
print(input, button)

  上面这个代码就是打开淘宝,然后调用wait方法等待10秒,给两个值,一个是id为q的定义为input,还有一个是css为btn-search的,等这两个元素加载出来了直接打印,也就是说搜索框和搜索按钮,加载好之后再执行下一步。
  另外这个方法还有很多的调用条件:

  • title_is:判断当前页面的title是否等于预期
  • title_contains:判断当前页面的title是否包含预期字符串
  • presence_of_element_located:判断某个元素是否被加到了dom树里,并不代表该元素一定可见
  • visibility_of_element_located:判断某个元素是否可见. 可见代表元素非隐藏,并且元素的宽和高都不等于0
  • visibility_of:跟上面的方法做一样的事情,只是上面的方法要传入locator,这个方法直接传定位到的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进去,如果可以的话,返回True并且switch进去,否则返回False
  • invisibility_of_element_located:判断某个元素中是否不存在于dom树或不可见
  • element_to_be_clickable - it is Displayed and Enabled:判断某个元素中是否可见并且是enable的,这样的话才叫clickable
  • staleness_of:等某个元素从dom树中移除,注意,这个方法也是返回True或False
  • element_to_be_selected:判断某个元素是否被选中了,一般用在下拉列表
  • element_located_to_be_selected
  • element_selection_state_to_be:判断某个元素的选中状态是否符合预期
  • element_located_selection_state_to_be:跟上面的方法作用一样,只是上面的方法传入定位到的element,而这个方法传入locator
  • alert_is_present:判断页面上是否存在alert

  现在看看怎么操作网站的前进和后退:

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.get('http://www.yuanpeng666.top')
browser.get('http://www.zhihu.com')
browser.back()
time.sleep(5)
browser.forward()
browser.close()

  这里的操作就是先打开这三个页面,然后先返回到本站,再前进到知乎,最后关闭

Cookies

  现在看看cookies怎么操作:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.zhihu.com/explore')
print(browser.get_cookies())
browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
browser.close()

  这里就是先把原来的cookies打印出来,然后再把我们传入进去的cookies打印出来,最后再把所有cookies删除掉,最后再打印出来。

选项卡管理

  现在看看选项卡怎么操作:

import time
from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.baidu.com')
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to_window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(1)
browser.switch_to_window(browser.window_handles[0])
browser.get('http://www.python.org')

  这里有两个选项卡,打开百度是默认的第一个,然后打开了第二个也就是window_handles[1]这里打开的是淘宝,之后我们切换到第一个选项卡window_handles[0],在这里打开python的官网。

异常处理:

  在实际中异常处理是很麻烦的, 到时候要不停的百度谷歌,举一个简单地例子,如果没有元素会宝什么样的错误:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.baidu.com')
browser.find_element_by_id('hello')

  上面这个代码运行时就会报错:
aNIh.png
  如果我们把这个异常写进去就会打印一个错误信息:

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException

browser = webdriver.Chrome()
try:
    browser.get('http://www.baidu.com')
except TimeoutException:
    print('Time out')
try:
    browser.find_element_by_id('hello')
except NoSuchElementException:
    print('no hello')
finally:
    browser.close()

  这样就不会报错,会打印出来没有hello。
  遇到问题多多查文档~~~

最后修改:2020 年 06 月 10 日 03 : 13 PM
请俺喝杯咖啡呗