文章目录
selenium模块的定义
基于浏览器自动化的一个模块
使用selenium模块的优点
- 便捷的获取网站加载的动态数据
- 便捷实现模拟登陆
selenium模块使用流程
环境安装:
pip install selenium
下载chrome浏览器的驱动程序(网站不太稳定,可能需要多进几次):http://chromedriver.storage.googleapis.com/index.html
步骤
- 实例化一个浏览器对象
- 编写基于浏览器自动化的操作代码
先看一个自动加载药监局网站并获取上面企业名称的例子
from selenium import webdriver
from lxml import etree
from time import sleep
if __name__ == '__main__':
# 实例化一个浏览器对象(一定要传入浏览器驱动程序)
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
# 让浏览器对指定url发起请求
bro.get('http://scxk.nmpa.gov.cn:81/xk/')
# 获取浏览器当前页面源码数据
page_text = bro.page_source
# 解析企业名称
tree = etree.HTML(page_text)
li_list = tree.xpath('//ul[@id="gzlist"]/li') # 定位到企业名称的li标签
for li in li_list:
title_name = li.xpath('./dl/@title')[0]
print(title_name)
sleep(5) # 在网站停留五秒
bro.quit() # 自动退出网站
再看一个在淘宝上自动搜索商品的操作
from selenium import webdriver
import time
if __name__ == '__main__':
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('https://www.taobao.com/')
# 标签定位,这里是根据id来定位
search_input = bro.find_element_by_id('q')
# 标签交互
search_input.send_keys('macbook')
time.sleep(2)
# 执行js代码
bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(5)
# 点击搜索按钮
btn = bro.find_element_by_class_name('btn-search')
btn.click()
time.sleep(5)
bro.quit()
代码中,bro.find_element_by_id()
函数用于定位网页源码中id
标签,函数的参数就是id
标签的值;
send_keys()
用于向输入框中输入字符;
如果想要模拟鼠标的滑轮向下滑的操作,可以用js代码实现,而执行js代码的函数是execute_script()
,相应的js代码window.scrollTo(0,document.body.scrollHeight)
,0
代表的是x轴
即横向移动的像素,另一个参数如果是数字的话,就是y轴
即竖着移动的像素,这里的document.body.scrollHeight
是向下滑动显示屏的大小;
在输入想要搜索的内容后,还有一个点击搜索按钮的操作,可以用click()
函数来实现。
bro.get('https://www.baidu.com/')
time.sleep(2)
# 回退
bro.back()
time.sleep(2)
# 前进
bro.forward()
这里还有两个常用操作,back()
用于返回浏览器上一页,forward()
用于返回上一页后再回去。
selenium处理iframe
先来了解下什么是iframe
iframe 用于在网页内显示网页。
<iframe src=“URL”></iframe>
URL 指向隔离页面的位置。
在程序中,如果定位的标签是存在于iframe中,则必须通过switch_to.frame('iframe标签id')
再进行标签定位。
实现拖动的操作,在我们实际操作时,需要点击+长按鼠标来拖动,在selenium模块中就要使用到动作链。
动作链
导入动作链对应的类:
from selenium.webdriver import ActionChains
实例化一个动作链对象:
action = ActionChains(bro)
执行长按且点击的操作:
action.click_and_hold(div)
执行拖动操作(x是水平移动的像素,y是竖直移动的像素):
action.move_by_offset(x, y)
让动作链立即执行:perform()
释放动作链对象:release()
下面是代码实现runoob上拖动的操作
from selenium import webdriver
from time import sleep
# 导入动作链对应的类
from selenium.webdriver import ActionChains
if __name__ == '__main__':
bro = webdriver.Chrome('./chromedriver.exe')
bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
bro.switch_to.frame('iframeResult')
div = bro.find_element_by_id('draggable')
# print(div)
# 动作链
action = ActionChains(bro)
# 点击长按指定标签
action.click_and_hold(div)
for i in range(5):
# move_by_offset()有x、y两个参数
# perform()表示立即执行动作链操作
action.move_by_offset(17, 0).perform()
# 释放动作链
action.release()
bro.quit()
自动登录QQ空间
网站url:https://qzone.qq.com/
先进行信息搜集,打开qq空间url后,出现的是扫码登录页面,需要点击账号密码登录才能输入账号密码,然后点击登录,最后成功。
from selenium import webdriver
from time import sleep
if __name__ == '__main__':
bro = webdriver.Chrome('./chromedriver.exe')
bro.get('https://qzone.qq.com/')
# 登录页面在一个iframe中
bro.switch_to.frame('login_frame')
btn = bro.find_element_by_id('switcher_plogin')
btn.click()
username_search = bro.find_element_by_id('u')
username_search.send_keys('xxxxxxx') # 用户账号
password_search = bro.find_element_by_id('p')
password_search.send_keys('xxxxxx') # 用户密码
login_btn = bro.find_element_by_id('login_button')
login_btn.click()
sleep(5)
bro.quit()
一开始,我不知道登录框在一个iframe
中,一直不成功,后来才发现登录框在一个名为login_frame
的iframe
中,需要先切换才能进行后面的操作。
无头谷歌浏览器
在前面,程序执行时会弹出一个浏览器的窗口,当我们不想它弹出窗口时,就要用到无头浏览器的知识。
导入相关类:
from selenium.webdriver.chrome.options import Options
调用类中方法进行参数设定:
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
例如请求百度
from selenium import webdriver
from time import sleep
from selenium.webdriver.chrome.options import Options
if __name__ == '__main__':
chrome_options = Options()
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')
print(bro.page_source)
sleep(2)
bro.quit()
程序运行后一直未弹出浏览器加载的界面,并且最后也输出了网站的源码。
selenium检测的规避
导入相关类:
from selenium.webdriver import ChromeOptions
调用类中方法进行参数设置:
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
还是上面那个例子
from selenium import webdriver
from time import sleep
# 实现无可视化界面
from selenium.webdriver.chrome.options import Options
# 规避检测
from selenium.webdriver import ChromeOptions
if __name__ == '__main__':
# 实现无可视化操作
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
# 规避被检测到的风险
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])
bro = webdriver.Chrome(executable_path='./chromedriver.exe', chrome_options=chrome_options, options=option)
bro.get('https://www.baidu.com')
print(bro.page_source)
sleep(2)
bro.quit()
12306模拟登陆
12306的登录界面主要是这个选图片的验证码,可以使用超级鹰打码平台进行识别。在验证码类型中,选择的是坐标多选,返回1~4个坐标,如:x1,y1|x2,y2|x3,y3
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()
if __name__ == '__main__':
chaojiying = Chaojiying_Client('用户名', '登录密码', '软件id') # 用户中心>>软件ID 生成一个替换 96001
im = open('a.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 9004)['pic_str']) # 9004 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()
模拟登录编码流程:
- 使用selenium打开登录页面
- 对当前selenium打开的这张页面进行截图
- 对当前截取的图片局部区域(验证码)进行裁剪
- 使用超级鹰识别验证码图片(返回坐标数据)
网站打开后是扫码登录的页面,首先要切换到账号密码登录
# 切换成账号密码登录
account_login_btn = bro.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a')
account_login_btn.click()
其次,打码平台能解决识别验证码的问题,如何把验证码保存到本地呢?可以使用截图的方法,先截图整个屏幕,然后再将验证码的那一块图片截出来。
# 截图并保存
bro.save_screenshot('aa.jpg')
# 确定验证码图片的左上角和右下角的坐标
code_img_ele = bro.find_element_by_class_name('imgCode')
location = code_img_ele.location # 验证码图片左上角的坐标x,y
print('location:', location)
size = code_img_ele.size # 验证码标签对应的长和宽
print('size:', size)
# 图片左上角和右下角的坐标
rangle = (
int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
# 至此验证码图片区域确定下来
接下来是要用到python的PIL模块,我下载时失败了,后来才知道要下Pillow
模块
from PIL import Image
# 实例化image对象
i = Image.open('./aa.jpg')
code_img_name = './code.png'
# 使用crop根据指定区域进行图片裁剪
frame = i.crop(rangle)
frame.save(code_img_name)
到这里,验证码的图片就被保存到了本地,接下来是用超级鹰对验证码进行识别。
识别完成后,注意到最后的结果是xxx,xxx|xxx,xxx
这一种的,想到可以以|
进行分割,将每一个坐标存为一个列表,最后所有的结果再存为一个大列表,类似于[[x1,y1],[x2,y2]]
,然后使用数组遍历,依次模拟鼠标点击。
from selenium.webdriver import ActionChains
if '|' in result:
list_1 = result.split('|')
count_1 = len(list_1)
for i in range(count_1):
xy_list = []
x = int(list_1[i].split(',')[0])
y = int(list_1[i].split(',')[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
else:
x = int(result.split(',')[0])
y = int(result.split(',')[1])
xy_list = []
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
print(all_list)
# 遍历列表,使用动作链进行点击
for l in all_list:
x = l[0]
y = l[1]
ActionChains(bro).move_to_element_with_offset(code_img_ele, x, y).click().perform()
time.sleep(0.5)
最后是简单的输入12306的用户名和密码,并点击登录
# 录入用户名和密码
username_btn = bro.find_element_by_id('J-userName')
password_btn = bro.find_element_by_id('J-password')
username_btn.send_keys('12306用户名')
password_btn.send_keys('12306密码')
login_btn = bro.find_element_by_id('J-login')
login_btn.click()
完整的模拟登录代码
import requests
from hashlib import md5
from selenium import webdriver
import time
from PIL import Image
from lxml import etree
from selenium.webdriver import ActionChains
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()
# 使用selenium打开页面
bro = webdriver.Chrome(executable_path='chromedriver.exe')
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
bro.maximize_window()
time.sleep(1)
# 切换成账号密码登录
account_login_btn = bro.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a')
account_login_btn.click()
# 截图并保存
bro.save_screenshot('aa.jpg')
# 确定验证码图片的左上角和右下角的坐标
code_img_ele = bro.find_element_by_class_name('imgCode')
location = code_img_ele.location # 验证码图片左上角的坐标x,y
print('location:', location)
size = code_img_ele.size # 验证码标签对应的长和宽
print('size:', size)
# 图片左上角和右下角的坐标
rangle = (
int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
# 至此验证码图片区域确定下来
# 实例化image对象
i = Image.open('./aa.jpg')
code_img_name = './code.png'
# 使用crop根据指定区域进行图片裁剪
frame = i.crop(rangle)
frame.save(code_img_name)
# 将验证码提交给超级鹰识别
chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰密码', '软件id') # 用户中心>>软件ID 生成一个替换 96001
im = open(code_img_name, 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
result = chaojiying.PostPic(im, 9004)['pic_str']
print(result) # 1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()
all_list = [] # 存储即将被点击的点的坐标 [[],[]]
if '|' in result:
list_1 = result.split('|')
count_1 = len(list_1)
for i in range(count_1):
xy_list = []
x = int(list_1[i].split(',')[0])
y = int(list_1[i].split(',')[1])
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
else:
x = int(result.split(',')[0])
y = int(result.split(',')[1])
xy_list = []
xy_list.append(x)
xy_list.append(y)
all_list.append(xy_list)
print(all_list)
# 遍历列表,使用动作链进行点击
for l in all_list:
x = l[0]
y = l[1]
ActionChains(bro).move_to_element_with_offset(code_img_ele, x, y).click().perform()
time.sleep(0.5)
# 录入用户名和密码
username_btn = bro.find_element_by_id('J-userName')
password_btn = bro.find_element_by_id('J-password')
username_btn.send_keys('12306账号')
password_btn.send_keys('12306密码')
login_btn = bro.find_element_by_id('J-login')
login_btn.click()
如果在第一次运行后想要再多运行几次的,一定要记得删掉当前文件夹下保存的屏幕截图和验证码区域的截图,然后才能开始运行。
最后又弹出来一个滑块验证,如果用脚本登录的话就算拖动完也会报错,不让登录,真不愧是12306,十几年的防黄牛经验啊。