Selenium 是一个自动化测试工具,它主要用于自动化网络应用程序的测试。不过,除了测试之外,它也常用于自动执行各种浏览器操作,比如自动填写表单、抓取网页数据、点击、下拉等。同时还可以获取浏览器当前所呈现的页面的源代码,做到可见即可爬,对于一些JavaScript动态渲染的页面来说,非常有效
谷歌浏览器为例,在开始之前确保已经安装好了浏览器并配置好了ChromeDriver,另外还需要安装python第三方selenium库。
1 安装库
pip install selenium
2 安装驱动
官网:http://chromedriver.storage.googleapis.com/index.html
注意:
驱动要对应浏览器版本,否则无法驱动
禁止浏览器更新services.msc找到给禁止
安装细节:
找到类似的版本的驱动
找一个下载win版本的然后解压,安装
将驱动的.exe文件放在python的安装目录下(或者把路径给配置到环境变量中,可以通过cmd输入chromedriver运行)
禁止更新服务:
win+R - services.msc找到下面的给禁止
如果版本超过114,进入下面的连接下载对应版本(下载win32)
Chrome for Testing availabilityhttps://googlechromelabs.github.io/chrome-for-testing/
Selenium支持众多的浏览器,如Chrome、Firefox、Edge等,还有Android、BlackBerry等手机端的浏览器。另外,也支持无界面的浏览器PhantomJS
初始化方式如下:
from selenium import webdriver
browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.Safari()
打开一个网站然后关闭,具体代码如下
from selenium import webdriver
import time
# 创建一个浏览器对象
browser = webdriver.Chrome()
# 打开指定网页
browser.get('https://www.baidu.com/')
# 停留5秒
time.sleep(5)
# 退出浏览器
browser.quit()
用浏览器搜索指定内容
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 提前添加配置信息
options = webdriver.ChromeOptions()
# 设置浏览器不被检测
options.add_argument('--disable-blink-features=AutomationControlled')
# 创建一个浏览器对象
browser = webdriver.Chrome()
# browser = webdriver.Chrome(options = options)
# 打开指定网页
browser.get('https://www.baidu.com/')
# 搜索中国武警
# 定位
browser.find_element(By.ID, 'kw').send_keys('中国武警')
# 点击搜索
browser.find_element(By.ID,'su').click()
# 停留5秒
time.sleep(5)
# 退出浏览器
browser.quit()
(可能会检测出来是非人为操作,通过提前配置浏览器参数解决)
设置屏蔽
options = webdriver.ChromeOptions()
# 设置浏览器不被检测
options.add_argument('--disable-blink-features=AutomationControlled')
# 创建一个浏览器对象
browser = webdriver.Chrome(options = options)
模拟键盘操作
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys #模拟键盘操作
import time
# 提前添加配置信息
options = webdriver.ChromeOptions()
# 设置浏览器不被检测
options.add_argument('--disable-blink-features=AutomationControlled')
# 创建一个浏览器对象
browser = webdriver.Chrome()
# 打开指定网页
browser.get('https://www.baidu.com/')
# 搜索中国武警
# 定位
s = browser.find_element(By.ID, 'kw')
s.send_keys('中国武警')
# 回车搜索
s.send_keys(Keys.ENTER)
time.sleep(10)
find_element()
# 通过 ID 定位元素
find_element(By.ID, "element_id")
# 通过类名定位元素
find_element(By.CLASS_NAME, "element_class")
# 通过名字定位元素
find_element(By.NAME, "element_name")
# 通过 CSS 选择器定位元素
find_element(By.CSS_SELECTOR, "css_selector")
# 通过 XPath 定位元素
find_element(By.XPATH, "xpath_expression")
# 通过链接文本定位超链接
find_element(By.LINK_TEXT, "link_text")
# 通过部分链接文本定位超链接
find_element(By.PARTIAL_LINK_TEXT, "partial_link_text")
# 通过标签名定位元素
find_element(By.TAG_NAME, "tag_name")
find_elements()
Selenium可以驱动浏览器来执行一系列操作,即让浏览器执行一些动作。常见用法:输入文字时用send_keys方法,清空文字用clear方法,点击按钮用click方法。示例如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
browser = webdriver.Chrome()
browser.get('https://www.baidu.com/')
s = browser.find_element(By.ID, 'kw')
s.send_keys('李宁')
time.sleep(1)
s.clear()
s.send_keys('耐克')
but = browser.find_element(By.ID,'su')
but.click()
time.sleep(10)
上面例子中,一些交互动作都是针对某个节点执行的,如,对于输入框,用他的输入文字和清空文字方法;对于按钮,调用它的点击方法。另外一种操作,没有特定的执行对象,如鼠标拖拽,键盘按键等,这些动作通过另一种形式来执行,那就是动作链。
比如,现在实现一个节点的拖拽操作,将某个节点从一处拖拽到另一处,可以这样实现:
from selenium import webdriver
from selenium.webdriver.common.by import By
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') #切换到嵌套的网页(frame的为iframeResult的网页)
source = browser.find_element(By.CSS_SELECTOR,'#draggable')
target = browser.find_element(By.CSS_SELECTOR,'#droppable')
action = ActionChains(browser)
action.drag_and_drop(source,target)
action.perform()
首先,打开一个网页的拖曳实例,然后依次选中要拖曳的节点和拖曳后的节点,接着声明ActionChains对象并赋值给action,然后通过调用drag_and_drop()方法,再调用perform()方法执行动作,此时就完成了拖曳操作。
移动元素element元素的顶端与当前窗口的顶部对齐
execute_script(“argument[0].scrollIntoView();”,element)
execute_script(“argument[0].scrollIntoView(True);”,element)
移动元素element对象的底端与当前窗口的底部对齐
execute_script(“argument[0].scrollIntoView(False);”,element)
移动到页面最底部(scrollTo(x,y),x指的是x轴坐标,y指的是y轴坐标)
execute_script("window.scrollTo(0,document.body.scrollHeight)")
移动到指定的坐标(相对当前的坐标)
execute_script("window.scrollBy(0,700)")
结合上面的scrollBy语句,相当于移动到700+800px位置
execute_script("window.scrollBy(0,800)")
移动到窗口绝对位置坐标
execute_script("window.scrollTo(0,700)")
对于某些操作,Selenium API没有提供,比如,下拉进度条,它可以通过直接模拟运行JavaScript,此时使用execute_script()方法即可以实现,代码如下:
# document.body.scrollHeight 获取页面高度
# 按F12之后在控制台输入document.body.scrollHeight 可以查看页面高度
from selenium import webdriver
import time,random
browser = webdriver.Chrome()
browser.get('https://36kr.com/')
# # 下拉边框,一次性拉到位
# browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
# 慢慢下拉
for i in range(1,9):
time.sleep(random.randint(100,300)/1000)
browser.execute_script('window.scrollTo(0,{})'.format(i*1000))
time.sleep(10)
这里利用execute_script()方法将进度条拉到最底部
所以说有了这个方法,基本上API没有提供的所有功能都可以执行JavaScript方式来实现
可以使用get_attribute()方法来获取节点的属性,但是其前提是先选中这个节点,示例如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
url = 'https://pic.netbian.com/4Kxinnian/'
browser = webdriver.Chrome()
browser.get(url)
src = browser.find_elements(By.XPATH,'//ul[@class="clearfix"]/li/a/img')
for i in src:
# 获取i中的src属性
url = i.get_attribute('src')
print(url)
通过get_attribute()方法,然后传入想要获得的属性名,就可以得到它的值了。
另外,WebElement节点还有一些其他属性,比如id属性可以获取节点id,location属性可以获取该节点在页面中的相对位置,tag_name 属性可以获取标签名称,size属性可以获取节点的大小(宽高),示例如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
# 安装Pillow库
from PIL import Image
from io import BytesIO
url = 'https://pic.netbian.com/4Kxinnian/'
browser = webdriver.Chrome()
browser.get(url)
img = browser.find_element(By.XPATH,'//ul[@class="clearfix"]/li[1]/a/img')
location = img.location
size = img.size
print(location,size)
# top bottom left right 分别代表上边界 下边界 左边界 右边界
top,bottom,left,right = location['y'],location['y']+size['height'],location['x'],location['x']+size['width']
print(top,bottom,left,right)
screen = browser.get_screenshot_as_png()
screen = Image.open(BytesIO(screen))
cap = screen.crop((top,left,right,bottom))
cap.save('1.png')
我们知道网页中有一种节点叫做iframe,也就是子Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium打开页面后,它默认是在父级Frame里面进行操作的,而此时页面中还有子Frame,它是不能获取到子Frame里面的节点的,这是需要使用switch_to.frame()方法来切换Frame。示例如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
browser.get('https://www.douban.com')
login_frame = browser.find_element(By.XPATH,'//div[@class="login"]/iframe')
browser.switch_to.frame(login_frame)
s = browser.find_element(By.CLASS_NAME,'account-tab-account')
s.click()
browser.find_element(By.ID,'username').send_keys('123456')
在Selenium中,get()方法会在网页框架加载结束后结束执行,次数如果获取page_source,可能并不是浏览器完全加载完成的界面,如果某些页面有额外的Ajax请求,我们在页面源代码中也不一定能够获取到。所以,这里需要延时等待一定时间,确保节点已经加载出来,这里等待的方式有两种,显式等待和隐式等待。
当使用隐式等待执行测试的时候,如果Selenium没有在DOM中找到节点,将继续等待,超出设定的时间后,会抛出找不到节点的异常。换句话说,当查找节点而节点并没有立即出现的时候,隐式等待会等待一段时间再查找DOM,默认时间是0,示例如下:
隐式等待效果并没有那么好,因为我们只规定了一个固定的时间,而页面加载受网络等条件的影响。
显示等待方法,它指定要查找的节点,然后指定一个最长的等待时间。如果在规定的时间内加载出来这个节点,就返回查找的节点,如果没有加载出来,则抛出超时异常,示例如下
平时使用浏览器都有前进和后退功能,Selenium也可以完成这样的操作,它使用back()方法后退,使用forward()方法前进,示例如下:
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.get('https://www.douban.com')
browser.get('https://www.jd.com')
browser.back()
time.sleep(1)
browser.forward()
browser.close()
使用Selenium,还可以方便的对Cookies进行操作,获取、添加、删除Cookies等
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get('https://www.zhihu.com')
print(browser.get_cookies())
browser.add_cookie({'name':'name','domain':'www.zhihu.com','value':'germy'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
在访问页面的时候,会开启一个个选项卡。在Selenium中,我们也可以对选项卡进行操作
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get('https://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('https://www.jd.com')
首先访问百度,然后调用execute_script()方法执行window.open()这个JavaScript语句开启一个选项卡。接下来我们想切换到该选项卡。调用window_handles属性获取当前开启的所有选项卡,返回选项卡的代号序列。使用switch_to.window()方法切换选项卡,其中参数一是选项卡的代号,我们将第二个选项卡的代号传入,即跳转到第二个选项卡,然后在第二个选项卡上打开淘宝,然后再切换到第一个选项卡(重新调用switch_to.window()方法),打开京东。
在使用Selenium的过程中,难免会遇到一些异常,例如超时、节点未找到等错误,一旦出现此类错误,程序便不会执行。这里使用try expect语句来捕获各种异常。
from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
try:
browser.get('https://www.baidu.com')
except TimeoutException:
print('超时!')
try:
browser.find_element(By.ID,'hello')
except NoSuchElementException:
print('没有此类节点')
finally:
browser.close()
无处理:
browser = get('https://www.baidu.com')
设置屏蔽
options = webdriver.ChromeOptions()
options.add_argument('--disable-blink-features=AutomationControlled')
browser = webdriver.Chrome(options = options)
browser.get('https://www.baidu.com')
采集义乌购商品网站
完整流程如下图所示:
代码实现:
from selenium import webdriver
from selenium.webdriver.common.by import By
import time,random
from selenium.common.exceptions import NoSuchElementException
import pymysql
class YwShop():
def __init__(self):
# 初始化函数,提供配置项,设置屏蔽,防止网站检测出是爬虫
options = webdriver.ChromeOptions()
options.add_argument('--disable-blink-features=AutomationControlled')
self.browser = webdriver.Chrome(options=options)
def base(self):
# 访问网站
self.browser.get('https://www.yiwugo.com/')
# 获取输入框
input = self.browser.find_element(By.ID,'inputkey')
# 发送参数
input.send_keys('饰品')
# 点击事件
self.browser.find_element(By.XPATH,'//div[@class="search-index"]/span[last()]').click()
print('调用访问网站函数')
def spider(self):
# 下拉翻页
# window.document.documentElement.scrollHeight 在控制台输入查看网页高度
self.drop_down()
# 定位数据区域
li = self.browser.find_elements(By.CLASS_NAME,'pro_list_product_img2')
for i in li:
# 定位数据
title = i.find_element(By.XPATH,'.//li/a[@class="productloc"]')
price = i.find_element(By.XPATH,'.//li/span[@class="pri-left"]/em')
info = i.find_elements(By.XPATH,'.//li/span[@class="pri-right"]/span')
address = i.find_element(By.XPATH,'.//li[@class="shshopname"]')
texts = ''
for j in info:
texts=texts+j.text
# 构造数据结构
items = {
'标题':title.text,
'价格':price.text,
'地址':address.text,
'信息':texts
}
# 调用保存逻辑
self.save_mysql(items)
# 翻页
self.page_next()
def save_mysql(self,data):
title = data.get('标题')
price = data.get('价格')
address = data.get('地址')
texts = data.get('信息')
db = pymysql.connect(host='localhost',user='root',password='123456',port=3306,db='test1')
cursor = db.cursor()
sql = 'insert into yiwudb(title,price,address,texts) values(%s,%s,%s,%s)'
try:
cursor.execute(sql,(title,price,address,texts))
db.commit()
except Exception as e :
print('出错信息:',e)
db.rollback()
db.close()
def page_next(self):
# 定位翻页
try:
next = self.browser.find_element(By.XPATH,'//ul[@class="right"]/a[@class="page_next_yes"]')
if next:
next.click()
self.spider()
else:
self.browser.close()
except Exception as e:
print(e)
def drop_down(self):
# 下拉页面
for i in range(1,10):
j = i/10
js = f'window.scrollTo(0,document.body.scrollHeight * {j})'
self.browser.execute_script(js)
time.sleep(random.randint(400,800)/1000)
if __name__ == '__main__':
f = YwShop()
f.base()
f.spider()