一、实验名称
python环境下browsermobproxy+selenium实现豆瓣模拟登录。
二、基本原理
(一)概述
? 使用selenium模拟打开页面、输入账户密码、点击、滑动等操作,browsermobproxy设置代理抓取包含滑动验证码图像的请求。实验中难点在于滑动验证码的破解。
(二)browsermobproxy
? BrowserMob Proxy允许操作HTTP请求和响应,捕获HTTP内容,并将性能数据导出为HAR文件。按下列教程安装好后即可运行。(https://blog.csdn.net/qq_44315987/article/details/116501955)
? 如下所示例子中result存储了webdriver访问时的请求。
from selenium import webdriver
from browsermobproxy import Server
server = Server(r'D:\python_env\exper4\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat')
server.start()
proxy = server.create_proxy()
print('proxy', proxy.proxy)
chrome_options = Options()
chrome_options.add_argument('--proxy-server={0}'.format(proxy.proxy))
proxy.new_har("douban", options={'captureHeaders': True, 'captureContent': True})
browser.get('https://accounts.douban.com/passport/login_popup?login_source=anony')
result = proxy.har
使用json格式化工具可以查看每一条请求的url。
从目标url中可以抓取验证图片。这样处理是因为直接通过元素查找不能找到所需图片。
(三)查找缺口位置
? 获取到无缺口的原图、带缺口的图片后,我们需要定位缺口的位置。由于图片中存在一些干扰因素(如下方两张灰度图的上面一张图的上边缘依稀可以看到一些白色的噪点,也就是说两张图不同的部分不止缺口所在位置),直接通过单个像素比较不能准确找到缺口的位置。
? 下面时我借助卷积神经网络的思想实现的一种图片比较方法。
? 将两个灰度图像对应的矩阵进行相减,同时采取极端的阈值(设为1)将所得图像进行二值化,得到如下图像(白色部分灰度值为255,黑色部分灰度值为0)。
然后用一个大小与拼图块相同,元素全为1的卷积核对图像进行类似卷积的操作(即分块求和)。
core_array = np.zeros((height,width))
step_size = 5
for i in range(seghalf,height-seghalf, step_size):
for j in range(seghalf,width-seghalf, step_size):
temp_array = minus_pic[i-seghalf:i+seghalf,j-seghalf:j+seghalf]
core_array[i][j] = np.sum(temp_array)
? 所得结果core_array中最大元素所在位置即为拼图的中心。
(四)滑动操作
? 使用坐标工具对相关位置进行定位,调用Selenium相关鼠标事件即可完成操作。
? 滑动不能过于僵硬,需要模拟人手动滑动过程。
def get_track(distance):
"""
根据偏移量获取移动轨迹
:param distance: 偏移量
:return: 移动轨迹
"""
track = []
current = 0
mid = distance * 4 / 5
t = 0.2
v = 0
while current < distance:
if current < mid:
a = 2
else:
a = -1
v0 = v
v = v0 + a * t
move = v0 * t + 1 / 2 * a * t * t
current += move
track.append(round(move))
return track
(五)防检测
? 这是后面发现的问题,selenium默认情况下会出现表单无法提交的情况。
? 因此也需要设置一些参数将自己隐藏起来,如打开谷歌开发者模式,去取webdriver标识。
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
打开开发者模式后,标签栏附近的自动测试提示也消失了。
三、具体步骤
1. 导入相关库
import re
import cv2
import time
import requests
import numpy as np
from selenium import webdriver
from browsermobproxy import Server
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
2.配置代理
server = Server(r'D:\python_env\exper4\browsermob-proxy-2.1.4\bin\browsermob-proxy.bat')
server.start()
proxy = server.create_proxy()
print('proxy', proxy.proxy)
chrome_options = Options()
chrome_options.add_argument('--proxy-server={0}'.format(proxy.proxy))
proxy.new_har("douban", options={'captureHeaders': True, 'captureContent': True})
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
3.打开浏览器访问登录页面
browser = webdriver.Chrome(chrome_options=chrome_options)
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
browser.get('https://accounts.douban.com/passport/login_popup?login_source=anony')
browser.maximize_window()
time.sleep(1)
browser.find_elements(by=By.XPATH,value='//li[@class="account-tab-account"]')[0].click()
time.sleep(1)
username = ''
password = ''
browser.find_elements(by=By.XPATH,value='//input[@class="account-form-input"]')[0].send_keys(username)
browser.find_elements(by=By.XPATH,value='//input[@class="account-form-input password"]')[0].send_keys(password)
browser.find_elements(by=By.XPATH,value='//div[@class="account-form-field-submit "]')[0].click()
time.sleep(sleep_time)
result = proxy.har
pic_url = None
for entry in result['log']['entries']:
_url = entry['request']['url']
if "hycdn?" in _url:
pic_url = _url
print(pic_url)
break
if pic_url is None:
print('no_data')
sleep_time += 1
browser.close()
continue
server.stop()
成功输入账号密码,打开验证码界面:
同时获得pic_url如下:
https://t.captcha.qq.com/hycdn?index=1&image=937625773573401088?aid=2044348370&sess=s0BPJKOhJ6Ui0xiAFkP8g9qIYCeMDfa0j2uQUgwwb0AT-dzDfRcdgckFzZL0EeTHhTKk8cCf0pHiyGsz2es72seJrNody8vV65ESofx0b2w8Y4WVkARtWmUjanuJjIoyMK2tYrqfU1A9mYcf7Ekv64ycewssZ_RW-nYJfiWuG6wkqqZ50ZcfrLuDaxsXDVkff1BUkUOV2HBjTRyGbVO08Gy7azDs8_sBJI7_KVuDQWoL8zuJVl6sDw_WkXL-5h3XnNirWtKA0Yf5tRT0MfAOW0fMXmbuCGNJ5ljszCluWuHJXxUME_O9R59g
4.将验证图片保存到本地
? 使用正则表达式对url关键位置字符进行替换得到三张图的url。使用requests库访问后将返回值保存到本地。
origin_pic = re.sub('img_index=\d&','img_index=0&',pic_url)
unfull_pic = re.sub('img_index=\d&','img_index=1&',pic_url)
seg_pic = re.sub('img_index=\d&','img_index=2&',pic_url)
response = requests.get(origin_pic)
with open('origin.jpg','wb') as f:
f.write(response.content)
response = requests.get(unfull_pic)
with open('unfull.jpg','wb') as f:
f.write(response.content)
response = requests.get(seg_pic)
with open('seg.jpg','wb') as f:
f.write(response.content)
5.查找缺口位置
结果如下所示(上方为图片尺寸,下方为缺口位置):
与二图相减并二值化处理的图像进行对比,发现几乎一致。
6.根据坐标对滑块进行拖动
截图快捷键捕捉不到成功的瞬间,就用手机相机拍摄图片了。
可以看到验证通过,精准无敌了!
此外尝试登陆次数过多时官网也会对帐号进行检测,需下载App扫码登陆,最近好像一直不行(可能这就是它的营销策略吧)。
四、完整代码
加入简单的异常处理后总结完整代码如下
import reimport cv2import timeimport requestsimport numpy as npfrom selenium import webdriverfrom browsermobproxy import Serverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.chrome.options import Options
五、实验感受
? 1.实验中验证图片不能直接通过查找元素得到,需要拦截请求做出相应操作,browsermobproxy可以很好地解决这个问题。
? 2.图片比较过程中单纯比较像素点的方法不是很有效,参考卷积神经网络思想使用卷积核对图像进行分块求和可以以相对较高的效率地查找到两张图片的不同区域位置。
? 3.在网页通过坐标进行鼠标事件操作是个令人头疼的问题,出于显示设置及坐标工具显示问题,在选取过程中一般需要通过css显示尺寸和所量得的尺寸进行一个等比缩放。
? 4.即使使用selenium也有被检测的可能。
? 5.打码服务贵有贵的理由。
|