import time
import re
import requests
from io import BytesIO
from PIL import Image
import random
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
class Geek_Huxiu(object):
def __init__(self):
self.driver = webdriver.Chrome(r'F:Python视屏10爬虫包chromedriver_win32chromedriver.exe')
self.driver.set_window_size(1366, 768)
def run(self):
self.driver.get("https://www.huxiu.com/") # 打开浏览器
WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, '//*[@class="js-register"]')))
login_btn = self.driver.find_element_by_xpath('//*[@class="js-register"]')
login_btn.click()
WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, '//div[@class="gt_slider_knob gt_show"]')))
# 模拟拖动
self.analog_drag()
def analog_drag(self):
# 鼠标移动到拖动按钮,显示出拖动图片
element = self.driver.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
ActionChains(self.driver).move_to_element(element).perform()
time.sleep(3)
# 刷新一下极验证图片
element = self.driver.find_element_by_xpath('//a[@class="gt_refresh_button"]')
element.click()
time.sleep(1)
# 获取图片地址和位置坐标列表
cut_image_url, cut_location = self.get_image_url('//div[@class="gt_cut_bg_slice"]')
full_image_url, full_location = self.get_image_url('//div[@class="gt_cut_fullbg_slice"]')
# print(cut_image_url)
# print(cut_location)
# 根据坐标拼接图片
cut_image = self.splicing_image(cut_image_url, cut_location)
full_image = self.splicing_image(full_image_url, full_location)
# 将图片存储到本地
cut_image.save("cut.jpg")
full_image.save("full.jpg")
# 根据两个图片计算距离
distance = self.get_distance(cut_image, full_image)
# 开始移动
self.start_move(distance)
# 如果出现error
try:
WebDriverWait(self.driver, 5, 0.5).until(
EC.element_to_be_clickable((By.XPATH, '//div[@class="gt_ajax_tip gt_error"]')))
print("验证失败")
return
except TimeoutException as e:
pass
# 判断是否验证成功
try:
WebDriverWait(self.driver, 10, 0.5).until(
EC.element_to_be_clickable((By.XPATH, '//div[@class="gt_ajax_tip gt_success"]')))
except TimeoutException:
print("again times")
time.sleep(3)
# 失败后递归执行拖动
self.analog_drag()
else:
print("验证成功")
def get_image_url(self, xpath):
link = re.compile('background-image: url("(.*?)"); background-position: (.*?)px (.*?)px;') # 为了获取url x坐标 y坐标
elements = self.driver.find_elements_by_xpath(xpath) # 获取所有图片的标签
image_url = None
location = list() # 创建空列表
for element in elements:
style = element.get_attribute('style')
groups = link.search(style) # 正则匹配 style中内容
url = groups[1] # 获取url
x_pos = groups[2] # 获取x
y_pos = groups[3] # 获取y
location.append((int(x_pos), int(y_pos))) # (x,y) 坐标 t添加到列表
if not image_url: # 不存在 image_url时候
image_url = url # image_url = url
return image_url, location # 返回一个图片url 坐标列表
def splicing_image(self, image_url, location):
res = requests.get(image_url)
file = BytesIO(res.content)
img = Image.open(file)
image_upper = []
image_down = []
for pos in location:
if pos[1] == 0:
# y值为0的坐标 属于图片上半部分,高度58
image_upper.append(img.crop((abs(pos[0]), 0, abs(pos[0]) + 10, 58)))
else:
# y值为58的坐标 属于图片下半部分,高度58
image_down.append(img.crop((abs(pos[0]), 58, abs(pos[0]) + 10, img.height)))
# 画布的x轴偏移量
x_offset = 0
# 创建一张画布
# print(image_upper)
# print('- -' * 30)
# print(image_down)
new_img = Image.new("RGB", (260, img.height))
for img in image_upper:
new_img.paste(img, (x_offset, 58))
print(img)
x_offset += img.width
x_offset = 0
for img in image_down:
new_img.paste(img, (x_offset, 0))
x_offset += img.width
return new_img
def get_distance(self, cut_image, full_image):
# print(cut_image.size)
threshold = 50
for i in range(0, cut_image.size[0]):
for j in range(0, cut_image.size[1]):
pixel1 = cut_image.getpixel((i, j))
pixel2 = full_image.getpixel((i, j))
res_R = abs(pixel1[0] - pixel2[0]) # 计算RGB差
res_G = abs(pixel1[1] - pixel2[1]) # 计算RGB差
res_B = abs(pixel1[2] - pixel2[2]) # 计算RGB差
if res_R > threshold and res_G > threshold and res_B > threshold:
return i # 需要移动的距离
# 移动滑块
def start_move(self, distance):
element = self.driver.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
# 使用滑块的一半进行偏移设置
distance -= element.size.get('width') / 2
distance += 15
# 按下鼠标左键
ActionChains(self.driver).click_and_hold(element).perform()
time.sleep(0.5)
while distance > 0:
if distance > 20:
# 如果距离大于20,就让他移动快一点
span = random.randint(5, 8)
else:
# 快到缺口了,就移动慢一点
span = random.randint(2, 3)
ActionChains(self.driver).move_by_offset(span, 0).perform()
distance -= span
time.sleep(random.randint(10, 60) / 100)
ActionChains(self.driver).move_by_offset(distance, 1).perform()
ActionChains(self.driver).release(on_element=element).perform()
if __name__ == '__main__':
h = Geek_Huxiu()
h.run()