1、驗證碼識別php
1.圖形驗證碼的識別css
識別圖形驗證碼須要 tesserocr 庫 OCR技術識別(光學字符識別,是指經過掃描字符,而後經過其形狀將其翻譯成電子文本的過程。)
例如 中國知網註冊頁面 http://my.cnki.net/elibregister/commonRegister.aspx
tesserocr是Python的一個OCR識別庫,但實際上是對tesseract作的一層Python API封裝,因此它的核心是tesseract
因此在安裝tesserocr以前要先安裝tesseracthtml
下載地址:python
https://digi.bib.uni-mannheim.de/tesseract/
其中帶dev的爲開發版本 不帶dev的爲穩定版本 下載好後雙擊安裝
勾選 additional language data 支持語言包git
安裝tesserocrgithub
pip install tesserocr pillow
若是報錯,查看pip支持版本 python命令行下 web
import pip import pip._internal print(pip._internal.pep425tags.get_supported())
去以下網址下載對應版本 進行安裝便可ajax
https://github.com/simonflueckiger/tesserocr-windows_build/releases
測試成功 import tesserocr 不報錯表示成功算法
pip install pillow 測試import PILchrome
1.1 識別測試
下載到本地一張驗證碼後 更改其名字爲 code.jpg 放在python代碼根目錄
代碼以下:
#識別code.jpg 圖片驗證碼
import tesserocr from PIL import Image image = Image.open('code.jpg') #新建image對象 result = tesserocr.image_to_text(image)#調用imgae_to_text方法 傳入image對象 print(result)
另外tesserocr 還有一個更加簡單的方法,這個方法能夠直接將圖片轉化爲字符串
示例:
import tesserocr print(tesserocr.file_to_text('code.jpg')) #不過此種方法識別效果不如上一種方法好
1.2 驗證碼處理
從新下載一張圖片命名爲code1.jpg 從新用以上代碼進行測試
能夠看到若是圖片當中多餘的線條幹擾會影響圖片識別的準確度
對於這種狀況咱們還要進行進一步的處理 例如 轉灰度 二值化等。
能夠利用Image對象的 convert()方法傳入參數 L 便可將圖片轉化爲灰度圖像
示例:
image = image.convert('L') image.show()
傳入參數1 便可將圖片二值化處理
image = image.convert('1') image.show()
可是此種方法默認閥值是127
而且不能直接轉換原圖 要先將原圖轉爲灰度圖像,而後再指定二值化閥值
示例:
image = image.convert('L') threshold = 80#二值化閥值 table = [] for i in range(256): if i < threshold: table.append(0) else: table.append(1) image = image.point(table,'1') image.show()
發現驗證碼中的線條已經去除 驗證碼黑白分明 再從新識別驗證碼
示例:
import tesserocr from PIL import Image image = Image.open('code1.jpg') image = image.convert('L') threshold = 127 table = [] for i in range(256): if i < threshold: table.append(0) else: table.append(1) image = image.point(table, '1') image.show() result = tesserocr.image_to_text(image) print(result)
若是針對一些有干擾的圖片,咱們能夠選擇作一些灰度和二值化處理 達到提升圖片識別的正確率
2. 極驗滑動驗證碼的識別
上面咱們說能夠利用 tesserocr 來識別簡單的圖形驗證碼 可是近幾年出現了一些新型驗證碼
其中比較有表明性的就是極驗驗證碼 它須要拖動拼合滑塊才能夠完成驗證,相對於圖形驗證碼來講
識別難度上升了幾個等級 例如 魅族 鬥魚
確保本機安裝好了selenium 瀏覽器爲Chrome 並配置ChromeDriver
極驗驗證碼官網 http://www.geetest.com/ 它是一個專一於提供驗證安全的系統 主要驗證方式是拖動滑塊拼合圖像
若是圖像徹底拼合 則驗證成功
2.1 極驗驗證碼特色
極驗驗證碼相比圖片驗證碼識別難度更大,對於極驗3.0版本 首先要點擊按鈕進行智能驗證,若是驗證不經過,則會彈出滑動驗證窗口
拖動滑動拼合圖像進行驗證,以後三個加密參數會生成,經過表單提交到後臺,後臺還會進行一次驗證。
極驗驗證碼還增長了機器學習的方法來識別拖動軌跡 官方網站的安全防禦有以下幾點說明:
1. 三角防禦之防模擬。惡意程序模仿人類行爲軌跡對驗證碼進行識別,針對模擬 極驗驗證碼擁有4000萬人機行爲樣本的海量數據
利用機器學習和神經網絡構建線上線下多重靜態 動態防護模型 識別模擬軌跡,界定人機邊界。
2. 三角防禦之防僞造。惡意程序經過僞造設備瀏覽器環境對驗證碼進行識別,針對僞造 極驗驗證碼利用設備基因技術,深度分析
瀏覽器的實際性能來識別僞造信息,同時根據僞造時間不斷更新黑名單,大幅度提升防僞造能力。
3. 三角防禦之防暴力。 惡意程序短期內進行密集攻擊,對驗證碼進行暴力識別,針對暴力識別 極驗驗證碼擁有多種驗證形態,
每一種驗證形態都利用神經網絡生成海量圖庫儲備,每一張圖片都是獨一無二的,且圖庫不斷更新,極大程度提升了暴力識別的成本。
另外 相比普通驗證方式 極驗更加方便 體驗更加友好:
1. 點擊驗證只需0.4秒
2. 全平臺兼容
3. 面向將來
相比通常驗證碼 極驗驗證碼的安全性和易用性有了很是大的提升。
2.2 識別思路
對於應用了極驗驗證碼的網站,若是直接模擬表單提交 加密參數的構造是個問題 須要分析它加密和校驗邏輯 相對繁瑣
因此採用直接模擬瀏覽器動做的方式來完成驗證 此驗證成本相比直接去識別加密算法少不少
示例: 中國保溫網
http://www.cnbaowen.net/api/geetest/
識別驗證只須要完成以下三步:
1.模擬點擊驗證按鈕
2.識別滑動缺口的位置
3.模擬拖動滑塊
第一步相對簡單 能夠直接用selenium 模擬點擊操做
第二步識別缺口位置比較關鍵 須要用到圖像相關的處理方法 首先觀察缺口的樣子 缺口四周邊緣有明顯的斷裂邊緣 邊緣和邊緣周圍有明顯的區別
能夠實現一個邊緣檢測算法找出缺口的位置。
第三步看似簡單 其中的坑比較多 極驗驗證碼增長了機器軌跡識別,勻速移動 隨機速度移動等方法都不能經過驗證,只有徹底模擬人的移動軌跡才能夠經過驗證
人的移動軌跡通常是先加速後減速 須要模擬這個過程才能經過驗證。
2.3 代碼實現
初始化 測試連接
http://www.cnbaowen.net/api/geetest/
from selenium import webdriver from selenium.webdriver.support.ui import WebDriverWait # 等待元素加載的 from selenium.webdriver.common.action_chains import ActionChains #拖拽 from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException from selenium.webdriver.common.by import By from PIL import Image import requests import time import re import random from io import BytesIO def merge_image(image_file,location_list): """ 拼接圖片 :param image_file: :param location_list: :return: """ im = Image.open(image_file) im.save('Code.jpg') new_im = Image.new('RGB',(260,116)) # 把無序的圖片 切成52張小圖片 im_list_upper = [] im_list_down = [] # print(location_list) for location in location_list: # print(location['y']) if location['y'] == -58: # 上半邊 im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,116))) if location['y'] == 0: # 下半邊 im_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58))) x_offset = 0 for im in im_list_upper: new_im.paste(im,(x_offset,0)) # 把小圖片放到 新的空白圖片上 x_offset += im.size[0] x_offset = 0 for im in im_list_down: new_im.paste(im,(x_offset,58)) x_offset += im.size[0] new_im.show() return new_im def get_image(driver,div_path): ''' 下載無序的圖片 而後進行拼接 得到完整的圖片 :param driver: :param div_path: :return: ''' time.sleep(2) background_images = driver.find_elements_by_xpath(div_path) location_list = [] for background_image in background_images: location = {} result = re.findall('background-image: url\("(.*?)"\); background-position: (.*?)px (.*?)px;',background_image.get_attribute('style')) # print(result) location['x'] = int(result[0][1]) location['y'] = int(result[0][2]) image_url = result[0][0] location_list.append(location) print('==================================') image_url = image_url.replace('webp','jpg') # '替換url http://static.geetest.com/pictures/gt/579066de6/579066de6.webp' image_result = requests.get(image_url).content # with open('1.jpg','wb') as f: # f.write(image_result) image_file = BytesIO(image_result) # 是一張無序的圖片 image = merge_image(image_file,location_list) return image def get_track(distance): ''' 拿到移動軌跡,模仿人的滑動行爲,先勻加速後勻減速 勻變速運動基本公式: ①v=v0+at ②s=v0t+(1/2)at² ③v²-v0²=2as :param distance: 須要移動的距離 :return: 存放每0.2秒移動的距離 ''' # 初速度 v=0 # 單位時間爲0.2s來統計軌跡,軌跡即0.2內的位移 t=0.2 # 位移/軌跡列表,列表內的一個元素表明0.2s的位移 tracks=[] # 當前的位移 current=0 # 到達mid值開始減速 mid=distance * 7/8 distance += 10 # 先滑過一點,最後再反着滑動回來 # a = random.randint(1,3) while current < distance: if current < mid: # 加速度越小,單位時間的位移越小,模擬的軌跡就越多越詳細 a = random.randint(2,4) # 加速運動 else: a = -random.randint(3,5) # 減速運動 # 初速度 v0 = v # 0.2秒時間內的位移 s = v0*t+0.5*a*(t**2) # 當前的位置 current += s # 添加到軌跡列表 tracks.append(round(s)) # 速度已經達到v,該速度做爲下次的初速度 v= v0+a*t # 反着滑動到大概準確位置 for i in range(4): tracks.append(-random.randint(2,3)) for i in range(4): tracks.append(-random.randint(1,3)) return tracks def get_distance(image1,image2): ''' 拿到滑動驗證碼須要移動的距離 :param image1:沒有缺口的圖片對象 :param image2:帶缺口的圖片對象 :return:須要移動的距離 ''' # print('size', image1.size) threshold = 60 for i in range(0,image1.size[0]): # 260 for j in range(0,image1.size[1]): # 160 pixel1 = image1.getpixel((i,j)) pixel2 = image2.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 main_check_code(driver, element): """ 拖動識別驗證碼 :param driver: :param element: :return: """ image1 = get_image(driver, '//div[@class="gt_cut_bg gt_show"]/div') image2 = get_image(driver, '//div[@class="gt_cut_fullbg gt_show"]/div') # 圖片上 缺口的位置的x座標 # 2 對比兩張圖片的全部RBG像素點,獲得不同像素點的x值,即要移動的距離 l = get_distance(image1, image2) print('l=',l) # 3 得到移動軌跡 track_list = get_track(l) print('第一步,點擊滑動按鈕') ActionChains(driver).click_and_hold(on_element=element).perform() # 點擊鼠標左鍵,按住不放 time.sleep(2) print('第二步,拖動元素') for track in track_list: ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform() # 鼠標移動到距離當前位置(x,y) time.sleep(0.002) ActionChains(driver).move_by_offset(xoffset=-random.randint(2,5), yoffset=0).perform() time.sleep(2) print('第三步,釋放鼠標') ActionChains(driver).release(on_element=element).perform() time.sleep(5) def main_check_slider(driver): """ 檢查滑動按鈕是否加載 :param driver: :return: """ while True: try : driver.get('http://www.cnbaowen.net/api/geetest/') element = WebDriverWait(driver, 30, 0.5).until(EC.element_to_be_clickable((By.CLASS_NAME, 'gt_slider_knob'))) if element: return element except TimeoutException as e: print('超時錯誤,繼續') time.sleep(5) if __name__ == '__main__': try: count = 6 # 最多識別6次 driver = webdriver.Chrome() # 等待滑動按鈕加載完成 element = main_check_slider(driver) while count > 0: main_check_code(driver,element) time.sleep(2) try: success_element = (By.CSS_SELECTOR, '.gt_holder .gt_ajax_tip.gt_success') # 獲得成功標誌 print('suc=',driver.find_element_by_css_selector('.gt_holder .gt_ajax_tip.gt_success')) success_images = WebDriverWait(driver, 20).until(EC.presence_of_element_located(success_element)) if success_images: print('成功識別') count = 0 break except NoSuchElementException as e: print('識別錯誤,繼續識別') ount -= 1 time.sleep(2) else: print('too many attempt check code ') exit('退出程序') finally: driver.close()
3. 點觸驗證碼的識別
除了極驗驗證碼以外,還有一個常見且比較普遍的驗證碼,既點觸驗證碼 例如12306
直接點擊圖中符合要求的圖 答案所有正確驗證纔會成功 有一個錯誤驗證就會失敗
示例:
https://www.jianshu.com/sign_in
識別思路 :
若是依靠圖像識別驗證碼 識別難度很是大 第一點是文字識別 第二點是圖像識別 圖像背景會干擾 致使ORC幾乎識別不出結果
若是直接識別白色文字 換一張驗證碼 顏色又變了
藉助打碼平臺:
http://www.chaojiying.com/user/reg/
註冊帳戶
進入用戶中心 申請軟件ID
關注微信或者購買題分
http://www.chaojiying.com/api-14.html
下載打碼平臺的api
示例以下:
#!/usr/bin/env python # coding:utf-8 import requests from hashlib import md5 class Chaojiying_Client(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } def PostPic(self, im, codetype): """ im: 圖片字節 codetype: 題目類型 參考 http://www.chaojiying.com/price.html """ params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() def ReportError(self, im_id): """ im_id:報錯題目的圖片ID """ params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json() if __name__ == '__main__': chaojiying = Chaojiying_Client('超級鷹用戶名', '超級鷹用戶名的密碼', '96001') #用戶中心>>軟件ID 生成一個替換 96001 im = open('a.jpg', 'rb').read() #本地圖片文件路徑 來替換 a.jpg 有時WIN系統需要// print chaojiying.PostPic(im, 1902) #1902 驗證碼類型 官方網站>>價格體系 3.4+版 print 後要加()
這裏定義了一個Chaojiying_Client類 其構造函數接收三個參數 分別是超級鷹用戶名,超級鷹用戶名的密碼,軟件ID
最重要的一個方法叫作PostPic,須要傳入圖片對象和驗證碼的代號,該方法會將圖片對象的相關信息 發送個超級鷹後臺進行識別,
而後將識別成功的JSON返回。
ReportError方法 發生錯誤的時候回調 若是驗證碼識別錯誤,調用此方法會返回相應的題分。
初始化
import time from PIL import Image from selenium import webdriver from selenium.webdriver import ActionChains from chaojiying import Chaojiying def crack(): # 保存網頁截圖 browser.save_screenshot('222.jpg') # 獲取 驗證碼肯定按鈕 button = browser.find_element_by_xpath(xpath='//div[@class="geetest_panel"]/a/div') # 獲取 驗證碼圖片的 位置信息 img1 = browser.find_element_by_xpath(xpath='//div[@class="geetest_widget"]') location = img1.location size = img1.size top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[ 'width'] print('圖片的寬:', img1.size['width']) print(top, bottom, left, right) # 根據獲取的驗證碼位置信息和網頁圖片 對驗證碼圖片進行裁剪 保存 img_1 = Image.open('222.jpg') capcha1 = img_1.crop((left, top, right, bottom - 54)) capcha1.save('tu1-1.png') # 接入超級鷹 API 獲取圖片中的一些參數 (返回的是一個字典) cjy = Chaojiying('liuxiaosong', '123456', '898237') im = open('tu1-1.png', 'rb').read() content = cjy.post_pic(im, 9004) print(content) # 將圖片中漢字的座標位置 提取出來 positions = content.get('pic_str').split('|') locations = [[int(number) for number in group.split(",")] for group in positions] print(positions) print(locations) # 根據獲取的座標信息 模仿鼠標點擊驗證碼圖片 for location1 in locations: print(location1) ActionChains(browser).move_to_element_with_offset(img1, location1[0], location1[1]).click().perform() time.sleep(1) button.click() time.sleep(1) # 失敗後重試 lower = browser.find_element_by_xpath('//div[@class="geetest_table_box"]/div[2]').text print('判斷', lower) if lower != '驗證失敗 請按提示從新操做' and lower != None: print('登陸成功') time.sleep(3) else: time.sleep(3) print('登陸失敗') # 登陸失敗後 , 調用 該函數 , 後臺 則對該次判斷不作扣分處理 pic_id = content.get('pic_id') print('圖片id爲:', pic_id) cjy = Chaojiying('liuxiaosong', '123456', '898237') cjy.report_error(pic_id) crack() if __name__ == '__main__': browser = webdriver.Chrome() browser.get('https://www.jianshu.com/sign_in') browser.save_screenshot('login.png') # 填寫from表單 點擊登錄 獲取驗證碼 的網頁截圖 login = browser.find_element_by_id('sign-in-form-submit-btn') username = browser.find_element_by_id('session_email_or_mobile_number') password = browser.find_element_by_id('session_password') username.send_keys('15221742895') time.sleep(1) password.send_keys('123456') time.sleep(2) login.click() time.sleep(10) crack()
2、代理的使用
前面介紹了多種請求庫 requests urllib selenium等
1.獲取代理
網上有不少免費代理 例如 西刺 http://www.xicidaili.com/ 可是免費代理大多數是很差用的 最靠譜的方法是購買付費代理
若是本機有代理軟件的話 軟件通常會在本機建立HTTP和SOCKS代理服務 本機直接使用代理也能夠
示例: (也能夠替換成本身的可用代理 設置代理後測試網址是http://httpbin.org/get 訪問該網站能夠獲得請求信息 其中origin字段就是客戶端的ip)
2.urllib
from urllib.error import URLError from urllib.request import ProxyHandler,build_opener proxy = '127.0.0.1:14155' proxy_handler = ProxyHandler({ 'http':'http://' + proxy, 'https':'https://' + proxy }) opener = build_opener(proxy_handler) try: response = opener.open('http://httpbin.org/get') print(response.read().decode('utf-8')) except URLError as e: print(e.reason)
這裏藉助ProxyHandler 設置代理 參數是字典 鍵名爲協議 鍵值爲代理
建立完ProxyHandler對象後 調用 build_opener()方法傳入該對象來建立一個opener對象
若是須要認證 能夠改變proxy 變量 只須要在代理前面加入代理認證的用戶名密碼便可 例如 username:password@127.0.0.1:14155
3.requests
代理設置相對urllib簡單 傳入參數proxies
import requests proxy = '127.0.0.1:14155' proxies = { 'http': 'http://' + proxy, 'https': 'https://' + proxy, } try: response = requests.get('http://httpbin.org/get', proxies=proxies) print(response.text) except requests.exceptions.ConnectionError as e: print('Error', e.args)
須要認證的話同理 proxy = 'username:password@127.0.0.1:9743'
4. selenium
from selenium import webdriver proxy = '127.0.0.1:14155' chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--proxy-server=http://' + proxy) chrome = webdriver.Chrome(chrome_options=chrome_options) chrome.get('http://httpbin.org/get')
若是是認證的相對比較麻煩
from selenium import webdriver from selenium.webdriver.chrome.options import Options import zipfile ip = '127.0.0.1' port = 14155 username = 'liuxiaosong' password = '123456' manifest_json = """ { "version": "1.0.0", "manifest_version": 2, "name": "Chrome Proxy", "permissions": [ "proxy", "tabs", "unlimitedStorage", "storage", "<all_urls>", "webRequest", "webRequestBlocking" ], "background": { "scripts": ["background.js"] } } """ background_js = """ var config = { mode: "fixed_servers", rules: { singleProxy: { scheme: "http", host: "%(ip)s", port: %(port)s } } } chrome.proxy.settings.set({value: config, scope: "regular"}, function() {}); function callbackFn(details) { return { authCredentials: { username: "%(username)s", password: "%(password)s" } } } chrome.webRequest.onAuthRequired.addListener( callbackFn, {urls: ["<all_urls>"]}, ['blocking'] ) """ % {'ip': ip, 'port': port, 'username': username, 'password': password} plugin_file = 'proxy_auth_plugin.zip' with zipfile.ZipFile(plugin_file, 'w') as zp: zp.writestr("manifest.json", manifest_json) zp.writestr("background.js", background_js) chrome_options = Options() chrome_options.add_argument("--start-maximized") chrome_options.add_extension(plugin_file) browser = webdriver.Chrome(chrome_options=chrome_options) browser.get('http://httpbin.org/get')
須要本地建立一個manifest.json配置文件 background.js 腳本設置代理 運行以後本地會生成一個 proxy_auth_plugin.zip 文件保存當前設置
5.phantomjs
須要安裝 下載地址 http://phantomjs.org/download 選擇對應平臺下載便可
下載後 解壓文件 複製在bin目錄下phantomjs.exe 到python目錄下的script目錄下 或者單獨添加環境變量
cmd 運行 phantomjs 進入到phantomjs命令行表示配置成功
在selenium中使用的話 只須要將Chrome切換爲PhantomJS便可
from selenium import webdriver browser = webdriver.PhantomJS() browser.get('https://www.baidu.com') print(browser.current_url)
會報警告 selenium 3.X版本 已經棄用PhantomJS 兩種方式 使用Chrome無界面 headless 或者下降selenium版本 推薦第一種
示例:
from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu')#上面三行代碼就是爲了將Chrome不彈出界面,實現無界面爬取 browser = webdriver.Chrome(chrome_options=chrome_options)
PhantomJS示例:
from selenium import webdriver service_args = [ '--proxy=127.0.0.1:9743', '--proxy-type=http' ] browser = webdriver.PhantomJS(service_args=service_args) browser.get('http://httpbin.org/get') print(browser.page_source)
若是加認證
from selenium import webdriver service_args = [ '--proxy=127.0.0.1:9743', '--proxy-type=http', '--proxy-auth=username:password' ] browser = webdriver.PhantomJS(service_args=service_args) browser.get('http://httpbin.org/get') print(browser.page_source)