概念:基于浏览器自动化的模块
appnium
:基于手机自动化的模块的应用环境的安装
pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple
跟爬虫之间的关联?
基本操作
导包:from selenium import webdriver
(web浏览器,driver驱动)
必须提供对应浏览器的驱动程序(谷歌,火狐...)
实例化一个浏览器对象
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
# Chrome 谷歌浏览器 executable_path 浏览器驱动路径
标签定位
标签对象.send_keys()
:向指定标签中录入数据
提交标签.click()
js
注入:浏览器对象.execute_script("js代码")
浏览器对象.page_source
:返回当前页面的页面源码数据,包含动态加载数据
关闭浏览器:浏览器对象.quit()
缺点
什么时候用selenium
from selenium import webdriver
from time import sleep
# 实例化浏览器对象
bro = webdriver.Chrome(executable_path='./chromedriver.exe') # Chrome 谷歌浏览器 executable_path 浏览器驱动地址
# 制定一些自动化的操作
# 发起请求
bro.get('https://www.jd.com')
# 如何进行标签定位
search_tag = bro.find_element_by_id('key')
# 向文本框中录入数据
search_tag.send_keys('mac pro')
sleep(2)
btn = bro.find_element_by_xpath('//*[@id="search"]/div/div[2]/button')
btn.click()
sleep(2)
# 注入JS代码
bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
sleep(2)
# page_source :返回当前页面的页面源码数据,包含动态加载数据
print(bro.page_source)
# 关闭浏览器
bro.quit()
from selenium import webdriver
from time import sleep
from lxml import etree
# 实例化浏览器对象
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
# 发起请求
bro.get('http://125.35.6.84:81/xk/')
sleep(1)
# 第一页的页面源码数据
page_text = bro.page_source
all_page_text = [page_text]
for i in range(1,5):
# 找到下一页对应的标签
a_tag = bro.find_element_by_xpath('//*[@id="pageIto_next"]')
# 对下一页的标签发起点击
a_tag.click()
sleep(1)
# page_source 获取当前页面的源码数据(涵动态加载)
page_text = bro.page_source
all_page_text.append(page_text)
for page_text in all_page_text:
tree = etree.HTML(page_text)
# xpath解析到name对应的标签
li_lst = tree.xpath('//*[@id="gzlist"]/li')
for li in li_lst:
name = li.xpath('./dl/@title')[0]
print(name)
sleep(2)
bro.quit()
from selenium.webdriver import ActionChains
NoSuchElementException
报错:没有定位到指定的标签
浏览器对象.switch_to.frame('iframe标签id的属性值')
:将当前浏览器页面切换到指定的子页面范围中action = ActionChains(bro)
action.click_and_hold(tagName)
action.move_by_offset(xoffset, yoffset)
一点一点偏移action.move_to_element(to_element)
action.move_to_element_with_offset(to_element, xoffset, yoffset)
偏移.perform()
:动作链立即执行from selenium import webdriver
from selenium.webdriver import ActionChains
from time import sleep
bro = webdriver.Chrome("./chromedriver.exe")
bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
# 标签定位
bro.switch_to.frame('iframeResult')
div_tag = bro.find_element_by_id('draggable')
# 需要使用ActionChains定制好的行为动作
# 针对当前浏览器页面实例化了一个动作链对象
action = ActionChains(bro)
# 点击且长按一个指定的标签
action.click_and_hold(div_tag)
for i in range(1,7):
# 一点一点迁移
action.move_by_offset(10,15).perform() # perform() 是动作链立即执行
action.move_to_element
action.move_to_element_with_offset
sleep(0.5)
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
# 无头浏览器开整
# 实例化options对象
chrome_options = Options()
# 调用add_argument方法,进行自定义配置
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
bro = webdriver.Chrome(executable_path="./chromedriver.exe",chrome_options=chrome_options)
bro.get('https://www.baidu.com')
# 截屏
bro.save_screenshot('./1.png')
print(bro.page_source)
webServer是如何检测到我们的请求是否使用了selenium
window.navigator.webdriver
环境配置
本机谷歌浏览器的驱动程序所在的目录路径添加到环境变量中
使用本机谷歌的驱动程序开启一个浏览器
chrome.exe --remote-debugging-port=9222 --user-data-dir="D:\selenum\AutomationProfile"
9222:端口(任意空闲端口)
"D:\selenum\AutomationProfile":已经事先存在的一个空目录
# 终端先运行如下代码
chrome.exe --remote-debugging-port=9222 --user-data-dir="D:\selenum\AutomationProfile"
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
chrome_options = Options()
chrome_options.add_experimental_option('debuggerAddress','127.0.0.1:9222')
# 代码托管打开的浏览器,不会实例化一个新的浏览器
driver = webdriver.Chrome(executable_path="./chromedriver.exe",chrome_options=chrome_options)
driver.get('http://www.taobao.com')
selenium
规避检测的操作
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions() #实例化一个ChromeOptions对象
option.add_experimental_option('excludeSwitches', ['enable-automation']) #以键值对的形式加入参数
bro = webdriver.Chrome(executable_path='./chromedriver.exe',options=option) #在调用浏览器驱动时传入option参数就能实现undefined
URL:12306登陆
分析:
基于超级鹰识别验证码登录,类型9004
# 超级鹰的包
import requests
from hashlib import md5
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
# 封装一个验证码识别的函数
def transform_code(imgPath,imgType):
chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰用户名对应的密码', '软件ID')
im = open(imgPath, 'rb').read()
return chaojiying.PostPic(im, imgType)['pic_str']
# 模拟登陆实现代码
from time import sleep
from PIL import Image # pip install Pillow
from selenium import webdriver
from selenium.webdriver import ActionChains
# 实例化一个谷歌浏览器对象
bro = webdriver.Chrome(executable_path="./chromedriver.exe")
# 发起请求
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
# 登录页面第一个展示的是扫码,点击帐号密码登录
bro.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a').click()
sleep(2) # 等待2秒,加载验证码图片
# 定位到用户名密码框,输入帐号密码
bro.find_element_by_id('J-userName').send_keys('xxxxxxxx') # 12306用户名
bro.find_element_by_id('J-password').send_keys('********') # 12306用户名对应的密码
# 验证码的点击操作
bro.save_screenshot('./12306.png')# 将页面当作图片保存到本地
# 将验证码图片的标签定位到
img_tag = bro.find_element_by_id('J-loginImg')
# 验证码的坐标和大小
location = img_tag.location
size = img_tag.size
# 裁剪的范围,这个根据截图自己情况调整,自己调试的(699, 284, 1015, 472)
rangle = (int(location['x'])-65,int(location['y']),int(location['x']+size['width'])-49,int(location['y']+size['height']))
# 使用Image类根据rangle裁剪范围进行验证码图片的裁剪
i = Image.open('./12306.png') # bytes类型数据
frame = i.crop(rangle) # 验证码对应的二进制数据
frame.save('./code.png')
img_coor = transform_code('./code.png',9004) # 返回坐标值 274,146|37,147
# 将坐标字符串转换为嵌套的列表
all_lst = [] # [[274,146],[37,147]...]
if '|' in img_coor:
lst_1 = img_coor.split("|")
count_1 = len(lst_1)
for i in range(count_1):
xy_lst = []
x = int(lst_1[i].split(',')[0])
y = int(lst_1[i].split(',')[1])
xy_lst.append(x)
xy_lst.append(y)
all_lst.append(xy_lst)
else:
x = int(img_coor.split(',')[0])
y = int(img_coor.split(',')[1])
xy_lst = []
xy_lst.append(x)
xy_lst.append(y)
all_lst.append(xy_lst)
for data in all_lst:
# 每个data都是一个列表中有2个元素
x = data[0]
y = data[1]
# 实例化一个动作链,在指定范围(验证码标签范围),找到x,y坐标,点击,动作链立即执行
ActionChains(bro).move_to_element_with_offset(img_tag,x,y).click().perform()
# 执行一次等待0.5秒,防止过快
sleep(0.5)
# 点击登录按钮,实现登录
bro.find_element_by_id('J-login').click()
sleep(2)
# 关闭浏览器
bro.quit()