-
隐藏自动化标识:通过排除
enable-automation
启动开关和修改navigator.webdriver
属性,使得Chrome浏览器在自动化控制时更难以被网站检测出来。这有助于绕过一些网站的反爬虫策略。 -
使用CDP命令:CDP(Chrome DevTools Protocol)是一个用于与Chrome浏览器进行通信的协议。
execute_cdp_cmd
方法允许开发者执行CDP命令,这里用于在每个新打开的页面文档上添加一段JavaScript代码。 -
修改
navigator.webdriver
属性:通过Object.defineProperty
方法,重新定义navigator.webdriver
属性的getter方法,使其返回undefined
。这样,当网站尝试检查navigator.webdriver
属性时,将不会返回任何值,从而隐藏了自动化控制的痕迹。 -
登录操作:代码模拟了用户在网页上的登录过程。它首先定位到用户名和密码输入框,分别清除了可能存在的默认文本或之前输入的内容,然后输入了指定的用户名和密码,最后点击了登录按钮。(换成你们自己的京东账号)
-
页面加载等待:通过使用
time.sleep
和implicitly_wait
,代码确保了页面和元素有足够的时间加载。这对于自动化测试或爬虫来说非常重要,因为网络延迟或页面加载缓慢可能导致元素尚未加载完成就被代码尝试访问,从而导致错误。(网络好的话可以优化一下time.sleep) -
准备下载文件:代码设定了两个文件名
bg_filename
和tp_filename
,这两个变量很可能用于后续的图片下载操作。 -
写for循环:这里for循环次数我定义了3次,可根据需要更改。通过for循环,先判断是否获取得到用户名称,使用try,except,如果找不到该元素,就报错并且运行过验证码代码。
-
过程:调用dow_base下载图片,然后使用resize_and_save_image更改图片的大小,让图片与浏览器验证码图片大小一致(由于下载的图片大小与浏览器图片大小不一样,会导致图片缺口识别距离不一样),调用identify_gap,获取保存好的图片进行缺口识别,通过变量获取到缺口的距离后创建动作链,将获取到的缺口距离进行求平均数(目前分成14次不容易给风控),执行动作链。
import re
import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from 基本操作.PIL图片大小设置 import resize_and_save_image
from 基本操作.下载base64图片 import dow_base
from 基本操作.缺口识别 import identify_gap
url = 'https://passport.jd.com/new/login.aspx?ReturnUrl=https%3A%2F%2Fre.jd.com%2Fsearch' \
'%3Fkeyword%3D%25e4%25ba%25ac%25e4%25b8%259c%25e5%2595%2586%25e5%259f%258e%25e7%25bd%2591' \
'%25e4%25b8%258a%25e8%25b4%25ad%25e7%2589%25a9%25e5%25ae%25b6%25e7%2594%25b5%25e5%2586%25b0' \
'%25e7%25ae%25b1%26keywordid%3D43620911346%26re_dcp%3D21Sm2D2ZOw%26traffic_source%3D1004' \
'%26test%3D1%26enc%3Dutf8%26cu%3Dtrue%26utm_source%3Dhaosou-search%26utm_medium%3Dcpc' \
'%26utm_campaign%3Dt_262767352_haosousearch%26utm_term' \
'%3D43620911346_0_8ec077c88b154b7584c370ff4aa065fe '
# 创建一个ChromeOptions对象,用于配置Chrome浏览器的启动参数
options = ChromeOptions()
# 添加一个实验性的选项,排除某些启动开关,这里排除了'enable-automation'开关
# 排除这个开关可以使浏览器在启动时不显示自动化控制的标识,有助于绕过某些网站的反爬虫机制
options.add_experimental_option('excludeSwitches', ['enable-automation'])
# 使用配置好的options来初始化Chrome浏览器驱动
driver = webdriver.Chrome(options=options)
# 执行Chrome DevTools Protocol (CDP) 命令,这个命令会在每个新打开的文档上执行一段JavaScript代码
# 这段JavaScript代码的目的是重新定义window.navigator.webdriver对象的getter方法
# 将其getter方法设置为返回undefined,这意味着当网站尝试检查navigator.webdriver属性时
# 它将无法得到任何值(undefined),从而隐藏了自动化控制的痕迹
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',
{
'source': 'Object.defineProperty(navigator, "webdriver",{get:() => undefined})'
})
driver.get(url)
# 填写登录信息
time.sleep(3)
driver.find_element(By.ID, 'loginname').clear()
driver.find_element(By.ID, 'loginname').send_keys('自己的账号')
time.sleep(0.5)
driver.find_element(By.ID, 'nloginpwd').clear()
driver.find_element(By.ID, 'nloginpwd').send_keys('自己的密码')
driver.implicitly_wait(3)
driver.find_element(By.ID, 'loginsubmit').click()
time.sleep(5)
# 下载图片
# 图片文件名(确保它们是唯一的,并且带上扩展名)
bg_filename = 'background.jpg'
tp_filename = 'piece.jpg'
# 尝试次数
attempts = 3
for i in range(attempts):
try:
# 尝试获取昵称元素的文本
name = driver.find_element(By.CLASS_NAME, 'nickname').text
print(f'{name}-用户登录成功')
# 如果找到了昵称,则关闭浏览器并退出循环
driver.quit()
break
except NoSuchElementException:
print('未找到昵称元素,尝试第', i + 1, '次')
# 获取图片src
bg_img = driver.find_element(By.XPATH,
'//*[@id="JDJRV-wrap-loginsubmit"]/div/div/div/div[1]/div[2]/div['
'1]/img').get_attribute('src')
tp_img = driver.find_element(By.XPATH,
'//*[@id="JDJRV-wrap-loginsubmit"]/div/div/div/div[1]/div[2]/div['
'2]/img').get_attribute('src')
# 清洗base64
bg_img = re.split(',', bg_img)[1]
tp_img = re.split(',', tp_img)[1]
# 调用函数保存图片
dow_base(bg_img, bg_filename)
dow_base(tp_img, tp_filename)
# 改变图片大小
resize_and_save_image('./photo/background.jpg', './resized_photos', 'background_new.jpg',
242, 94)
resize_and_save_image('./photo/piece.jpg', './resized_photos', 'piece_new.jpg', 33, 33)
# 分析滑块距离
total_distance = identify_gap()
print('缺口x距离:', total_distance)
# 获取滑块元素
hk = driver.find_element(By.XPATH,
'//*[@id="JDJRV-wrap-loginsubmit"]/div/div/div/div[2]/div[3]')
# 创建动作链并执行滑动操作
action = ActionChains(driver)
action.click_and_hold(hk).perform()
time.sleep(2) # 等待点击和保持生效
# 接下来执行滑动逻辑...
# 这里添加您自己的滑动逻辑代码
# 每次滑动的距离
step_size = int(total_distance) / 14
print('每次滑动的距离:', step_size)
# 滑动的间隔时间
interval = 1
for j in range(14):
if j <= 6:
action.move_by_offset(xoffset=step_size - 1, yoffset=2).perform()
# action.pause(interval)
elif 6 < j <= 12:
action.move_by_offset(xoffset=step_size + 1, yoffset=1).perform()
# action.pause(2)
else:
action.move_by_offset(xoffset=step_size + 1.5, yoffset=1).perform()
action.release().perform() # 释放鼠标
# 等待一段时间再进行下一次尝试
time.sleep(5)
# 如果循环结束后仍未找到昵称元素,则退出浏览器
if i == attempts - 1:
driver.quit()
print('尝试了', attempts, '次,仍未找到昵称元素,退出浏览器。')
封装了三个函数:分别是
1.resize_and_save_image(PIL图片大小设置),为了改变验证码图片大小的问题
from PIL import Image
import os
def resize_and_save_image(image_path, output_directory, output_filename, target_width,
target_height):
# 确保输出目录存在
if not os.path.exists(output_directory):
os.makedirs(output_directory)
# 打开图像
image = Image.open(image_path)
# 如果图像是RGBA模式,转换为RGB模式
if image.mode == 'RGBA':
image = image.convert('RGB')
# 调整图像尺寸
new_img = image.resize((target_width, target_height))
# 构建完整的输出文件路径
output_path = os.path.join(output_directory, output_filename)
# 保存为JPEG格式
new_img.save(output_path)
2.dow_base(下载base64图片),普通的图片直接请求就行了,不过京东的图片是base64,需要解密
import base64
def dow_base(base64_encoded_image, name):
# 确保目录存在,如果不存在则创建它
import os
directory = './photo/'
if not os.path.exists(directory):
os.makedirs(directory)
# 解码Base64数据
imagedata = base64.b64decode(base64_encoded_image)
# 将解码后的数据写入文件
with open(f'{directory}{name}', "wb") as file:
file.write(imagedata)
print(f'图片已保存为:{directory}{name}')
3.identify_gap(缺口识别),使用cv2
import time
import cv2
def identify_gap():
print('开始识别时间', time.asctime())
# 读取背景图片和缺口图片
bg_img = cv2.imread('./resized_photos/background_new.jpg') # 背景图片
tp_img = cv2.imread('./resized_photos/piece_new.jpg') # 缺口图片
# 确保图片加载成功
if bg_img is None or tp_img is None:
print("Error: 图片未正确加载。请检查文件路径和文件完整性。")
return None
# 识别图片边缘
bg_edge = cv2.Canny(bg_img, 100, 200) # 修正拼写错误
tp_edge = cv2.Canny(tp_img, 100, 200) # 修正拼写错误
# 缺口匹配
# 如果 tp_edge 太大,您可能需要先对其进行缩放
res = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
# 绘制方框
th, tw = tp_edge.shape[:2]
top_left = max_loc # 左上角点的坐标,使用英文命名
bottom_right = (top_left[0] + tw, top_left[1] + th) # 右下角点的坐标
cv2.rectangle(bg_img, top_left, bottom_right, (0, 0, 255), 2) # 绘制矩形
# 保存图片到本地
cv2.imwrite('./resized_photos/output_with_rectangle.jpg', bg_img)
print('结束时间', time.asctime())
# 返回缺口的X坐标
return top_left[0]