爲了寫這篇文章,先寫了兩篇爬蟲cookies詳解和selenium+requests進行cookies保存讀取操做,感興趣的朋友能夠看看前兩篇文章。html
這篇文章我主要是提供另外一種滑動驗證碼的處理方式,看過我文章的朋友應該知道那篇極驗驗證碼破解之selenium,在那篇文章中咱們經過分析元素中的圖片信息拼接完整圖片和缺口圖片,而後經過像素對比計算移動距離,使用selenium模擬拖動完成驗證。python
在上一篇極驗驗證碼破解的文章中,咱們能找到圖片拼接信息還原原來的圖片,可是後來我發如今不少網站中極驗驗證碼的顯示都是使用canvas進行渲染的,在網頁元素中是找不到圖片信息的,例如咱們要說的博客園登陸web
那麼針對這種方式咱們怎麼獲取圖片進行缺口計算呢?很簡單,截圖chrome
這是彈出框顯示的圖片json
這是點擊拖動按鈕顯示的圖片canvas
那麼咱們只要把這兩塊圖片截下來,而後把滑塊部分過濾掉,其餘部分進行像素對比,便可獲取拖動距離。使用selenium進行截圖保存很方便,可是要注意不一樣的瀏覽器截圖方式不一樣,若是使用Firefox瀏覽器,能夠直接獲取圖片元素,進行元素截圖;若是使用chrome瀏覽器,此功能有BUG,咱們能夠進行瀏覽器截屏,而後把整個圖片中圖像部分進行裁剪處理,獲得全圖和缺陷圖。瀏覽器
使用get_screenshot_as_file(filename)接口,將登陸頁面截圖保存下來,而後獲取canvas元素微信
獲得x、y座標和大小cookie
left = element.location.get("x") top = element.location.get("y") right = left + element.size.get("width") bottom = top + element.size.get("height")
使用Image庫打開保存的截圖文件,而後使用crop函數進行截圖,再使用灰度處理(灰度處理主要是爲了減小像素點的處理,不是必須的)session
# -*- coding: utf-8 -*- import random import time from PIL import Image from selenium import webdriver 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 CNBlogSelenium(object): def __init__(self): opt = webdriver.ChromeOptions() # 設置無頭模式,調試的時候能夠註釋這句 # opt.set_headless() self.driver = webdriver.Chrome(executable_path=r"/usr1/webdrivers/chromedriver", chrome_options=opt) self.driver.set_window_size(1440, 900) def visit_login(self): try: self.driver.get("https://passport.cnblogs.com/user/signin") WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="input1"]'))) username = self.driver.find_element_by_xpath('//*[@id="input1"]') username.clear() username.send_keys("帳號") WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="input2"]'))) password = self.driver.find_element_by_xpath('//*[@id="input2"]') password.clear() password.send_keys("密碼") WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//*[@id="signin"]'))) signin = self.driver.find_element_by_xpath('//*[@id="signin"]') signin.click() WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//*[@class="geetest_radar_tip_content"]'))) geetest = self.driver.find_element_by_xpath('//*[@class="geetest_radar_tip_content"]') geetest.click() #點擊滑動驗證碼後加載圖片須要時間 time.sleep(3) self.analog_move() except : pass self.driver.quit() # 截圖處理 def screenshot_processing(self): WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable( (By.XPATH, '//canvas[@class="geetest_canvas_fullbg geetest_fade geetest_absolute"]'))) element = self.driver.find_element_by_xpath( '//canvas[@class="geetest_canvas_fullbg geetest_fade geetest_absolute"]') # 保存登陸頁面截圖 self.driver.get_screenshot_as_file("login.png") image = Image.open("login.png") # 打開截圖,獲取element的座標和大小 left = element.location.get("x") top = element.location.get("y") right = left + element.size.get("width") bottom = top + element.size.get("height") # 對此區域進行截圖,而後灰度處理 cropImg = image.crop((left, top, right, bottom)) full_Img = cropImg.convert("L") full_Img.save("fullimage.png") WebDriverWait(self.driver, 10, 0.5).until( EC.element_to_be_clickable((By.XPATH, '//*[@class="geetest_slider_button"]'))) move_btn = self.driver.find_element_by_xpath('//*[@class="geetest_slider_button"]') ActionChains(self.driver).move_to_element(move_btn).click_and_hold(move_btn).perform() WebDriverWait(self.driver, 10, 0.5).until( EC.element_to_be_clickable((By.XPATH, '//canvas[@class="geetest_canvas_slice geetest_absolute"]'))) element = self.driver.find_element_by_xpath('//canvas[@class="geetest_canvas_slice geetest_absolute"]') self.driver.get_screenshot_as_file("login.png") image = Image.open("login.png") left = element.location.get("x") top = element.location.get("y") right = left + element.size.get("width") bottom = top + element.size.get("height") cropImg = image.crop((left, top, right, bottom)) cut_Img = cropImg.convert("L") cut_Img.save("cutimage.png")
經過觀察圖片咱們發現每一個缺口圖片的都是處於最左側,即最左側部分爲滑塊,無需進行像素對比,對滑動塊進行截圖查看,寬度基本在60像素左右,咱們能夠直接越過前面這部分,可是保險起見我仍是從開始進行像素計算,在獲得第一個不一樣像素後,向後加+60像素,繼續進行像素對比。
def calc_cut_offset(self, cut_img, full_img): x, y = 1, 1 find_one = False top = 0 left = 0 right = 0 while x < cut_img.width: y = 1 while y < cut_img.height: cpx = cut_img.getpixel((x, y)) fpx = full_img.getpixel((x, y)) if abs(cpx - fpx) > 50: if not find_one: find_one = True x += 60 y -= 10 continue else: if left == 0: left = x top = y right = x break y += 1 x += 1 return left, right - left
這裏的移動處理同極驗驗證碼破解之selenium中同樣,具體解釋能夠查看上篇文章
def start_move(self, distance, element, click_hold=False): # 這裏就是根據移動進行調試,計算出來的位置不是百分百正確的,加上一點偏移 distance -= 7 print(distance) # 按下鼠標左鍵 if click_hold: ActionChains(self.driver).click_and_hold(element).perform() while distance > 0: if distance > 10: # 若是距離大於10,就讓他移動快一點 span = random.randint(5, 8) else: time.sleep(random.randint(10, 50) / 100) # 快到缺口了,就移動慢一點 span = random.randint(2, 3) ActionChains(self.driver).move_by_offset(span, 0).perform() distance -= span ActionChains(self.driver).move_by_offset(distance, 1).perform() ActionChains(self.driver).release(on_element=element).perform()
移動處理這裏識別率不是很高,當咱們移動失敗後,要進行重試,若是驗證成功後面提示顯示登陸成功,咱們經過查看tip_btn元素的文本信息便可
進行屢次嘗試之後,拖動框會消失,點觸式按鈕顯示點擊重試,咱們一樣檢測點觸式按鈕上是否顯示點擊重試字樣,若是存在就執行一次點擊事件
在進行極驗驗證碼處理的過程當中必定要進行失敗重試的處理,由於咱們很難作到百分百驗證成功。
# 判斷是否登陸成功 tip_btn = self.driver.find_element_by_xpath('//*[@id="tip_btn"]') if tip_btn.text.find("登陸成功") == -1: try: WebDriverWait(self.driver, 3, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//*[@class="geetest_reset_tip_content"]'))) reset_btn = self.driver.find_element_by_xpath('//*[@class="geetest_reset_tip_content"]') #判斷是否須要從新打開滑塊框 if reset_btn.text.find("重試") != -1: reset_btn.click() except: pass else: time.sleep(1) # 刷新滑塊驗證碼圖片 refresh_btn = self.driver.find_element_by_xpath('//*[@class="geetest_refresh_1"]') refresh_btn.click() time.sleep(0.5) # 從新進行截圖、分析、計算、拖動處理 self.analog_move() else: print("登陸成功")
登陸完成之後,咱們保存cookies到本地,以供requests使用,具體使用方式請參看selenium+requests進行cookies保存讀取操做
cookies = self.driver.get_cookies() with open("cookies.txt", "w") as fp: json.dump(cookies, fp)
登陸完成保存了cookies咱們就可使用requests來發布博客園隨筆文章了。這回又轉到咱們熟悉的請求分析啦。
查看POST請求,form值中title、body還有兩個__開頭的變量,其餘的都是固定值,找一下__VIEWSTATE/__VIEWSTATEGENERATOR的值
url = "https://i.cnblogs.com/EditPosts.aspx?opt=1" r = self.session.get(url) html = etree.HTML(r.text) __VIEWSTATE = html.xpath('//*[@id="__VIEWSTATE"]')[0].attrib.get('value') __VIEWSTATEGENERATOR = html.xpath('//*[@id="__VIEWSTATEGENERATOR"]')[0].attrib.get('value') data = { "__VIEWSTATE":__VIEWSTATE, "__VIEWSTATEGENERATOR":__VIEWSTATEGENERATOR, "Editor$Edit$txbTitle":title, "Editor$Edit$EditorBody":content, "Editor$Edit$Advanced$ckbPublished":"on", "Editor$Edit$Advanced$chkDisplayHomePage":"on", "Editor$Edit$Advanced$chkComments":"on", "Editor$Edit$Advanced$chkMainSyndication":"on", "Editor$Edit$Advanced$txbEntryName":"", "Editor$Edit$Advanced$txbExcerpt":"", "Editor$Edit$Advanced$txbTag":"", "Editor$Edit$Advanced$tbEnryPassword":"", "Editor$Edit$lkbPost":"發佈" } self.session.post(url, data=data) # 訪問個人博客首頁,查看是否有些發佈的文章 url = "http://www.cnblogs.com/small-bud/" r = self.session.get(url) if r.text.find(title) != -1: print("發佈成功")
博客園的自動發佈搞定了,還有其餘的,之後就能夠一鍵發佈到其餘網站不再須要手動去搞啦
若是你以爲個人文章還能夠,能夠關注個人微信公衆號,查看更多實戰文章:Python爬蟲實戰之路
也能夠掃描下面二維碼,添加個人微信公衆號