Selenium
Selenium是一個Web的自動化測試工具,最初是為網站自動化測試而開發的,類型像我們玩游戲用的按鍵精靈,可以按指定的命令自動操作,不同是Selenium 可以直接運行在瀏覽器上,它支持所有主流的瀏覽器(包括PhantomJS這些無界面的瀏覽器)。
Selenium 可以根據我們的指令,讓瀏覽器自動加載頁面,獲取需要的數據,甚至頁面截屏,或者判斷網站上某些動作是否發生。
Selenium 自己不帶瀏覽器,不支持瀏覽器的功能,它需要與第三方瀏覽器結合在一起才能使用。但是我們有時候需要讓它內嵌在代碼中運行,所以我們可以用一個叫 PhantomJS 的工具代替真實的瀏覽器。
可以從 PyPI 網站下載 Selenium庫https://pypi.python.org/simple/selenium ,也可以用 第三方管理器 pip用命令安裝:pip install selenium
Selenium 官方參考文檔:http://selenium-python.readthedocs.io/index.html
PhantomJS
PhantomJS 是一個基於Webkit的“無界面”(headless)瀏覽器,它會把網站加載到內存並執行頁面上的 JavaScript,因為不會展示圖形界面,所以運行起來比完整的瀏覽器要高效。
如果我們把 Selenium 和 PhantomJS 結合在一起,就可以運行一個非常強大的網絡爬蟲了,這個爬蟲可以處理 JavaScrip、Cookie、headers,以及任何我們真實用戶需要做的事情。
注意:PhantomJS 只能從它的官方網站http://phantomjs.org/download.html) 下載。 因為 PhantomJS 是一個功能完善(雖然無界面)的瀏覽器而非一個 Python 庫,所以它不需要像 Python 的其他庫一樣安裝,但我們可以通過Selenium調用PhantomJS來直接使用。
PhantomJS 官方參考文檔:http://phantomjs.org/documentation
快速入門
Selenium 庫里有個叫 WebDriver 的 API。WebDriver 有點兒像可以加載網站的瀏覽器,但是它也可以像 BeautifulSoup 或者其他 Selector 對象一樣用來查找頁面元素,與頁面上的元素進行交互 (發送文本、點擊等),以及執行其他動作來運行網絡爬蟲。
# IPython2 測試代碼 # 導入 webdriver from selenium import webdriver # 要想調用鍵盤按鍵操作需要引入keys包 from selenium.webdriver.common.keys import Keys # 調用環境變量指定的PhantomJS瀏覽器創建瀏覽器對象 driver = webdriver.PhantomJS() # 如果沒有在環境變量指定PhantomJS位置 # driver = webdriver.PhantomJS(executable_path="./phantomjs")) # get方法會一直等到頁面被完全加載,然后才會繼續程序,通常測試會在這里選擇 time.sleep(2) driver.get("http://www.baidu.com/") # 獲取頁面名為 wrapper的id標簽的文本內容 data = driver.find_element_by_id("wrapper").text # 打印數據內容 print data # 打印頁面標題 "百度一下,你就知道" print driver.title # 生成當前頁面快照並保存 driver.save_screenshot("baidu.png") # id="kw"是百度搜索輸入框,輸入字符串"長城" driver.find_element_by_id("kw").send_keys(u"長城") # id="su"是百度搜索按鈕,click() 是模擬點擊 driver.find_element_by_id("su").click() # 獲取新的頁面快照 driver.save_screenshot("長城.png") # 打印網頁渲染后的源代碼 print driver.page_source # 獲取當前頁面Cookie print driver.get_cookies() # ctrl+a 全選輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a') # ctrl+x 剪切輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x') # 輸入框重新輸入內容 driver.find_element_by_id("kw").send_keys("itcast") # 模擬Enter回車鍵 driver.find_element_by_id("su").send_keys(Keys.RETURN) # 清除輸入框內容 driver.find_element_by_id("kw").clear() # 生成新的頁面快照 driver.save_screenshot("itcast.png") # 獲取當前url print driver.current_url # 關閉當前頁面,如果只有一個頁面,會關閉瀏覽器 # driver.close() # 關閉瀏覽器 driver.quit()
頁面操作
Selenium 的 WebDriver提供了各種方法來尋找元素,假設下面有一個表單輸入框:
<input type="text" name="user-name" id="passwd-id" />
那么:
# 獲取id標簽值 element = driver.find_element_by_id("passwd-id") # 獲取name標簽值 element = driver.find_element_by_name("user-name") # 獲取標簽名值 element = driver.find_elements_by_tag_name("input") # 也可以通過XPath來匹配 element = driver.find_element_by_xpath("//input[@id='passwd-id']")
定位UI元素 (WebElements)
關於元素的選取,有如下的API 單個元素選取
find_element_by_id
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
By ID <div id="coolestWidgetEvah">...</div> 實現 element = driver.find_element_by_id("coolestWidgetEvah") ------------------------ or ------------------------- from selenium.webdriver.common.by import By element = driver.find_element(by=By.ID, value="coolestWidgetEvah") By Class Name <div class="cheese"><span>Cheddar</span></div><div class="cheese"><span>Gouda</span></div> 實現 cheeses = driver.find_elements_by_class_name("cheese") ------------------------ or ------------------------- from selenium.webdriver.common.by import By cheeses = driver.find_elements(By.CLASS_NAME, "cheese") By Tag Name <iframe src="..."></iframe> 實現 frame = driver.find_element_by_tag_name("iframe") ------------------------ or ------------------------- from selenium.webdriver.common.by import By frame = driver.find_element(By.TAG_NAME, "iframe") By Name <input name="cheese" type="text"/> 實現 cheese = driver.find_element_by_name("cheese") ------------------------ or ------------------------- from selenium.webdriver.common.by import By cheese = driver.find_element(By.NAME, "cheese") By Link Text <a href="http://www.google.com/search?q=cheese">cheese</a> 實現 cheese = driver.find_element_by_link_text("cheese") ------------------------ or ------------------------- from selenium.webdriver.common.by import By cheese = driver.find_element(By.LINK_TEXT, "cheese") By Partial Link Text <a href="http://www.google.com/search?q=cheese">search for cheese</a>> 實現 cheese = driver.find_element_by_partial_link_text("cheese") ------------------------ or ------------------------- from selenium.webdriver.common.by import By cheese = driver.find_element(By.PARTIAL_LINK_TEXT, "cheese") By CSS <div id="food"><span class="dairy">milk</span><span class="dairy aged">cheese</span></div> 實現 cheese = driver.find_element_by_css_selector("#food span.dairy.aged") ------------------------ or ------------------------- from selenium.webdriver.common.by import By cheese = driver.find_element(By.CSS_SELECTOR, "#food span.dairy.aged") By XPath <input type="text" name="example" /> <INPUT type="text" name="other" /> 實現 inputs = driver.find_elements_by_xpath("//input") ------------------------ or ------------------------- from selenium.webdriver.common.by import By inputs = driver.find_elements(By.XPATH, "//input")
鼠標動作鏈
有些時候,我們需要再頁面上模擬一些鼠標操作,比如雙擊、右擊、拖拽甚至按住不動等,我們可以通過導入 ActionChains 類來做到:
示例:
#導入 ActionChains 類 from selenium.webdriver import ActionChains # 鼠標移動到 ac 位置 ac = driver.find_element_by_xpath('element') ActionChains(driver).move_to_element(ac).perform() # 在 ac 位置單擊 ac = driver.find_element_by_xpath("elementA") ActionChains(driver).move_to_element(ac).click(ac).perform() # 在 ac 位置雙擊 ac = driver.find_element_by_xpath("elementB") ActionChains(driver).move_to_element(ac).double_click(ac).perform() # 在 ac 位置右擊 ac = driver.find_element_by_xpath("elementC") ActionChains(driver).move_to_element(ac).context_click(ac).perform() # 在 ac 位置左鍵單擊hold住 ac = driver.find_element_by_xpath('elementF') ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform() # 將 ac1 拖拽到 ac2 位置 ac1 = driver.find_element_by_xpath('elementD') ac2 = driver.find_element_by_xpath('elementE') ActionChains(driver).drag_and_drop(ac1, ac2).perform()
填充表單
我們已經知道了怎樣向文本框中輸入文字,但是有時候我們會碰到<select> </select>
標簽的下拉框。直接點擊下拉框中的選項不一定可行。
<select id="status" class="form-control valid" onchange="" name="status"> <option value=""></option> <option value="0">未審核</option> <option value="1">初審通過</option> <option value="2">復審通過</option> <option value="3">審核不通過</option> </select>
Selenium專門提供了Select類來處理下拉框。 其實 WebDriver 中提供了一個叫 Select 的方法,可以幫助我們完成這些事情:
# 導入 Select 類 from selenium.webdriver.support.ui import Select # 找到 name 的選項卡 select = Select(driver.find_element_by_name('status')) # select.select_by_index(1) select.select_by_value("0") select.select_by_visible_text(u"未審核")
以上是三種選擇下拉框的方式,它可以根據索引來選擇,可以根據值來選擇,可以根據文字來選擇。注意:
- index 索引從 0 開始
- value是option標簽的一個屬性值,並不是顯示在下拉框中的值
- visible_text是在option標簽文本的值,是顯示在下拉框的值
全部取消選擇怎么辦呢?很簡單:
select.deselect_all()
彈窗處理
當你觸發了某個事件之后,頁面出現了彈窗提示,處理這個提示或者獲取提示信息方法如下:
alert = driver.switch_to_alert()
頁面切換
一個瀏覽器肯定會有很多窗口,所以我們肯定要有方法來實現窗口的切換。切換窗口的方法如下:
driver.switch_to.window("this is window name")
也可以使用 window_handles 方法來獲取每個窗口的操作對象。例如:
for handle in driver.window_handles: driver.switch_to_window(handle)
頁面前進和后退
操作頁面的前進和后退功能:
driver.forward() #前進 driver.back() # 后退
Cookies
獲取頁面每個Cookies值,用法如下
for cookie in driver.get_cookies(): print "%s -> %s" % (cookie['name'], cookie['value'])
刪除Cookies,用法如下
# By name driver.delete_cookie("CookieName") # all driver.delete_all_cookies()
頁面等待
注意:這是非常重要的一部分!!
現在的網頁越來越多采用了 Ajax 技術,這樣程序便不能確定何時某個元素完全加載出來了。如果實際頁面等待時間過長導致某個dom元素還沒出來,但是你的代碼直接使用了這個WebElement,那么就會拋出NullPointer的異常。
為了避免這種元素定位困難而且會提高產生 ElementNotVisibleException 的概率。所以 Selenium 提供了兩種等待方式,一種是隱式等待,一種是顯式等待。
隱式等待是等待特定的時間,顯式等待是指定某一條件直到這個條件成立時繼續執行。
顯式等待
顯式等待指定某個條件,然后設置最長等待時間。如果在這個時間還沒有找到元素,那么便會拋出異常了。
from selenium import webdriver from selenium.webdriver.common.by import By # WebDriverWait 庫,負責循環等待 from selenium.webdriver.support.ui import WebDriverWait # expected_conditions 類,負責條件出發 from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome() driver.get("http://www.xxxxx.com/loading") try: # 頁面一直循環,直到 id="myDynamicElement" 出現 element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement")) ) finally: driver.quit()
如果不寫參數,程序默認會 0.5s 調用一次來查看元素是否已經生成,如果本來元素就是存在的,那么會立即返回。
下面是一些內置的等待條件,你可以直接調用這些條件,而不用自己寫某些等待條件了。
title_is title_contains presence_of_element_located visibility_of_element_located visibility_of presence_of_all_elements_located text_to_be_present_in_element text_to_be_present_in_element_value frame_to_be_available_and_switch_to_it invisibility_of_element_located element_to_be_clickable – it is Displayed and Enabled. staleness_of element_to_be_selected element_located_to_be_selected element_selection_state_to_be element_located_selection_state_to_be alert_is_present
隱式等待
隱式等待比較簡單,就是簡單地設置一個等待時間,單位為秒。
from selenium import webdriver driver = webdriver.Chrome() driver.implicitly_wait(10) # seconds driver.get("http://www.xxxxx.com/loading") myDynamicElement = driver.find_element_by_id("myDynamicElement")
當然如果不設置,默認等待時間為0。
案例一:網站模擬登錄
# douban.py from selenium import webdriver from selenium.webdriver.common.keys import Keys import time driver = webdriver.PhantomJS() driver.get("http://www.douban.com") # 輸入賬號密碼 driver.find_element_by_name("form_email").send_keys("xxxxx@xxxx.com") driver.find_element_by_name("form_password").send_keys("xxxxxxxx") # 模擬點擊登錄 driver.find_element_by_xpath("//input[@class='bn-submit']").click() # 等待3秒 time.sleep(3) # 生成登陸后快照 driver.save_screenshot("douban.png") with open("douban.html", "w") as file: file.write(driver.page_source) driver.quit()
案例二:動態頁面模擬點擊
#!/usr/bin/env python # -*- coding:utf-8 -*- # python的測試模塊 import unittest from selenium import webdriver from bs4 import BeautifulSoup class douyuSelenium(unittest.TestCase): # 初始化方法 def setUp(self): self.driver = webdriver.PhantomJS() #具體的測試用例方法,一定要以test開頭 def testDouyu(self): self.driver.get('http://www.douyu.com/directory/all') while True: # 指定xml解析 soup = BeautifulSoup(driver.page_source, 'xml') # 返回當前頁面所有房間標題列表 和 觀眾人數列表 titles = soup.find_all('h3', {'class': 'ellipsis'}) nums = soup.find_all('span', {'class': 'dy-num fr'}) # 使用zip()函數來可以把列表合並,並創建一個元組對的列表[(1,2), (3,4)] for title, num in zip(nums, titles): print u"觀眾人數:" + num.get_text().strip(), u"\t房間標題: " + title.get_text().strip() # page_source.find()未找到內容則返回-1 if driver.page_source.find('shark-pager-disable-next') != -1: break # 模擬下一頁點擊 self.driver.find_element_by_class_name('shark-pager-next').click() # 退出時的清理方法 def tearDown(self): print '加載完成...' self.driver.quit() if __name__ == "__main__": unittest.main()
案例三:執行 JavaScript 語句
隱藏百度圖片
from selenium import webdriver driver = webdriver.PhantomJS() driver.get("https://www.baidu.com/") # 給搜索輸入框標紅的javascript腳本 js = "var q=document.getElementById(\"kw\");q.style.border=\"2px solid red\";" # 調用給搜索輸入框標紅js腳本 driver.execute_script(js) #查看頁面快照 driver.save_screenshot("redbaidu.png") #js隱藏元素,將獲取的圖片元素隱藏 img = driver.find_element_by_xpath("//*[@id='lg']/img") driver.execute_script('$(arguments[0]).fadeOut()',img) # 向下滾動到頁面底部 driver.execute_script("$('.scroll_top').click(function(){$('html,body').animate({scrollTop: '0px'}, 800);});") #查看頁面快照 driver.save_screenshot("nullbaidu.png") driver.quit()
模擬滾動條滾動到底部
# from selenium import webdriver import time driver = webdriver.PhantomJS() driver.get("https://movie.douban.com/typerank?type_name=劇情&type=11&interval_id=100:90&action=") # 向下滾動10000像素 js = "document.body.scrollTop=10000" #js="var q=document.documentElement.scrollTop=10000" time.sleep(3) #查看頁面快照 driver.save_screenshot("douban.png") # 執行JS語句 driver.execute_script(js) time.sleep(10) #查看頁面快照 driver.save_screenshot("newdouban.png") driver.quit()
機器視覺
從 Google 的無人駕駛汽車到可以識別假鈔的自動售賣機,機器視覺一直都是一個應用廣 泛且具有深遠的影響和雄偉的願景的領域。
我們將重點介紹機器視覺的一個分支:文字識別,介紹如何用一些 Python庫來識別和使用在線圖片中的文字。
我們可以很輕松的閱讀圖片里的文字,但是機器閱讀這些圖片就會非常困難,利用這種人類用戶可以正常讀取但是大多數機器人都沒法讀取的圖片,驗證碼 (CAPTCHA)就出現了。驗證碼讀取的難易程度也大不相同,有些驗證碼比其他的更加難讀。
將圖像翻譯成文字一般被稱為光學文字識別(Optical Character Recognition, OCR)。可以實現OCR的底層庫並不多,目前很多庫都是使用共同的幾個底層 OCR 庫,或者是在上面 進行定制。
ORC庫概述
在讀取和處理圖像、圖像相關的機器學習以及創建圖像等任務中,Python 一直都是非常出色的語言。雖然有很多庫可以進行圖像處理,但在這里我們只重點介紹:Tesseract
Tesseract
Tesseract 是一個 OCR 庫,目前由 Google 贊助(Google 也是一家以 OCR 和機器學習技術聞名於世的公司)。Tesseract 是目前公認最優秀、最精確的開源 OCR 系統。 除了極高的精確度,Tesseract 也具有很高的靈活性。它可以通過訓練識別出任何字體,也可以識別出任何 Unicode 字符。
安裝Tesseract
Windows 系統
下載可執行安裝文件https://code.google.com/p/tesseract-ocr/downloads/list安裝。
Linux 系統
可以通過 apt-get 安裝: $sudo apt-get tesseract-ocr
Mac OS X系統
用 Homebrew(http://brew.sh/)等第三方庫可以很方便地安裝 brew install tesseract
要使用 Tesseract 的功能,比如后面的示例中訓練程序識別字母,要先在系統中設置一 個新的環境變量 $TESSDATA_PREFIX
,讓 Tesseract 知道訓練的數據文件存儲在哪里,然后搞一份tessdata數據文件,放到Tesseract目錄下。
-
在大多數 Linux 系統和 Mac OS X 系統上,你可以這么設置:
$export TESSDATA_PREFIX=/usr/local/share/Tesseract
-
在 Windows 系統上也類似,你可以通過下面這行命令設置環境變量:
#setx TESSDATA_PREFIX C:\Program Files\Tesseract OCR\Tesseract
安裝pytesseract
Tesseract 是一個 Python 的命令行工具,不是通過 import 語句導入的庫。安裝之后,要用 tesseract 命令在 Python 的外面運行,但我們可以通過 pip 安裝支持Python 版本的 Tesseract庫:
pip install pytesseract
處理給規范的文字
你要處理的大多數文字都是比較干凈、格式規范的。格式規范的文字通常可以滿足一些需求,不過究竟什么是“格式混亂”,什么算“格式規范”,確實因人而異。 通常,格式規范的文字具有以下特點:
- 使用一個標准字體(不包含手寫體、草書,或者十分“花哨的”字體) • 雖然被復印或拍照,字體還是很清晰,沒有多余的痕跡或污點
- 排列整齊,沒有歪歪斜斜的字
- 沒有超出圖片范圍,也沒有殘缺不全,或緊緊貼在圖片的邊緣
文字的一些格式問題在圖片預處理時可以進行解決。例如,可以把圖片轉換成灰度圖,調 整亮度和對比度,還可以根據需要進行裁剪和旋轉(詳情請關注圖像與信號處理),但是,這些做法在進行更具擴展性的 訓練時會遇到一些限制。
格式規范文字的理想示例
通過下面的命令運行 Tesseract,讀取文件並把結果寫到一個文本文件中: `tesseract test.jpg text
cat text.txt
即可顯示結果。
識別結果很准確,不過符號^
和*
分別被表示成了雙引號和單引號。大體上可以讓你很舒服地閱讀。
通過Python代碼實現
import pytesseract from PIL import Image image = Image.open('test.jpg') text = pytesseract.image_to_string(image) print text
運行結果:
This is some text, written in Arial, that will be read by Tesseract. Here are some symbols: !@#$%"&*()
對圖片進行閾值過濾和降噪處理(了解即可)
很多時候我們在網上會看到這樣的圖片:
Tesseract 不能完整處理這個圖片,主要是因為圖片背景色是漸變的,最終結果是這樣:
隨着背景色從左到右不斷加深,文字變得越來越難以識別,Tesseract 識別出的 每一行的最后幾個字符都是錯的。
遇到這類問題,可以先用 Python 腳本對圖片進行清理。利用 Pillow 庫,我們可以創建一個 閾值過濾器來去掉漸變的背景色,只把文字留下來,從而讓圖片更加清晰,便於 Tesseract 讀取:
from PIL import Image import subprocess def cleanFile(filePath, newFilePath): image = Image.open(filePath) # 對圖片進行閾值過濾,然后保存 image = image.point(lambda x: 0 if x<143 else 255) image.save(newFilePath) # 調用系統的tesseract命令對圖片進行OCR識別 subprocess.call(["tesseract", newFilePath, "output"]) # 打開文件讀取結果 file = open("output.txt", 'r') print(file.read()) file.close() cleanFile("text2.jpg", "text2clean.png")
通過一個閾值對前面的“模糊”圖片進行過濾的結果
除了一些標點符號不太清晰或丟失了,大部分文字都被讀出來了。Tesseract 給出了最好的 結果:
從網站圖片中抓取文字
用 Tesseract 讀取硬盤里圖片上的文字,可能不怎么令人興奮,但當我們把它和網絡爬蟲組合使用時,就能成為一個強大的工具。
網站上的圖片可能並不是故意把文字做得很花哨 (就像餐館菜單的 JPG 圖片上的藝術字),但它們上面的文字對網絡爬蟲來說就是隱藏起來 了,舉個例子:
-
雖然亞馬遜的 robots.txt 文件允許抓取網站的產品頁面,但是圖書的預覽頁通常不讓網絡機 器人采集。
-
圖書的預覽頁是通過用戶觸發 Ajax 腳本進行加載的,預覽圖片隱藏在 div 節點 下面;其實,普通的訪問者會覺得它們看起來更像是一個 Flash 動畫,而不是一個圖片文 件。當然,即使我們能獲得圖片,要把它們讀成文字也沒那么簡單。
-
下面的程序就解決了這個問題:首先導航到托爾斯泰的《戰爭與和平》的大字號印刷版 1, 打開閱讀器,收集圖片的 URL 鏈接,然后下載圖片,識別圖片,最后打印每個圖片的文 字。因為這個程序很復雜,利用了前面幾章的多個程序片段,所以我增加了一些注釋以讓 每段代碼的目的更加清晰:
import time from urllib.request import urlretrieve import subprocess from selenium import webdriver #創建新的Selenium driver driver = webdriver.PhantomJS() # 用Selenium試試Firefox瀏覽器: # driver = webdriver.Firefox() driver.get("http://www.amazon.com/War-Peace-Leo-Nikolayevich-Tolstoy/dp/1427030200") # 單擊圖書預覽按鈕 driver.find_element_by_id("sitbLogoImg").click() imageList = set() # 等待頁面加載完成 time.sleep(5) # 當向右箭頭可以點擊時,開始翻頁 while "pointer" in driver.find_element_by_id("sitbReaderRightPageTurner").get_attribute("style"): driver.find_element_by_id("sitbReaderRightPageTurner").click() time.sleep(2) # 獲取已加載的新頁面(一次可以加載多個頁面,但是重復的頁面不能加載到集合中) pages = driver.find_elements_by_xpath("//div[@class='pageImage']/div/img") for page in pages: image = page.get_attribute("src") imageList.add(image) driver.quit() # 用Tesseract處理我們收集的圖片URL鏈接 for image in sorted(imageList): # 保存圖片 urlretrieve(image, "page.jpg") p = subprocess.Popen(["tesseract", "page.jpg", "page"], stdout=subprocess.PIPE,stderr=subprocess.PIPE) f = open("page.txt", "r") p.wait() print(f.read())
和我們前面使用 Tesseract 讀取的效果一樣,這個程序也會完美地打印書中很多長長的段 落,第六頁的預覽如下所示:
6
"A word of friendly advice, mon cher. Be off as soon as you can, that's all I have to tell you. Happy he who has ears to hear. Good-by, my dear fellow. Oh, by the by!" he shouted through the doorway after Pierre, "is it true that the countess has fallen into the clutches of the holy fathers of the Society of je- sus?" Pierre did not answer and left Ros- topchin's room more sullen and an- gry than he had ever before shown himself.
但是,當文字出現在彩色封面上時,結果就不那么完美了:
WEI' nrrd Peace Len Nlkelayevldu Iolfluy Readmg shmdd be ax wlnvame asnossxble Wenfler an mm m our cram: Llhvary - Leo Tmsloy was a Russian rwovelwst I and moval phflmopher med lur A ms Ideas 01 nonviolenx reswslance m 5 We range 0, "and"
如果想把文字加工成普通人可以看懂的 效果,還需要花很多時間去處理。
下一節將介紹另一種方法來解決文字混亂的問題,尤其是當你願意花一點兒時間訓練 Tesseract 的時候。
通過給 Tesseract 提供大量已知的文字與圖片映射集,經過訓練 Tesseract 就可以“學會”識別同一種字體,而且可以達到極高的精確率和准確率,甚至可以忽略圖 片中文字的背景色和相對位置等問題。
嘗試對知乎網驗證碼進行處理:
許多流行的內容管理系統即使加了驗證碼模塊,其眾所周知的注冊頁面也經常會遭到網絡 機器人的垃圾注冊。
那么,這些網絡機器人究,竟是怎么做的呢?既然我們已經,可以成功地識別出保存在電腦上 的驗證碼了,那么如何才能實現一個全能的網絡機器人呢?
大多數網站生成的驗證碼圖片都具有以下屬性。
- 它們是服務器端的程序動態生成的圖片。驗證碼圖片的 src 屬性可能和普通圖片不太一 樣,比如
<img src="WebForm.aspx?id=8AP85CQKE9TJ">
,但是可以和其他圖片一樣進行 下載和處理。 - 圖片的答案存儲在服務器端的數據庫里。
- 很多驗證碼都有時間限制,如果你太長時間沒解決就會失效。
- 常用的處理方法就是,首先把驗證碼圖片下載到硬盤里,清理干凈,然后用 Tesseract 處理 圖片,最后返回符合網站要求的識別結果。
#!/usr/bin/env python # -*- coding:utf-8 -*- import requests import time import pytesseract from PIL import Image from bs4 import BeautifulSoup def captcha(data): with open('captcha.jpg','wb') as fp: fp.write(data) time.sleep(1) image = Image.open("captcha.jpg") text = pytesseract.image_to_string(image) print "機器識別后的驗證碼為:" + text command = raw_input("請輸入Y表示同意使用,按其他鍵自行重新輸入:") if (command == "Y" or command == "y"): return text else: return raw_input('輸入驗證碼:') def zhihuLogin(username,password): # 構建一個保存Cookie值的session對象 sessiona = requests.Session() headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'} # 先獲取頁面信息,找到需要POST的數據(並且已記錄當前頁面的Cookie) html = sessiona.get('https://www.zhihu.com/#signin', headers=headers).content # 找到 name 屬性值為 _xsrf 的input標簽,取出value里的值 _xsrf = BeautifulSoup(html ,'lxml').find('input', attrs={'name':'_xsrf'}).get('value') # 取出驗證碼,r后面的值是Unix時間戳,time.time() captcha_url = 'https://www.zhihu.com/captcha.gif?r=%d&type=login' % (time.time() * 1000) response = sessiona.get(captcha_url, headers = headers) data = { "_xsrf":_xsrf, "email":username, "password":password, "remember_me":True, "captcha": captcha(response.content) } response = sessiona.post('https://www.zhihu.com/login/email', data = data, headers=headers) print response.text response = sessiona.get('https://www.zhihu.com/people/maozhaojun/activities', headers=headers) print response.text if __name__ == "__main__": #username = raw_input("username") #password = raw_input("password") zhihuLogin('xxxx@qq.com','ALAxxxxIME')
值得注意的是,有兩種異常情況會導致這個程序運行失敗。第一種情況是,如果 Tesseract 從驗證碼圖片中識別的結果不是四個字符(因為訓練樣本中驗證碼的所有有效答案都必須 是四個字符),結果不會被提交,程序失敗。第二種情況是雖然識別的結果是四個字符, 被提交到了表單,但是服務器對結果不認可,程序仍然失敗。
在實際運行過程中,第一種 情況發生的可能性大約為 50%,發生時程序不會向表單提交,程序直接結束並提示驗證碼 識別錯誤。第二種異常情況發生的概率約為 20%,四個字符都對的概率約是 30%(每個字 母的識別正確率大約是 80%,如果是五個字符都識別,正確的總概率是 32.8%)。