selenium+python实现1688网站验证码图片的截取功能


Posted in Python onAugust 14, 2018

1. 背景

•在1688网站爬取数据时,如果访问过于频繁,无论用户是否已经登录,就会弹出如下所示的验证码登录框。

selenium+python实现1688网站验证码图片的截取功能

一般的验证码是类似于如下的元素(通过链接单独加载进页面,而不是嵌入图片元素):

<img id="J_CheckCodeImg1" width="100" height="30" onmousedown="return false;" src="//pin.aliyun.com/get_img?identity=sm-searchweb2&sessionid=9c3a51d81de07ddf1bfd9bbc70863b0f&type=default&t=1511315617645">

•一般来说,获取验证码图片有两种方式:

•第一,拿到上面验证码的图片链接:src=”//pin.aliyun.com/get_img?identity=sm-searchweb2&sessionid=9c3a51d81de07ddf1bfd9bbc70863b0f&type=default&t=1511315617645”,但是这种方式有时候行不通。因为有时候会发现当前的验证码和通过提取出来的url链接打开的验证码,内容是不一样的,其内容不断发生变化。

•第二,利用selenium先进行可视区域的截屏,然后定位验证码元素的位置以及大小,然后利用Image(PIL模块中)进行裁剪,得到验证码图片,然后送往验证码模块或者打码平台处理。

2. 环境

•python 3.6.1
•系统:win7
•IDE:pycharm
•安装过chrome浏览器
•配置好chromedriver
•selenium 3.7.0

3. 分析网页结构

selenium+python实现1688网站验证码图片的截取功能

通过分析网页源代码,我们可以得出以下结论:

•这个验证码登录框是通过iframe嵌入到网页中的。
•页面中不止这一个iframe嵌套。
•这个验证码iframe有很明显的特征:id=”sufei-dialog-content”和src=”https://sec.1688.com/query.htm?……”

<iframe id="sufei-dialog-content" frameborder="none" src="https://sec.1688.com/query.htm?style=mini&smApp=searchweb2&smPolicy=searchweb2-RpcAsyncAll-anti_Spider-checkcode&smCharset=GBK&smTag=MTIxLjE1LjI2LjIzMywzNTE1MTA4MjI5LGFlNGE1ZGI1YTQ4NDQ3NTNiYzY5OTZlZmU1OWE3Njhm&smReturn=https%3A%2F%2Fs.1688.com%2Fselloffer%2Frpc_async_render.jsonp%3Fkeywords%3D%25CF%25B4%25CD%25EB%25B2%25BC%26startIndex%3D0%26n%3Dy%26pageSize%3D60%26rpcflag%3Dnew%26async%3Dtrue%26templateConfigName%3DmarketOfferresult%26enableAsync%3Dtrue%26qrwRedirectEnabled%3Dfalse%26filterP4pIds%3D1245873517%252C561786598916%252C559726907082%252C523166432402%252C557139543735%252C529784793813%252C543923733444%252C560590249743%26asyncCount%3D20%26_pageName_%3Dmarket%26offset%3D9%26uniqfield%3Dpic_tag_id%26leftP4PIds%3D%26callback%3DjQuery18305735956012709345_1511341604992%26beginPage%3D48%26_%3D1511341615310&smSign=XKm5xSgAkIixvOkhV1VSyg%3D%3D" cd_frame_id_="c4ae94ef2bea60f0b4729f319df59251"></iframe>

4. 代码

# 前提是,在程序启动时,对浏览器窗口大小进行了设置
from selenium import webdriver
import time
from PIL import Image
browser = webdriver.Chrome()
# 根据桌面分辨率来定,主要是为了抓到验证码的截屏,验证码需要出现在可视区域中
browser.set_window_size(960, 960)
# 处理验证码弹窗
def captchaHandler(browser, DamatuInstance):
  iframeLst = browser.find_elements_by_tag_name('iframe')
  print(f"captchaHandler: enter , iframeLst = {iframeLst}")
  for iframe in iframeLst:
    iframeID = iframe.get_attribute('id')
    iframeSrc = iframe.get_attribute('src')
    print(f"captchaHandler: iframeID = {iframeID}, iframeSrc = {iframeSrc}")
    # 找到验证码登录iframe
    if iframeID and iframeID.find('dialog') != -1:
      if iframeSrc and iframeSrc.find(r'sec.1688.com') != -1:
        # 拿到iframe的宽度和高度
        frameWidth = iframe.size['width']
        frameHeight = iframe.size['height']
        # 代表验证码区域可见
        # 某些情况下,会出现验证码框不弹出,而iframe还在的暂态
        if frameWidth > 0 and frameHeight > 0:
          print(f"验证码弹出, 进行处理, frameWidth = {frameWidth}, frameHeight = {frameHeight}")
          # 截屏,在chrome中截取的是可视区域,而不是整个html页面
          # 前提是当前project下已经创建了clawerImgs目录
          browser.get_screenshot_as_file('clawerImgs/screenshot.png')
          # 先拿到iframe在整个可视页面(也就是上面的截屏)中的相对位置,因为前面对页面的窗口大小进行了设置960 X 960
          # location_once_scrolled_into_view 拿到的是相对于可视区域的坐标
          # location 拿到的是相对整个html页面的坐标
          frameX = int(iframe.location_once_scrolled_into_view['x'])
          frameY = int(iframe.location_once_scrolled_into_view['y'])
          print(f"captchaHandler: frameX = {frameX}, frameY = {frameY}, frameWidth = {frameWidth}, frameHeight = {frameHeight}")
          # 获取指定元素位置,先拿iframe元素的图片
          left = frameX
          top = frameY
          right = frameX + frameWidth
          bottom = frameY + frameHeight
          # 通过Image处理图像,截取frame的图片 ———— 无意义,只是做经验总结
          imgFrame = Image.open('clawerImgs/screenshot.png')
          imgFrame = imgFrame.crop((left, top, right, bottom)) # 裁剪
          imgFrame.save('clawerImgs/iframe.png')
          # 切换到验证码弹出框的frame,不然无法获取到验证码元素,因为验证码元素是在iframe中
          browser.switch_to.frame(iframe)
          # ------获取验证码图片,第一种方法:在frame区域截取
          # 获取指定元素位置
          captchaElem = browser.find_element_by_xpath("//img[contains(@id, 'CheckCodeImg')]")
          # 因为验证码在frame中没有缩放,直接取验证码图片的绝对坐标
          # 这个坐标是相对于它所属的frame的,而不是整个可视区域
          captchaX = int(captchaElem.location['x'])
          captchaY = int(captchaElem.location['y'])
          # 取验证码的宽度和高度
          captchaWidth = captchaElem.size['width']
          captchaHeight = captchaElem.size['height']
          captchaRight = captchaX + captchaWidth
          captchaBottom = captchaY + captchaHeight
          print(f"captchaHandler: 1 captchaX = {captchaX}, captchaY = {captchaY}, captchaWidth = {captchaWidth}, captchaHeight = {captchaHeight}")
          # 通过Image处理图像,第一种方法:在frame区域截取
          imgObject = Image.open('clawerImgs/iframe.png')
          imgCaptcha = imgObject.crop((captchaX, captchaY, captchaRight, captchaBottom))   # 裁剪
          imgCaptcha.save('clawerImgs/captcha1.png')
          # ------获取验证码图片,第二种方法:在整个可视区域截取。 就要加上这个iframe的便宜量
          captchaElem = browser.find_element_by_xpath("//img[contains(@id, 'CheckCodeImg')]")
          captchaX = int(captchaElem.location['x']) + frameX
          captchaY = int(captchaElem.location['y']) + frameY
          captchaWidth = captchaElem.size['width']
          captchaHeight = captchaElem.size['height']
          captchaRight = captchaX + captchaWidth
          captchaBottom = captchaY + captchaHeight
          print(f"captchaHandler: 2 captchaX = {captchaX}, captchaY = {captchaY}, captchaWidth = {captchaWidth}, captchaHeight = {captchaHeight}")
          # 通过Image处理图像,第二种方法:在整个可视区域截取
          imgObject = Image.open('clawerImgs/screenshot.png')
          imgCaptcha = imgObject.crop((captchaX, captchaY, captchaRight, captchaBottom))    # 裁剪
          imgCaptcha.save('clawerImgs/captcha2.png')

5. 结果展示

•整个可视区域:screenshot.png

selenium+python实现1688网站验证码图片的截取功能

•验证码登录框iframe区域:iframe.png

selenium+python实现1688网站验证码图片的截取功能

•相对于iframe截取的验证码图片:captcha1.png

selenium+python实现1688网站验证码图片的截取功能

•相对于整个可视区域截取的验证码图片:captcha2.png

selenium+python实现1688网站验证码图片的截取功能

6. 拓展

# 摘自https://www.cnblogs.com/my8100/p/7225408.html
chrome
  default:
    location 不滚动,直接返回相对整个html的坐标 {'x': 15.0, 'y': 129.0}
    location_once_scrolled_into_view 返回相对可视区域的坐标(改变浏览器高度,可以观察到底部元素底部对齐后y的变化)
      顶部/底部元素 完全可见不滚动,{u'x': 15, u'y': 60}
      顶部元素部分可见或完全不可见都会滚动到 顶部对齐 {u'x': 15, u'y': 0} account-wall
      底部元素部分可见或完全不可见都会滚动到 底部对齐 {u'x': 15, u'y': 594} theme-list
  frame:
    location 不滚动,直接返回相对frame即当前相应内层html的坐标{'x': 255.0, 'y': 167.0} captcha_frame 的 lc-refresh
    location_once_scrolled_into_view 返回相对可视区域的坐标
      完全可见不滚动{u'x': 273, u'y': 105}
      部分可见或完全不可见滚动到 顶部对齐 {u'x': 273, u'y': 0}
  firefox
    default:
      顶部元素 底部元素
        location 不滚动,直接返回相对整个html的坐标 {'x': 15.0, 'y': 130.0} {'x': 15.0, 'y': 707.0}
        location_once_scrolled_into_view 返回相对可视区域的坐标(y=1足以说明)
          可见不可见 都滚动到顶部对齐 {'x': 15.0, 'y': 1.0} {'x': 15.0, 'y': 1.0}
          如果下拉条直到底部,底部元素仍然无法顶部对齐 {'x': 15.0, 'y': 82.0}
    frame:
      location 不滚动,都是相对frame即当前相应html的坐标{'x': 255.0, 'y': 166.0}
      location_once_scrolled_into_view 可见不可见都会滚动到顶部对齐,('y'依旧是166.0)
        结果也是相对frame即当前相应html的坐标{'x': 255.0, 'y': 166.0}
# 总结
location 
  始终不滚动,返回相对整个html或者对应frame的坐标
location_once_scrolled_into_view
  chrome完全可见不滚动,firefox始终会滚动;而且chrome底部元素会底部对齐,其余情况两者都是顶部对齐。
  一般返回相对可视区域坐标,但是firefox的frame依旧返回相对frame的坐标
# 摘自:https://zhuanlan.zhihu.com/p/25171554
selenium.webdriver 内置了截取当前页面的功能,其中:
  a.WebDriver.Chrome自带的方法只能对当前窗口截屏,若是需要截取的窗口超过了一屏,就只能另辟蹊径了。
  b.WebDriver.PhantomJS自带的方法支持对整个网页截屏。

总结

以上所述是小编给大家介绍的selenium+python实现1688网站验证码图片的截取功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
Python的自动化部署模块Fabric的安装及使用指南
Jan 19 Python
python实现随机调用一个浏览器打开网页
Apr 21 Python
使用Python微信库itchat获得好友和群组已撤回的消息
Jun 24 Python
替换python字典中的key值方法
Jul 06 Python
Python中return self的用法详解
Jul 27 Python
python画图——实现在图上标注上具体数值的方法
Jul 08 Python
Java文件与类动手动脑实例详解
Nov 10 Python
python 链接sqlserver 写接口实例
Mar 11 Python
Python实现猜年龄游戏代码实例
Mar 25 Python
python 实现数据库中数据添加、查询与更新的示例代码
Dec 07 Python
Python中的套接字编程是什么?
Jun 21 Python
使用pd.merge表连接出现多余行的问题解决
Jun 16 Python
django+xadmin+djcelery实现后台管理定时任务
Aug 14 #Python
Python延时操作实现方法示例
Aug 14 #Python
详解PyCharm配置Anaconda的艰难心路历程
Aug 13 #Python
python 实现A*算法的示例代码
Aug 13 #Python
Python绘制KS曲线的实现方法
Aug 13 #Python
Python标准库shutil用法实例详解
Aug 13 #Python
详解windows python3.7安装numpy问题的解决方法
Aug 13 #Python
You might like
php+html5+ajax实现上传图片的方法
2016/05/14 PHP
创建无限极分类树型结构的简单方法
2017/06/20 PHP
PHP操作redis实现的分页列表,新增,删除功能封装类与用法示例
2018/08/04 PHP
asp 的 分词实现代码
2007/05/24 Javascript
自己的js工具_Form 封装
2009/08/21 Javascript
通过正则格式化url查询字符串实现代码
2012/12/28 Javascript
javascript中attribute和property的区别详解
2014/06/05 Javascript
JavaScript的RequireJS库入门指南
2015/07/01 Javascript
nodejs爬虫抓取数据乱码问题总结
2015/07/03 NodeJs
谈一谈bootstrap响应式布局
2016/05/23 Javascript
js style.display=block显示布局错乱问题的解决方法
2016/09/21 Javascript
angular源码学习第一篇 setupModuleLoader方法
2016/10/20 Javascript
利用JS实现点击按钮后图片自动切换的简单方法
2016/10/24 Javascript
jquery 多个radio的click事件实例
2016/12/03 Javascript
D3.js进阶系列之CSV表格文件的读取详解
2017/06/06 Javascript
基于vue-cli配置lib-flexible + rem实现移动端自适应
2017/12/26 Javascript
jQuery实现的简单拖拽功能示例【测试可用】
2018/08/14 jQuery
JS调用安卓手机摄像头扫描二维码
2018/10/16 Javascript
记录一次开发微信网页分享的步骤
2019/05/07 Javascript
[01:49]一目了然!DOTA2DotA快捷操作对比第二弹
2014/05/16 DOTA
[01:55]2014DOTA2国际邀请赛快报:国土生病 紧急去医院治疗
2014/07/10 DOTA
Python的Twisted框架上手前所必须了解的异步编程思想
2016/05/25 Python
Python Sqlite3以字典形式返回查询结果的实现方法
2016/10/03 Python
Python使用pip安装pySerial串口通讯模块
2018/04/20 Python
基于python实现名片管理系统
2018/11/30 Python
Python常用爬虫代码总结方便查询
2019/02/25 Python
Django框架教程之中间件MiddleWare浅析
2019/12/29 Python
Django rest framework分页接口实现原理解析
2020/08/21 Python
Python模拟登录requests.Session应用详解
2020/11/17 Python
Python的信号库Blinker用法详解
2020/12/31 Python
德国在线香料制造商:Gewürzland
2020/03/10 全球购物
人事助理岗位职责
2013/11/18 职场文书
服装区域经理岗位职责
2015/04/10 职场文书
机器人总动员观后感
2015/06/09 职场文书
食品卫生管理制度
2015/08/06 职场文书
Spring Boot 实现 WebSocket
2022/04/30 Java/Android