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求素数示例分享
Feb 16 Python
python实现快速排序的示例(二分法思想)
Mar 12 Python
Python基于百度AI的文字识别的示例
Apr 21 Python
使用sklearn之LabelEncoder将Label标准化的方法
Jul 11 Python
python 实现在tkinter中动态显示label图片的方法
Jun 13 Python
Pyqt QImage 与 np array 转换方法
Jun 27 Python
python实现比对美团接口返回数据和本地mongo数据是否一致示例
Aug 09 Python
python实现读取excel文件中所有sheet操作示例
Aug 09 Python
Python 共享变量加锁、释放详解
Aug 28 Python
python3 googletrans超时报错问题及翻译工具优化方案 附源码
Dec 23 Python
Python实现Hash算法
Mar 18 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不用内置函数对数组排序的两个算法代码
2010/02/08 PHP
thinkPHP3.2简单实现文件上传的方法
2016/05/16 PHP
php获取当前月与上个月月初及月末时间戳的方法
2016/12/05 PHP
PHP获取二叉树镜像的方法
2018/01/17 PHP
PHP实现Markdown文章上传到七牛图床的实例内容
2020/02/11 PHP
PHP中关于php.ini参数优化详解
2020/02/28 PHP
基于PHP+Mysql简单实现了图书购物车系统的实例详解
2020/08/06 PHP
HTML5如何适配 iPhone IOS 底部黑条
2021/03/09 HTML / CSS
JQuery 插件制作实践 xMarquee插件V1.0
2010/04/02 Javascript
JQuery的AJAX实现文件下载的小例子
2013/05/15 Javascript
Javascript this 关键字 详解
2014/10/22 Javascript
Jquery中$.post和$.ajax的用法小结
2015/04/28 Javascript
jQuery实现文本框邮箱输入自动补全效果
2015/11/17 Javascript
Javascript中的几种继承方式对比分析
2016/03/22 Javascript
jQuery中text() val()和html()的区别实例详解
2016/06/28 Javascript
详解JavaScript树结构
2017/01/09 Javascript
ajax接收后台数据在html页面显示
2017/02/19 Javascript
Vue.js仿Metronic高级表格(二)数据渲染
2017/04/19 Javascript
详解Vue.js之视图和数据的双向绑定(v-model)
2017/06/23 Javascript
基于iScroll实现下拉刷新和上滑加载效果
2017/07/18 Javascript
详解vue-meta如何让你更优雅的管理头部标签
2018/01/18 Javascript
如何写好一个vue组件,老夫的一年经验全在这了(推荐)
2019/05/18 Javascript
通过实例解析vuejs如何实现调试代码
2020/07/16 Javascript
[38:39]完美世界DOTA2联赛循环赛 IO vs GXR BO2第二场 11.04
2020/11/05 DOTA
Python中用format函数格式化字符串的用法
2015/04/08 Python
python使用wxPython打开并播放wav文件的方法
2015/04/24 Python
Python itertools模块详解
2015/05/09 Python
Python基于PycURL自动处理cookie的方法
2015/07/25 Python
Python super()函数使用及多重继承
2020/05/06 Python
一款纯css3实现的颜色渐变按钮的代码教程
2014/11/12 HTML / CSS
耐克波兰官方网站:Nike波兰
2019/09/03 全球购物
操行评语大全
2014/04/30 职场文书
义和团口号
2014/06/17 职场文书
检查机关领导群众路线教育实践活动个人整改措施
2014/10/28 职场文书
公司与个人合作协议书
2016/03/19 职场文书
正则表达式基础与常用验证表达式
2022/06/16 Javascript