
本文共 12448 字,大约阅读时间需要 41 分钟。
文章目录
selenium模块的定义
基于浏览器自动化的一个模块
使用selenium模块的优点
- 便捷的获取网站加载的动态数据
- 便捷实现模拟登陆
selenium模块使用流程
环境安装:
pip install selenium
下载chrome浏览器的驱动程序(网站不太稳定,可能需要多进几次):
步骤
- 实例化一个浏览器对象
- 编写基于浏览器自动化的操作代码
先看一个自动加载药监局网站并获取上面企业名称的例子
from selenium import webdriverfrom lxml import etreefrom time import sleepif __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 webdriverimport timeif __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 webdriverfrom time import sleep# 导入动作链对应的类from selenium.webdriver import ActionChainsif __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:
先进行信息搜集,打开qq空间url后,出现的是扫码登录页面,需要点击账号密码登录才能输入账号密码,然后点击登录,最后成功。
from selenium import webdriverfrom time import sleepif __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 webdriverfrom time import sleepfrom selenium.webdriver.chrome.options import Optionsif __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 webdriverfrom time import sleep# 实现无可视化界面from selenium.webdriver.chrome.options import Options# 规避检测from selenium.webdriver import ChromeOptionsif __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 requestsfrom hashlib import md5class 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,yprint('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 ActionChainsif '|' 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 requestsfrom hashlib import md5from selenium import webdriverimport timefrom PIL import Imagefrom lxml import etreefrom selenium.webdriver import ActionChainsclass 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,yprint('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 生成一个替换 96001im = 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,十几年的防黄牛经验啊。
发表评论
最新留言
关于作者
