网抓神器--自动化测试工具 selenium 的使用

我们知道Excel有VBA来操作相关对象做自动化操作,和它类似浏览器也有一款"宏工具"--selenium.
下面我们一起来看怎么用python脚本调用这款工具辅助网抓。
第一步pip安装selenium
第二步下载对应浏览器驱动
请自行搜索“Chrome浏览器如何查看版本”,找到自己浏览器的版本号,我以谷歌浏览器为例、测试环境是win7系统:
file
file
可以看到我的版本号是58.0.3029.81
然后去根据以下对照表找到对应的驱动版本号
file
然后在以下网址下载对应的驱动
http://npm.taobao.org/mirrors/chromedriver/
下载对应windows系统安装包
file
将包中的exe文件解压放到你的python文件夹下的Scripts文件夹中(注意该路径必须在系统环境配置好)
下面我们测试一下号称浏览器的“宏工具”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')#按id号定位找到搜索框
    input.send_keys('Python')#在搜索框对象中键入python字符串
    input.send_keys(Keys.ENTER)#回车
    wait = WebDriverWait(browser, 10)#设置浏览器等待时间10秒。
    wait.until(EC.presence_of_element_located((By.ID, 'content_left')))#10秒内若正常跳转则加载页面否则返回报错信息。
    print(browser.current_url)#输出跳转后当前网页地址
    print(browser.get_cookies())#获取网页cookie
    print(browser.page_source)#输出网页源码
finally:
    browser.close()

非常的简单,执行上述代码将会类似VBA宏给我们自动模拟出整个浏览器操作的过程。
file
file
加载页面后时间非常短就会立刻输出以下打印页面。
file
有没有可能无界面的浏览器,也就是后台偷偷的运行、不占用电脑屏幕,据说高版本可以在初始化时,设置为如下静默方式:

option=webdriver.ChromeOptions()
option.add_argument('headless')
browser = webdriver.Chrome(chrome_options=option)

或者调用PhantomJS,初始化浏览器时设置为:

browser = webdriver.PhantomJS()

重点讲几个过程:
节点对象搜索定位方法:
find_elements_by_id('')
find_elements_by_id_by_class_name('')
find_elements_by_tag_name('')
find_elements_by_link_text('')
find_elements_by_partial_link_text('')
find_elements_by_xpath('')
find_elements_by_css_selector('')
这些你学网抓应该不陌生了,直接在网页对象上右击鼠标检查元素将会给我们返回网页中该对象的属性等信息。
这些方法统一为一个就是:find_element(定位方式,'定位对象定位方式中对应的属性值')
如find_element(By.ID,'q')
上面只能匹配第一个满足的节点,如果有多个节点满足可以使用find_elements
节点交互:
上面案例使用了搜索框的文本输入及回车操作
还比如,使用input.clear()对搜索框对象input进行文本内容清空
对按钮点击,button.click()对定位的button对象执行单击操作。
获取对象属性:
该工具类似于Beautifulsoup、正则表达式一样有自己的提取属性值等信息的方法
get_attribute:获取指定节点的属性,如 节点对象.getattribute('class')
获取文本直接对象节点.text
与text类似的还有.id、.location、.tag_name、.size分别获取对象的id号、在页面的相对位置、标签名称、大小等信息。
关于网页前进与后退操作
browser.forward()与browser.back()
关于网页操作动作链:
1.鼠标操作
context_click(对象):右击
double_click(对象):双击
drag_and_drop(从某个对象,拖到哪个对象):拖到
move_to_element(放在某个对象上):鼠标悬停
执行步骤:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains # 引入 ActionChains 类
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
对象= browser.find_element_by_link_text('新闻')
ActionChains(browser).context_click(对象).perform()

ActionChains(browser):调用ActionChains()类,并将浏览器驱动browser作为参数传入
context_click(对象):模拟鼠标右击,需要传入指定定位的元素作为参数
perform():执行ActionChains()中储存的所有操作

2.键盘操作(以下列举常用操作)
send_keys(Keys.BACK_SPACE):删除键(BackSpace)
send_keys(Keys.SPACE):空格键(Space)
send_keys(Keys.TAB):制表键(TAB)
send_keys(Keys.ESCAPE):回退键(ESCAPE)
send_keys(Keys.ENTER):回车键(ENTER)
send_keys(Keys.CONTROL,'a'):全选(Ctrl+A)
send_keys(Keys.CONTROL,'c'):复制(Ctrl+C)
send_keys(Keys.CONTROL,'x'):剪切(Ctrl+X)
send_keys(Keys.CONTROL,'v'):粘贴(Ctrl+V)
send_keys(Keys.F1):键盘F1
.....
send_keys(Keys.F12):键盘F12
执行步骤:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys #导入键盘类 Keys()
driver=webdriver.Chrome()
driver.get("http://www.baidu.com")
driver.find_element_by_id("kw").send_keys("selenium")
当我们发送中文字符串时出现乱码可以使用:send_keys(u"输入中文")解决!
定位一组元素,单选框、复选框的选中方法:
我们可以采用循环的方式对定位对象的子集进行处理,如下

for i in 对象:
    i.click()

一条龙案例展示:
1.快递100的批量查询截图

import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome()
browser.get('http://www.kuaidi100.com/')
input = browser.find_element_by_id('postid')
for i in ['263432466781','640006627091']:
    input.send_keys(i)
    time.sleep(2)
    browser.find_element_by_xpath('//*[@id="query"]').click()
    browser.maximize_window()
    time.sleep(1)
    browser.get_screenshot_as_file('F:\\'+i+'.png')    
browser.close()

file
上面保存的图片是当前网页的图像,如果我们的信息需要拉动滚动条,可以使用如下方法拖动到指定位置后截图:

import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome()
browser.get('http://www.kuaidi100.com/')
input = browser.find_element_by_id('postid')
input.send_keys('263432466781')
time.sleep(2)
browser.find_element_by_xpath('//*[@id="query"]').click()
browser.maximize_window()
target = browser.find_element_by_xpath('//*[@id="resultTop"]/span[2]')
browser.execute_script("arguments[0].scrollIntoView();", target)
time.sleep(1)
browser.get_screenshot_as_file('F:\\a.png')    
browser.close()

我们可能最终的目的不是截图,而是提取对象中的文字,比如上面我们可能需要的是路由信息,我们可以直接定位到内容进行提取,比如:

a=browser.find_element_by_xpath('//*[@id="queryResult"]/div[3]/div[3]/table/tbody/tr[1]/td[3]').text
print(a)

可以使用len函数获取表格行数(注意使用.find_elements_by_xpath):

len(browser.find_elements_by_xpath('//*[@id="queryResult"]/div[3]/div[3]/table/tbody/tr'))

根据这个特点我们循环构建xpath从而提取出弹出表单中全部的路由信息,不再赘述。
如果上面的案例确实无法获取到图片中的文本内容,比如验证码等情况,我们需要进一步剪裁至目标区域再结合ocr技术识别文本内容。
剪裁目标区域:

import time
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

browser = webdriver.Chrome()
browser.get('http://www.kuaidi100.com/')
input = browser.find_element_by_id('postid')
input.send_keys('263432466781')
time.sleep(2)
browser.find_element_by_xpath('//*[@id="query"]').click()
browser.maximize_window()
target = browser.find_element_by_xpath('//*[@id="resultTop"]/span[2]')
browser.execute_script("arguments[0].scrollIntoView();", target)
time.sleep(1)
browser.get_screenshot_as_file('F:\\a.png')

a=browser.find_element_by_xpath('//*[@id="queryResult"]')
left =a.location['x']
top =a.location['y']-338
Width =a.size['width']
Height =a.size['height']
np= Image.open(r'F:\a.png')
np=np.crop((left,top,Width,Height)) 
np.save(r'F:\a.png') 

browser.close()

当然上面如果操作滚动条只下拉滚动指定距离,我们可以采用如下方式设置:

js="var q=document.body.scrollTop=338"
browser.execute_script(js)

看到这里你应该脑洞大开,又可以使用js配合操作了......
我们还可以利用接口本身的菜单打印路由:

browser.find_element_by_xpath('//*[@id="queryResult"]/div[3]/div[2]/p/a[3]').click()
time.sleep(2)
handles = browser.window_handles
browser.switch_to_window(handles[1])#切换浏览器窗口句柄
time.sleep(1)
browser.save_screenshot('F:\\a.png')
browser.close()

我们发现案例中输入快递单号后会有个搜索下拉框出现,我们可以采用循环方式输出这个下拉框内匹配词条:

for i in browser.find_elements_by_id('inputTips'):
    print(i.text)

或者

for i in browser.find_elements(By.ID,'inputTips'):
    print(i.text)

如果我们需要调用浏览器部分菜单功能,比如调用打印功能可以借助于win32api、win32con等模拟:

import win32api,win32con,time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()

browser.get('http://www.kuaidi100.com/')
input = browser.find_element_by_id('postid')
input.send_keys('263432466781')
time.sleep(2)
browser.find_element_by_xpath('//*[@id="query"]').click()
browser.maximize_window()
a=browser.find_element_by_xpath('//*[@id="queryResult"]') 

ActionChains(browser).context_click(a).perform()
time.sleep(1)

win32api.keybd_event(80,win32con.KEYEVENTF_KEYUP,0)
time.sleep(3)
win32api.keybd_event(13,win32con.KEYEVENTF_KEYUP,0)
time.sleep(1)

browser.close()

如果需要使用组合键,请用下面按下单独释放方式:

win32api.keybd_event(17,0,0,0)
win32api.keybd_event(83,0,0,0)
win32api.keybd_event(83,0,win32con.KEYEVENTF_KEYUP,0)
win32api.keybd_event(17,0,win32con.KEYEVENTF_KEYUP,0)

另外我们可以借助剪切板传递变量值进行相关赋值操作:

import win32clipboard as w
import win32api,win32con
w.OpenClipboard()
w.EmptyClipboard()
w.SetClipboardData(win32con.CF_TEXT,'中国.html'.encode('gbk'))
w.CloseClipboard()
...
win32api.keybd_event(17,0,0,0)#模拟ctrl+v
win32api.keybd_event(86,0,0,0)
win32api.keybd_event(86,0,win32con.KEYEVENTF_KEYUP,0)
win32api.keybd_event(17,0,win32con.KEYEVENTF_KEYUP,0)
win32api.keybd_event(13,0,0,0)
win32api.keybd_event(13,0,win32con.KEYEVENTF_KEYUP,0)

另:360浏览器可以直接将网页保存为图片。
附网上的键值表:
file
2.超屏截图与验证码处理
老版本的谷歌浏览器可以直接CTRL+M保存整个网页为图片,网页截长图前面提到的PhantomJS也可以处理:

from selenium import webdriver
driver = webdriver.PhantomJS()
driver.get("http://www.guancha.cn")
driver.save_screenshot(r'F:\a.png')

百度AI接口的智能识别

from aip import AipOcr
client = AipOcr('11453826','rXvojWX46GFMeB6gP3HXuq7I','boZTSbiQLLOfTAg3qGs5u6Mlb3XY5WEK')
image=open(r'C:\Users\Administrator\Desktop\a.png', 'rb').read()
print(client.basicGeneral(image)['words_result'][0]['words'])

网络图片识别:client.basicGeneralUrl(url)
还可以针对设置参数识别数据:
options={"language_type":"CHN_ENG","detect_direction":"true","detect_language":"true","probability":"true"}
client.basicGeneral(image,options)
对于高精度要求可以使用:
client.basicAccurate(image)
其中高精度版本也有以下参数可设置:
file
如果图片中有生僻字可以使用client.enhancedGeneral(image)
百度AI的验证码短信通知测试:

from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from aip import AipOcr
import time

browser = webdriver.Chrome()
browser.get('https://login.bce.baidu.com/?account=&redirect=http%3A%2F%2Fconsole.bce.baidu.com%2Fai%2F%3Ffromai%3D1#/ai/ocr/overview/index')
browser.find_element_by_xpath('//*[@id="TANGRAM__PSP_4__smsSwitchWrapper"]').click()
time.sleep(1)
input=browser.find_element_by_xpath('//*[@id="TANGRAM__PSP_4__smsPhone"]')
input.send_keys('请填写你绑定的电话号码')
browser.find_element_by_xpath('//*[@id="TANGRAM__PSP_4__smsTimer"]').click()
time.sleep(1)
browser.maximize_window()
browser.get_screenshot_as_file('F:\\a.png')
#计算验证码范围尺寸
a=browser.find_element_by_xpath('//*[@id="TANGRAM__PSP_4__confirmVerifyCodeImg"]')
left =a.location['x']
top =a.location['y']
Width =a.size['width']
Height =a.size['height']
#剪裁至验证码区域
b=Image.open(r'F:\a.png').crop((left,top,left+Width,top+Height)).save(r'F:\a.png')
#ocr识别
client = AipOcr('11453826','rXvojWX46GFMeB6gP3HXuq7I','boZTSbiQLLOfTAg3qGs5u6Mlb3XY5WEK')
mt=open(r'F:\a.png','rb').read()
s=client.basicAccurate(mt)['words_result'][0]['words']
time.sleep(3)
#填写识别结果
input2=browser.find_element_by_xpath('//*[@id="TANGRAM__PSP_4__confirmVerifyCode"]')
input2.send_keys(s)
browser.find_element_by_xpath('//*[@id="TANGRAM__PSP_34__confirm_continue"]').click()
browser.close()

对于简单滑块验证的处理:

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time

driver = webdriver.Chrome()
driver.get("https://www.helloweba.com/demo/2017/unlock/")
hk= driver.find_elements_by_class_name("slide-to-unlock-handle")[0]

a=ActionChains(driver)
a.click_and_hold(hk).perform()
a.move_by_offset(500,0).perform()    #水平滑动距离足够大
a.reset_actions()
time.sleep(0.2)
ws= driver.switch_to.alert.text   #捕捉弹出提示信息,类似.switch_to.frame进行子页面的跳转。
print(ws)
time.sleep(2)
driver.quit()

对于图像核对的滑块:
参考:【Python+selenium】网页登录滑动解锁 - CSDN博客 https://blog.csdn.net/EB_NUM/article/details/78394958

道高一尺 魔高一丈
https://pbihub.cn/users/44
M与DAX的恩怨纠葛