接上篇
上篇的最后我贴出了一份代码,是测试用的代码。我们大概改一下,让它变得可以生产用。
import cv2
def locate():
img = cv2.imread('./src.jpg', 0)
re, img1 = cv2.threshold(img, 125, 255, 0)
contours, b = cv2.findContours(img1.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for j in range(0, len(contours) - 1):
M = cv2.moments(contours[j]) # 计算第一条轮廓的各阶矩,字典形式
try:
center_x = int(M["m10"] / M["m00"])
center_y = int(M["m01"] / M["m00"])
except:
continue
area = cv2.contourArea(contours[j])
if area < 6000 or area > 8000 or center_x < 500:
continue
return center_x
该代码用上篇的思路,识别同目录下的src.jpg。将分析结果的x坐标返回,即我们所需要移动的距离。
正文
现在我们新建一个文件,我给它命名为qzone.py,首先我们import可能需要用到的库和模块
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from location import locate
from selenium.webdriver.common.action_chains import ActionChains
import time
import requests
import random
然后我们定义第一个函数,即打开无头浏览器。
def loginBefore(qq, pwd):
opt = Options()
opt.add_argument('--headless')
opt.add_argument('log-level=3')
driver = webdriver.Chrome(chrome_options=opt)
try:
cookie = login(qq, pwd, driver)
except:
pass
finally:
driver.quit()
return cookie
前三行为配置参数,第四行打开浏览器。
在执行完login函数后,退出driver并且返回cookie。
然后咱们来看login函数:
def login(qq, pwd, driver, con=1):
# 打开网页
openAndWait(qq, pwd, driver)
# 保存图片
save_pic(driver)
# 判断位置
x = locate()
# 转换为实际长度 /1.954
xBegin = 119.194
try:
distance = x - xBegin
except:
print('未捕捉到拼图位置,重新载入页面')
login(qq, pwd, driver, con)
else:
distance = distance / 1.72
# 模拟鼠标移动
return move(distance, qq, pwd, driver, con)
注释写的比较完整,我唯一要说的一点是xBegin是拼图碎片的初始位置距离图片左侧边界的距离。
当图片缺口捕捉失败时,我们递归执行login函数重新加载网页。
距离捕捉完成后,由于浏览器的缩放等原因,我们需要重新调整distance,即distance = distance / 1.72
。后面的1.72需要你慢慢调整到一个适合你自己的值。
随后我们便可以开始调用move函数来进行模拟移动操作。
def move(distance, qq, pwd, driver, con=1):
for n in range(3):
track = get_track(distance)
url_login = driver.current_url
slider = driver.find_element_by_xpath('/html/body/div[1]/div[3]/div[2]/div[2]/div[2]/div[1]')
ActionChains(driver).click_and_hold(slider).perform()
time.sleep(1)
i = 0
for x in track:
y = random.choice([-2, -1, 0, 1, 2])
ActionChains(driver).move_by_offset(xoffset=x, yoffset=y).perform()
t = random.choice([0.007, 0.008, 0.009, 0.010, 0.011])
if i < 20:
time.sleep(t * 10)
else:
time.sleep(t)
i += 1
time.sleep(1)
ActionChains(driver).release(on_element=slider).perform()
for j in range(5):
url_now = driver.current_url
if url_now != url_login:
print('登录成功,返回cookies')
cookies = driver.get_cookies()
# driver.quit()
return cookies
time.sleep(1)
if n != 2: print('尝试失败,正在进行再次尝试')
print('多次失败,重新载入页面')
if con >= 3:
print('多次登录失败,程序停止')
return 'more'
login(qq, pwd, driver, con + 1)
track = get_track(distance)
语句通过get_track函数获得步长列表,我们通过使用这个随机的步长列表来模拟人的拖拽动作。
首先记录当前的url,用于以后判断是否登录成功。然后我们捕捉到拼图碎片并且点住它ActionChains(driver).click_and_hold(slider).perform()
。
随后,读取track列表,随机一个纵向的移动距离,进行一小格移动。然后随机一个休息时间再进行下一个track的移动。
其中
if i < 20:
time.sleep(t * 10)
else:
time.sleep(t)
i += 1
用来实现先慢后快的拖拽。
拖拽完成后,ActionChains(driver).release(on_element=slider).perform()
来松开鼠标。
后面我们通过检测url判断是否成功登录,如果成功则返回cookie,失败则进行几次尝试。尝试次数过多后返回错误信息。
最后我们看到生成track的函数
def get_track(distance):
# 移动轨迹
track = []
# 当前位移
current = 0
# 减速阈值
mid = distance * 4 / 5
# 计算间隔
t = 0.15
# 初速度
v = 1
r = [0.9, 0.95, 0.975, 1, 1.025, 1.05, 1.1]
i = 0
while current < distance:
if current < mid:
a = 1
else:
a = -3.5
v0 = v
v = v0 + a * t
r1 = random.choice(r)
move = v * t * r1
current += move
track.append(move)
if distance - current <= move:
track.append(distance - current)
return track
i = i + 1
return track
同样,也是随机,具体我也就不在讲述了。
至此,通过selenium来模拟鼠标通过qq空间拼图验证码也算是成功完成。
后话
首先确实感谢几位朋友的赞扬,本来写上篇时因为太耗费时间所以一直没动力写续集。前几天倒腾个人博客偶然想起自己还有个csdn,登录上来居然看到了2个评论呜呜呜真是眼眶都红了。
不过上次详细讲述了思路过程,虽然可能对于喜欢仔细阅读的朋友可以有很大的连贯性,但是确实非常耗费精力,再加上时隔已久我确实也想不清自己的思路了哈哈哈哈。所以这次采用了源码讲解的方式,希望这样也可以给大家提供些许帮助吧。
以及,因为整个项目我是用flask部署到服务器上的,所以其实还有第三个文件我没有讲述——基于flask框架的qq空间自动点赞后端程序。
为了避免填一坑挖一坑的恶性循环,我会整理下我的代码全部放到porn啊不是,全部放到github上。地址在这里:qq空间自动点赞
此外,如果有机会我会再写这个项目的第三篇——基于flask框架的qq空间自动点赞后端程序