草蟒中文编程:“自动访问”库(selenium)基本使用
参考文章:
- https://blog.csdn.net/weixin_36279318/article/details/79475388
- https://www.cnblogs.com/WiseAdministrator/articles/11316809.html
1. 简介
- “自动访问”库 (selenium) 最初是一个自动化测试工具,爬虫中使用它主要是为了解决“网络请求”库 (requests) 无法直接执行 JavaScript 代码的问题。
- 其本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果。
- 支持多种浏览器。本次汉化重点是支持谷歌 Chrome 浏览器。对其他浏览器的完整支持待老吴有空再说。
- 本文未详尽列出涉及到的所有类、方法和属性。欲知详情,请使用草蟒的自动提示和补全功能查看。
- 本文非原创,而是参考众多网站改编而成。
2. 前提条件
- 谷歌浏览器
- 根据浏览器版本下载相应的驱动程序:http://chromedriver.storage.googleapis.com/index.html
3. 简单使用
从 自动访问 导入 老司机
从 时间 导入 睡眠
# 首先要创建一个开“车”带你到处逛的“司机”对象,这里的“车”就是浏览器
# 如果本库使用频率较高,可以配置环境变量,将驱动程序路径配置到环境变量 Path 中,这样就不必每次都要传入路径参数了
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe') # 传入驱动程序路径
# 发送 url 请求
司机.获取('https://www.baidu.com')
睡眠(2)
#司机.刷新()
#设置浏览器大小
司机.设置窗口大小(1400, 800)
# 司机.窗口最大化()
# 查看网页标题
司机.标题
# 查看当前网页的 url
司机.当前url
# 根据 id 找到搜索框并输入搜索内容"自动访问"
司机.查找元素_id("kw").发送按键('自动访问')
# 根据 id 找到 "百度一下" 按钮并点击
司机.查找元素_id("su").点击()
睡眠(1)
司机.标题
司机.当前url
# 根据类名找到搜索结果条数信息
司机.查找元素_类名('nums').文本
# 根据 xpath 查找出当前页面的所有搜索结果
元素列表 = 司机.查找所有元素_xpath('//div/h3/a')
# 做一些处理: 打印每个链接的文字内容, 然后找到链接并点击打开
取 元素 于 元素列表[:3]: # 这里仅选取前三条进行演示
打印(元素.文本)
司机.查找元素_链接文本(元素.文本).点击()
睡眠(2)
# 关闭当前窗口
司机.关闭()
# 彻底结束
司机.退出()
4. 元素定位
- "自动访问"库提供了一系列元素定位方法
- 这些方法分为两类: '查找元素_xxx' 和 '查找所有元素_xxx', xxx 表示条件
- 第一类找到的是第一个符合条件的元素, 第二类找到的是所有符合条件的元素
- 可在编辑器里输入 '司机.查找', 通过弹出的自动补全了解所有定位方法
5. 元素交互
比较常见的用法有:输入文字时用 '发送按键()' 方法,清空文字时用 '清除()' 方法,点击元素时用 '点击()' 方法。传入文件路径的话,'发送按键()' 还能上传文件。
6. 前进和后退
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.获取('https://www.baidu.com')
司机.获取('https://www.taobao.com')
司机.获取('http://www.sina.com.cn/')
司机.后退()
睡眠(2)
司机.前进()
7. 可以获取标签属性
司机.查找元素_css选择器('#SI_Top_Weibo .tn-tab').获取属性('href')
8. 框架切换
在 Web 应用中经常会遇到 frame/iframe 表单嵌套页面的应用,老司机只能在一个页面上对元素识别与定位,对于 frame/iframe 表单内嵌页面上的元素无法直接定位。这时就需要通过 '切换到.框架()' 方法将当前定位的主体切换为 frame/iframe 表单的内嵌页面。
参考:https://www.cnblogs.com/chenshaoping/p/5540434.html (xpath 定位详解)
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.获取('https://www.126.com')
# 司机.切换到.框架(司机.查找所有元素_标签名称("iframe")[0])
# 原文直接用'x-URS-iframe', 现已无法定位, 126 网站在后面加上了一串不断变化的数字, 比如 x-URS-iframe1592051452298.8767, 故可用 xpath 模糊匹配方法
xp = 司机.查找元素_xpath('//*[contains(@id, "x-URS-iframe")]')
司机.切换到.框架(xp)
司机.查找元素_名称('email').清除()
司机.查找元素_名称('email').发送按键('username')
司机.查找元素_名称('password').清除()
司机.查找元素_名称('password').发送按键('password')
司机.查找元素_id('dologin').点击()
司机.切换到.默认内容()
9. 三种等待方式
参考:https://www.jianshu.com/p/5edb75d0b687
- 强制等待: 时间.睡眠()
- 隐式等待: 在你设置的时间内判断网页是否加载完成,如果完成了,就进行下一步;如果没有加载完成,则会报超时错误. 隐性等待的设置时全局性的,在开头设置过之后,整个的程序运行过程中都会有效,都会等待页面加载完成;不需要每次设置一遍.
- 显示等待 ('老司机等等我'): 周期性地根据你设定的条件去判断,直到超过你设置的等待时间. 如果设置的条件满足,就进行下一步操作,如果没有满足会报超时错误
10. 窗口句柄
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.隐式等待(10)
司机.获取('https://www.sogou.com')
# 获得搜索窗口句柄
搜索窗口 = 司机.当前窗口句柄
司机.查找元素_链接文本('登录').点击()
司机.查找元素_部分链接文本('注册').点击()
# 获得所有打开窗口的句柄
所有窗口 = 司机.所有窗口句柄
取 窗口 于 所有窗口:
如果 窗口 != 搜索窗口:
司机.切换到.窗口(窗口)
打印('跳转到注册窗口')
司机.查找元素_id('number_input').发送按键('1234567')
司机.查找元素_id('message_input').发送按键('1234567')
睡眠(3)
11. 选择类:处理下拉框
以亚马逊北京控制台登录页面语言选择下拉框为例
从 自动访问.老司机 导入 选择类
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.隐式等待(10)
司机.获取('https://console.amazonaws.cn')
# 找到语言选择下拉框
语言选择 = 司机.查找元素_id('languageSelector')
# 选择类(语言选择).选择_可见文本('English')
# 选择类(语言选择).选择_索引(4)
# 选择类(语言选择).选择_值('ja')
选择类(语言选择).所有选项
12. Cookie 处理
可以方便地对Cookie进行操作,例如获取、添加、删除等。
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.隐式等待(10)
司机.获取('https://weibo.com')
睡眠(3)
司机.获取所有cookie()
司机.获取cookie('SUB')
13. 警示框处理
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.隐式等待(10)
# 下面是我自己写的一个HTML文件, 其中有一个 alert()、一个 confirm() 和 一个 prompt()
司机.获取('file:///E:/test/jstest.html')
示警 = 司机.切换到.警示框
示警.文本
示警.忽略()
示警2 = 司机.切换到.警示框
示警2.接受()
示警3 = 司机.切换到.警示框
示警3.发送按键('老吴同志') # 在弹出的提示框中输入内容
示警3.接受()
14. 执行 JavaScript
对于某些操作,“自动访问”库并没有提供,但可以模拟运行 JavaScript,使用 '执行脚本()' 方法即可实现.
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.获取('https://www.baidu.com')
司机.查找元素_id('kw').发送按键('李子柒')
司机.查找元素_id('su').点击()
睡眠(3)
js = 'window.scrollTo(100, 450);'
司机.执行脚本(js)
15. 可以自动截图
司机.保存截图('e:/test/bd.jpg')
16. 动作链
有一些操作没有特定的执行对象,比如鼠标拖曳、键盘按键等,这些动作用另一种方式来执行,那就是动作链。
比如,现在实现一个元素的拖曳操作,将其从一处拖曳到另一处,可以这样实现:
从 自动访问.老司机 导入 动作链类
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.隐式等待(10)
司机.获取('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
司机.切换到.框架('iframeResult')
目标元素 = 司机.查找元素_id('draggable')
动作链 = 动作链类(司机)
动作链.点击并按住(目标元素)
取 i 于 范围(4):
动作链.偏移(18, 0).完成()
睡眠(2)
动作链.释放()
动作链.完成()
睡眠(2)
司机.切换到.警示框.接受()
综合实例 1
# 素材图片下载
站长素材url = 'http://sc.chinaz.com/tupian/weimeiyijingtupian.html'
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.获取(站长素材url)
司机.查找元素_xpath('//*[@id="container"]/div[1]/p/a').点击()
所有句柄 = 司机.所有窗口句柄
司机.切换到.窗口(所有句柄[1])
睡眠(2)
# 点击图片下载
# 按钮 = 司机.查找元素_xpath('/html/body/div[7]/div[5]/div[1]/div[6]/div[2]/div[1]/div/div[3]/a[2]')
# 按钮.点击()
# 通过文本信息定位来下载
司机.查找元素_链接文本('广东电信下载').点击()
司机.退出()
失败案例
药监局反爬虫技术厉害,无法自动访问。
爬取上海药监局数据成功
上海url = 'http://wlxsba.smda.sh.cn/openApi/recordManage'
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.获取(上海url)
# 找到第一页的所有药企
药企列表 = 司机.查找所有元素_xpath('/html/body/div[1]/div[1]/div/div/div[2]/div/div[3]/div[3]/div/table/tbody/tr')
# 打印药企信息
取 企业 于 药企列表:
打印(企业.文本)
# 当前页码
司机.查找元素_类名('ui-pg-input').获取属性('value')
# 总页数
司机.查找元素_id('sp_1_gp-record').文本
# 自动输入页码, 实现翻页
# 如果写个 '取' 循环, 就能爬取所有页面的数千条数据了
从 自动访问.老司机 导入 按键类 # 回车等特殊按键在这个类中
页码框 = 司机.查找元素_类名('ui-pg-input')
页码框.清除()
页码框.发送按键('10')
页码框.发送按键(按键类.回车)
司机.退出()
自动访问 QQ 空间
从 自动访问 导入 老司机
从 自动访问.老司机 导入 动作链类, 老司机等等我, 期待条件, 方式类
从 时间 导入 睡眠
从 紫檀 导入 紫檀类
司机 = 老司机.谷歌(r'e:/chromedriver/chromedriver.exe')
司机.获取('https://qzone.qq.com/')
司机.窗口最大化()
司机.切换到.框架('login_frame') # 切换到登录界面
司机.查找元素_id('switcher_plogin').点击()
司机.查找元素_id('u').发送按键('xxxx') # 填入QQ号
司机.查找元素_id('p').发送按键('xxxx') # 填入密码
司机.查找元素_id('login_button').点击()
# 等待安全验证界面出现
睡眠(3)
# 切换到安全验证界面
司机.切换到.框架(司机.查找元素_xpath('//iframe'))
# 司机.保存截图('e:/test/安全验证.png')
滑块 = 司机.查找元素_id('slideBlock') # 也可以找 tcaptcha_drag_thumb 这个元素, 即下面的蓝色滑块
# 这里的滑块自动移动算法比较简单, 更复杂的算法参见:
# https://blog.csdn.net/u012067766/article/details/79793264
# https://www.zhihu.com/question/32209043
# https://blog.csdn.net/jingjing_94/article/details/80555511 , 用到图像分割
距离 = 160 # 滑块移动距离初始值, 这里有意设置得偏小
只要 真:
动作链 = 动作链类(司机)
动作链.点击并按住(滑块).完成()
动作链.偏移(距离, 0).完成()
动作链.重置动作()
# 司机.保存截图('e:/test/安全验证过程.png')
# 睡眠(0.5)
# 动作链.释放().完成()
睡眠(1)
试: # 如果未通过,"安全验证”页面一直会显示“拖动滑块”字样的文字提示
提示 = 司机.查找元素_类名('tc-title').文本
捕:
提示 = ''
如果 提示:
打印('需要调整...')
距离 += 3
如果 距离 > 190:
距离 = 160 # 防止滑块越界
睡眠(2)
否则: # 通过验证
# 司机.保存截图('e:/test/安全验证通过.png')
司机.切换到.父框架()
跳出
# 等待 QQ 空间页面加载
老司机等等我(司机, 10).直到(期待条件.元素存在((方式类.类名, 'feed_page_container')))
html = 司机.页面源代码
# 打印空间好友发布的一些文字内容
树 = 紫檀类(html, 'html.parser')
条目列表 = 树.选择('.feed_page_container li')
打印(长(条目列表)) # 显示当前页面有几条内容
取 条目 于 条目列表:
消息 = 条目.查找(类_='f-info')
转载 = 条目.查找(类_='txt-box')
如果 转载 且 转载.文本 != ' ':
打印(转载.文本)
打印()
或如 消息:
打印(消息.文本)
打印()
否则:
打印('无\n')
司机.退出()
部分代码截图
本文另有 jupyter notebook 文件,可在草蟒官网下载,方便大家查看实际运行结果。
中文编程是不是有点意思?希望更多人喜欢。
对于英文编程,我现在只想说:Go Foo Your Bar!