爬蟲(chóng)理解版本3

1 爬蟲(chóng)高級(jí)

1.1 動(dòng)態(tài)HTML處理和機(jī)器圖像識(shí)別

爬蟲(chóng)(Spider)性含,反爬蟲(chóng)(Anti-Spider)临燃,反反爬蟲(chóng)(Anti-Anti-Spider) 之間恢宏壯闊的斗爭(zhēng)...

Day 1

· 小莫想要某站上所有的電影橱乱,寫(xiě)了標(biāo)準(zhǔn)的爬蟲(chóng)(基于HttpClient庫(kù))办成,不斷地遍歷某站的電影列表頁(yè)面俱病,根據(jù) Html 分析電影名字存進(jìn)自己的數(shù)據(jù)庫(kù)蜜唾。

· 這個(gè)站點(diǎn)的運(yùn)維小黎發(fā)現(xiàn)某個(gè)時(shí)間段請(qǐng)求量陡增,分析日志發(fā)現(xiàn)都是 IP(xxx.xxx.xxx.xxx)這個(gè)用戶庶艾,并且 user-agent 還是 Python-urllib/2.7 ,基于這兩點(diǎn)判斷非人類后直接在服務(wù)器上封殺擎勘。

Day 2

· 小莫電影只爬了一半咱揍,于是也針對(duì)性的變換了下策略:1. user-agent 模仿百度("Baiduspider..."),2. IP每爬半個(gè)小時(shí)就換一個(gè)IP代理棚饵。

· 小黎也發(fā)現(xiàn)了對(duì)應(yīng)的變化煤裙,于是在服務(wù)器上設(shè)置了一個(gè)頻率限制掩完,每分鐘超過(guò)120次請(qǐng)求的再屏蔽IP。 同時(shí)考慮到百度家的爬蟲(chóng)有可能會(huì)被誤傷硼砰,想想市場(chǎng)部門(mén)每月幾十萬(wàn)的投放且蓬,于是寫(xiě)了個(gè)腳本,通過(guò) hostname 檢查下這個(gè) ip 是不是真的百度家的题翰,對(duì)這些 ip 設(shè)置一個(gè)白名單恶阴。

Day 3

· 小莫發(fā)現(xiàn)了新的限制后,想著我也不急著要這些數(shù)據(jù)豹障,留給服務(wù)器慢慢爬吧冯事,于是修改了代碼,隨機(jī)1-3秒爬一次血公,爬10次休息10秒昵仅,每天只在8-12,18-20點(diǎn)爬累魔,隔幾天還休息一下摔笤。

· 小黎看著新的日志頭都大了,再設(shè)定規(guī)則不小心會(huì)誤傷真實(shí)用戶垦写,于是準(zhǔn)備換了一個(gè)思路吕世,當(dāng)3個(gè)小時(shí)的總請(qǐng)求超過(guò)50次的時(shí)候彈出一個(gè)驗(yàn)證碼彈框,沒(méi)有準(zhǔn)確正確輸入的話就把 IP 記錄進(jìn)黑名單梯澜。

Day 4

· 小莫看到驗(yàn)證碼有些傻臉了寞冯,不過(guò)也不是沒(méi)有辦法,先去學(xué)習(xí)了圖像識(shí)別(關(guān)鍵詞 PIL晚伙,tesseract)吮龄,再對(duì)驗(yàn)證碼進(jìn)行了二值化,分詞咆疗,模式訓(xùn)練之后窑眯,總之最后識(shí)別了小黎的驗(yàn)證碼(關(guān)于驗(yàn)證碼,驗(yàn)證碼的識(shí)別舶得,驗(yàn)證碼的反識(shí)別也是一個(gè)恢弘壯麗的斗爭(zhēng)史...)懊昨,之后爬蟲(chóng)又跑了起來(lái)。

· 小黎是個(gè)不折不撓的好同學(xué)迅皇,看到驗(yàn)證碼被攻破后昧辽,和開(kāi)發(fā)同學(xué)商量了變化下開(kāi)發(fā)模式,數(shù)據(jù)并不再直接渲染登颓,而是由前端同學(xué)異步獲取搅荞,并且通過(guò) JavaScript 的加密庫(kù)生成動(dòng)態(tài)的 token,同時(shí)加密庫(kù)再進(jìn)行混淆(比較重要的步驟的確有網(wǎng)站這樣做,參見(jiàn)淘寶和微博的登陸流程)咕痛。

Day 5

· 混淆過(guò)的加密庫(kù)就沒(méi)有辦法了么痢甘?當(dāng)然不是,可以慢慢調(diào)試茉贡,找到加密原理塞栅,不過(guò)小莫不準(zhǔn)備用這么耗時(shí)耗力的方法,他放棄了基于 HttpClient的爬蟲(chóng)腔丧,選擇了內(nèi)置瀏覽器引擎的爬蟲(chóng)(關(guān)鍵詞:PhantomJS放椰,Selenium),在瀏覽器引擎運(yùn)行頁(yè)面悔据,直接獲取了正確的結(jié)果庄敛,又一次拿到了對(duì)方的數(shù)據(jù)。

· 小黎:.....

爬蟲(chóng)與發(fā)爬蟲(chóng)的斗爭(zhēng)還在繼續(xù)...

通常情況下科汗,在爬蟲(chóng)與反爬蟲(chóng)的對(duì)弈中藻烤,爬蟲(chóng)一定會(huì)勝利。

換言之头滔,只要人類能夠正常訪問(wèn)的網(wǎng)頁(yè)怖亭,爬蟲(chóng)在具備同等資源的情況下就一定可以抓取到。

關(guān)于爬蟲(chóng)部分一些建議:

  1. 盡量減少請(qǐng)求次數(shù)坤检,能抓列表頁(yè)就不抓詳情頁(yè)兴猩,減輕服務(wù)器壓力,程序員都是混口飯吃不容易早歇。

  2. 不要只看 Web 網(wǎng)站倾芝,還有手機(jī) App 和 H5,這樣的反爬蟲(chóng)措施一般比較少箭跳。

  3. 實(shí)際應(yīng)用時(shí)候晨另,一般防守方做到根據(jù) IP 限制頻次就結(jié)束了,除非很核心的數(shù)據(jù)谱姓,不會(huì)再進(jìn)行更多的驗(yàn)證借尿,畢竟成本的問(wèn)題會(huì)考慮到。

  4. 如果真的對(duì)性能要求很高屉来,可以考慮多線程(一些成熟的框架如 Scrapy都已支持)路翻,甚至分布式...

關(guān)于反爬蟲(chóng)部分的一些建議:

· 這篇文章就夠了:攜程技術(shù)中心 - 攜程酒店研發(fā)部研發(fā)經(jīng)理崔廣宇 <爬蟲(chóng)與反爬蟲(chóng)> 技術(shù)分享

1.1.1 動(dòng)態(tài)HTML介紹

JavaScript

JavaScript 是網(wǎng)絡(luò)上最常用也是支持者最多的客戶端腳本語(yǔ)言。它可以收集 用戶的跟蹤數(shù)據(jù),不需要重載頁(yè)面直接提交表單茄靠,在頁(yè)面嵌入多媒體文件茂契,甚至運(yùn)行網(wǎng)頁(yè)游戲。

我們可以在網(wǎng)頁(yè)源代碼的<scripy>標(biāo)簽里看到慨绳,比如:

<script type=

jQuery

jQuery 是一個(gè)十分常見(jiàn)的庫(kù),70% 最流行的網(wǎng)站(約 200 萬(wàn))和約 30% 的其他網(wǎng)站(約 2 億)都在使用账嚎。一個(gè)網(wǎng)站使用 jQuery 的特征,就是源代碼里包含了 jQuery 入口,比如:

<script type=

如果你在一個(gè)網(wǎng)站上看到了 jQuery莫瞬,那么采集這個(gè)網(wǎng)站數(shù)據(jù)的時(shí)候要格外小心。jQuery 可 以動(dòng)態(tài)地創(chuàng)建 HTML 內(nèi)容,只有在 JavaScript 代碼執(zhí)行之后才會(huì)顯示郭蕉。如果你用傳統(tǒng)的方 法采集頁(yè)面內(nèi)容,就只能獲得 JavaScript 代碼執(zhí)行之前頁(yè)面上的內(nèi)容。

Ajax

我們與網(wǎng)站服務(wù)器通信的唯一方式喂江,就是發(fā)出 HTTP 請(qǐng)求獲取新頁(yè)面召锈。如果提交表單之后,或從服務(wù)器獲取信息之后获询,網(wǎng)站的頁(yè)面不需要重新刷新涨岁,那么你訪問(wèn)的網(wǎng)站就在用Ajax 技術(shù)。

Ajax 其實(shí)并不是一門(mén)語(yǔ)言,而是用來(lái)完成網(wǎng)絡(luò)任務(wù)(可以認(rèn)為 它與網(wǎng)絡(luò)數(shù)據(jù)采集差不多)的一系列技術(shù)吉嚣。Ajax 全稱是 Asynchronous JavaScript and XML(異步 JavaScript 和 XML)梢薪,網(wǎng)站不需要使用單獨(dú)的頁(yè)面請(qǐng)求就可以和網(wǎng)絡(luò)服務(wù)器進(jìn)行交互 (收發(fā)信息)。

DHTML

Ajax 一樣尝哆,動(dòng)態(tài) HTML(Dynamic HTML, DHTML)也是一系列用于解決網(wǎng)絡(luò)問(wèn)題的 技術(shù)集合秉撇。DHTML 是用客戶端語(yǔ)言改變頁(yè)面的 HTML 元素(HTML、CSS秋泄,或者二者皆 被改變)琐馆。比如頁(yè)面上的按鈕只有當(dāng)用戶移動(dòng)鼠標(biāo)之后才出現(xiàn),背景色可能每次點(diǎn)擊都會(huì)改變,或者用一個(gè) Ajax 請(qǐng)求觸發(fā)頁(yè)面加載一段新內(nèi)容恒序,網(wǎng)頁(yè)是否屬于DHTML瘦麸,關(guān)鍵要看有沒(méi)有用 JavaScript 控制 HTML 和 CSS 元素。

那么歧胁,如何搞定滋饲?

那些使用了 Ajax 或 DHTML 技術(shù)改變 / 加載內(nèi)容的頁(yè)面,可能有一些采集手段喊巍。但是用 Python 解決這個(gè)問(wèn)題只有兩種途徑:

  1. 直接從 JavaScript 代碼里采集內(nèi)容(費(fèi)時(shí)費(fèi)力)

  2. 用 Python 的 第三方庫(kù)運(yùn)行 JavaScript屠缭,直接采集你在瀏覽器里看到的頁(yè)面(這個(gè)可以有)。

1.1.2 Selenium與PhantomJS

Selenium

Selenium是一個(gè)Web的自動(dòng)化測(cè)試工具玄糟,最初是為網(wǎng)站自動(dòng)化測(cè)試而開(kāi)發(fā)的勿她,類型像我們玩游戲用的按鍵精靈,可以按指定的命令自動(dòng)操作阵翎,不同是Selenium 可以直接運(yùn)行在瀏覽器上逢并,它支持所有主流的瀏覽器(包括PhantomJS這些無(wú)界面的瀏覽器)。

Selenium 可以根據(jù)我們的指令郭卫,讓瀏覽器自動(dòng)加載頁(yè)面砍聊,獲取需要的數(shù)據(jù),甚至頁(yè)面截屏贰军,或者判斷網(wǎng)站上某些動(dòng)作是否發(fā)生玻蝌。

Selenium 自己不帶瀏覽器蟹肘,不支持瀏覽器的功能,它需要與第三方瀏覽器結(jié)合在一起才能使用俯树。但是我們有時(shí)候需要讓它內(nèi)嵌在代碼中運(yùn)行帘腹,所以我們可以用一個(gè)叫 PhantomJS 的工具代替真實(shí)的瀏覽器。

可以從 PyPI 網(wǎng)站下載 Selenium庫(kù)https://pypi.python.org/simple/selenium 许饿,

也可以用 第三方管理器 pip用命令安裝:sudo pip install selenium

Selenium 官方參考文檔:http://selenium-python.readthedocs.io/index.html

PhantomJS

PhantomJS 是一個(gè)基于Webkit的“無(wú)界面”(headless)瀏覽器阳欲,它會(huì)把網(wǎng)站加載到內(nèi)存并執(zhí)行頁(yè)面上的 JavaScript,因?yàn)椴粫?huì)展示圖形界面陋率,所以運(yùn)行起來(lái)比完整的瀏覽器要高效球化。

如果我們把 Selenium 和 PhantomJS 結(jié)合在一起,就可以運(yùn)行一個(gè)非常強(qiáng)大的網(wǎng)絡(luò)爬蟲(chóng)了瓦糟,這個(gè)爬蟲(chóng)可以處理 JavaScrip筒愚、Cookie、headers菩浙,以及任何我們真實(shí)用戶需要做的事情巢掺。

PhantomJS 是一個(gè)功能完善(雖然無(wú)界面)的瀏覽器而非一個(gè) Python 庫(kù),所以它不需要像 Python 的其他庫(kù)一樣安裝芍耘,但我們可以通過(guò)Selenium調(diào)用PhantomJS來(lái)直接使用址遇。

PhantomJS 官方參考文檔:http://phantomjs.org/documentation

安裝示例1:

  1. 下載PhantomJS:

<pre style="background:#F7F7F7">wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2</pre>

  1. 解壓并創(chuàng)建軟連接:

tar -xvjf phantomjs-2.1.1-linux-x86_64.tar.bz2

sudo cp -R phantomjs-2.1.1-linux-x86_64 /usr/local/share/

sudo ln -sf /usr/local/share/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/

安裝實(shí)例2:

|

1、下載安裝

sudo apt update

sudo apt install phantomjs

2斋竞、下載driver

http://chromedriver.storage.googleapis.com/index.html

3倔约、修改權(quán)限、移動(dòng)

|

<v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"><v:formulas></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path></v:stroke></v:shapetype><v:shape id="圖片_x0020_44" o:spid="_x0000_i1035" type="#_x0000_t75" style="width:223.8pt;height:303.6pt;visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png" o:title=""></v:imagedata></v:shape>

ubuntu16.04 chrome瀏覽器安裝方式

https://jingyan.baidu.com/article/335530da98061b19cb41c31d.html

快速入門(mén)

Selenium 庫(kù)里有個(gè)叫 WebDriver 的 API坝初。WebDriver 有點(diǎn)兒像可以加載網(wǎng)站的瀏覽器浸剩,但是它也可以像 BeautifulSoup 或者其他 Selector 對(duì)象一樣用來(lái)查找頁(yè)面元素,與頁(yè)面上的元素進(jìn)行交互 (發(fā)送文本鳄袍、點(diǎn)擊等)绢要,以及執(zhí)行其他動(dòng)作來(lái)運(yùn)行網(wǎng)絡(luò)爬蟲(chóng)。

<pre style="background:#F7F7F7"># IPython測(cè)試代碼</pre>

<pre style="background:#F7F7F7"># 導(dǎo)入 webdriver</pre>

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

<pre style="background:#F7F7F7"># 調(diào)用環(huán)境變量指定的PhantomJS瀏覽器創(chuàng)建瀏覽器對(duì)象</pre>

driver = webdriver.PhantomJS()

<pre style="background:#F7F7F7"># 如果沒(méi)有在環(huán)境變量指定PhantomJS位置</pre>

<pre style="background:#F7F7F7"># driver = webdriver.PhantomJS(executable_path="/usr/local/bin/phantomjs"))</pre>

<pre style="background:#F7F7F7"># get方法會(huì)一直等到頁(yè)面被完全加載拗小,然后才會(huì)繼續(xù)程序重罪,通常測(cè)試會(huì)在這里選擇 time.sleep(2)</pre>

driver.get(

<pre style="background:#F7F7F7"># 獲取頁(yè)面名為 wrapper的id標(biāo)簽的文本內(nèi)容</pre>

data = driver.find_element_by_id(

<pre style="background:#F7F7F7"># 打印數(shù)據(jù)內(nèi)容</pre>

<pre style="background:#F7F7F7">print(data)</pre>

<pre style="background:#F7F7F7"># 打印頁(yè)面標(biāo)題 "百度一下,你就知道"</pre>

<pre style="background:#F7F7F7">print(driver.title)</pre>

<pre style="background:#F7F7F7"># 生成當(dāng)前頁(yè)面快照并保存</pre>

driver.save_screenshot(

<pre style="background:#F7F7F7"># id="kw"是百度搜索輸入框哀九,輸入字符串"長(zhǎng)城"</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># id="su"是百度搜索按鈕剿配,click() 是模擬點(diǎn)擊</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># 獲取新的頁(yè)面快照</pre>

driver.save_screenshot(

<pre style="background:#F7F7F7"># 打印網(wǎng)頁(yè)渲染后的源代碼</pre>

<pre style="background:#F7F7F7">print(driver.page_source)</pre>

<pre style="background:#F7F7F7"># 獲取當(dāng)前頁(yè)面Cookie</pre>

<pre style="background:#F7F7F7">print(driver.get_cookies())</pre>

<pre style="background:#F7F7F7"># 調(diào)用鍵盤(pán)按鍵操作時(shí)需要引入的Keys包</pre>

<pre style="background:#F7F7F7">from selenium.webdriver.common.keys import Keys</pre>

<pre style="background:#F7F7F7"># ctrl+a 全選輸入框內(nèi)容</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># ctrl+x 剪切輸入框內(nèi)容</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># 輸入框重新輸入內(nèi)容</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># 模擬Enter回車鍵</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># 清除輸入框內(nèi)容</pre>

driver.find_element_by_id(

<pre style="background:#F7F7F7"># 生成新的頁(yè)面快照</pre>

driver.save_screenshot(

<pre style="background:#F7F7F7"># 獲取當(dāng)前url</pre>

<pre style="background:#F7F7F7">print(driver.current_url)</pre>

<pre style="background:#F7F7F7"># 關(guān)閉當(dāng)前頁(yè)面,如果只有一個(gè)頁(yè)面阅束,會(huì)關(guān)閉瀏覽器</pre>

<pre style="background:#F7F7F7"># driver.close()</pre>

<pre style="background:#F7F7F7"># 關(guān)閉瀏覽器</pre>

driver.quit()

頁(yè)面操作

Selenium 的 WebDriver提供了各種方法來(lái)尋找元素呼胚,假設(shè)下面有一個(gè)表單輸入框:

<pre style="background:#F7F7F7"><input type="text" name="user-name" id="passwd-id" /></pre>

那么:

<pre style="background:#F7F7F7"># 獲取id標(biāo)簽值</pre>

element = driver.find_element_by_id(

<pre style="background:#F7F7F7"># 獲取name標(biāo)簽值</pre>

element = driver.find_element_by_name(

<pre style="background:#F7F7F7"># 獲取標(biāo)簽名值</pre>

element = driver.find_elements_by_tag_name(

<pre style="background:#F7F7F7"># 也可以通過(guò)XPath來(lái)匹配</pre>

element = driver.find_element_by_xpath(

定位UI元素 (WebElements)

關(guān)于元素的選取,有如下的API 單個(gè)元素選取

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
  1. By ID

|

<div id="coolestWidgetEvah">...</div>

實(shí)現(xiàn)

element = driver.find_element_by_id("coolestWidgetEvah")

------------------------ or -------------------------

from selenium.webdriver.common.by import By

element = driver.find_element(by=By.ID, value="coolestWidgetEvah")

|

  1. By Class Name

|

<div class="cheese"><span>Cheddar</span></div><div class="cheese"><span>Gouda</span></div>

實(shí)現(xiàn)

cheeses = driver.find_elements_by_class_name("cheese")

------------------------ or -------------------------

from selenium.webdriver.common.by import By

cheeses = driver.find_elements(By.CLASS_NAME, "cheese")

|

  1. By Tag Name

|

<iframe src="..."></iframe>

實(shí)現(xiàn)

frame = driver.find_element_by_tag_name("iframe")

------------------------ or -------------------------

from selenium.webdriver.common.by import By

frame = driver.find_element(By.TAG_NAME, "iframe")

|

  1. By Name

|

<input name="cheese" type="text"/>

實(shí)現(xiàn)

cheese = driver.find_element_by_name("cheese")

------------------------ or -------------------------

from selenium.webdriver.common.by import By

cheese = driver.find_element(By.NAME, "cheese")

|

  1. By Link Text

|

<a >cheese</a>

實(shí)現(xiàn)

cheese = driver.find_element_by_link_text("cheese")

------------------------ or -------------------------

from selenium.webdriver.common.by import By

cheese = driver.find_element(By.LINK_TEXT, "cheese")

|

  1. By Partial Link Text

|

<a >search for cheese</a>>

實(shí)現(xiàn)

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")

|

  1. By CSS

|

<div id="food"><span class="dairy">milk</span><span class="dairy aged">cheese</span></div>

實(shí)現(xiàn)

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")

|

  1. By XPath

|

<input type="text" name="example" />

<input type="text" name="other" />

實(shí)現(xiàn)

inputs = driver.find_elements_by_xpath("http://input")

------------------------ or -------------------------

from selenium.webdriver.common.by import By

inputs = driver.find_elements(By.XPATH, "http://input")

|

鼠標(biāo)動(dòng)作鏈

有些時(shí)候息裸,我們需要再頁(yè)面上模擬一些鼠標(biāo)操作蝇更,比如雙擊沪编、右擊、拖拽甚至按住不動(dòng)等年扩,我們可以通過(guò)導(dǎo)入 ActionChains 類來(lái)做到:

示例:

<pre style="background:#F7F7F7">#導(dǎo)入 ActionChains 類</pre>

<pre style="background:#F7F7F7">from selenium.webdriver import ActionChains</pre>

<pre style="background:#F7F7F7"># 鼠標(biāo)移動(dòng)到 ac 位置</pre>

ac = driver.find_element_by_xpath(
ActionChains(driver).move_to_element(ac).perform()

<pre style="background:#F7F7F7"># 在 ac 位置單擊</pre>

ac = driver.find_element_by_xpath(
ActionChains(driver).move_to_element(ac).click(ac).perform()

<pre style="background:#F7F7F7"># 在 ac 位置雙擊</pre>

ac = driver.find_element_by_xpath(
ActionChains(driver).move_to_element(ac).double_click(ac).perform()

<pre style="background:#F7F7F7"># 在 ac 位置右擊</pre>

ac = driver.find_element_by_xpath(
ActionChains(driver).move_to_element(ac).context_click(ac).perform()

<pre style="background:#F7F7F7"># 在 ac 位置左鍵單擊hold住</pre>

ac = driver.find_element_by_xpath(
ActionChains(driver).move_to_element(ac).click_and_hold(ac).perform()

<pre style="background:#F7F7F7"># 將 ac1 拖拽到 ac2 位置</pre>

ac1 = driver.find_element_by_xpath(
ac2 = driver.find_element_by_xpath(
ActionChains(driver).drag_and_drop(ac1, ac2).perform()

填充表單

我們已經(jīng)知道了怎樣向文本框中輸入文字蚁廓,但是有時(shí)候我們會(huì)碰到<select> </select>標(biāo)簽的下拉框。直接點(diǎn)擊下拉框中的選項(xiàng)不一定可行常遂。

<pre style="background:#F7F7F7"><select id="status" class="form-control valid" onchange="" name="status"></pre>






<pre style="background:#F7F7F7"></select></pre>

<v:shape id="圖片_x0020_25" o:spid="_x0000_i1034" type="#_x0000_t75" style="width:159pt;height:143.4pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png" o:title=""></v:imagedata></v:shape>

Selenium專門(mén)提供了Select類來(lái)處理下拉框纳令。 其實(shí) WebDriver 中提供了一個(gè)叫 Select 的方法,可以幫助我們完成這些事情:

導(dǎo)入 Select 類

from selenium.webdriver.support.ui import Select

找到 name 的選項(xiàng)卡

select = Select(driver.find_element_by_name('status'))

select.select_by_index(1)

select.select_by_value("0")

select.select_by_visible_text("未審核")

以上是三種選擇下拉框的方式克胳,它可以根據(jù)索引來(lái)選擇,可以根據(jù)值來(lái)選擇圈匆,可以根據(jù)文字來(lái)選擇漠另。注意:

  1. index 索引從 0 開(kāi)始

  2. value是option標(biāo)簽的一個(gè)屬性值,并不是顯示在下拉框中的值

  3. visible_text是在option標(biāo)簽文本的值跃赚,是顯示在下拉框的值

全部取消選擇怎么辦呢笆搓?很簡(jiǎn)單:

select.deselect_all()

彈窗處理

當(dāng)你觸發(fā)了某個(gè)事件之后,頁(yè)面出現(xiàn)了彈窗提示纬傲,處理這個(gè)提示或者獲取提示信息方法如下:

alert = driver.switch_to_alert()

頁(yè)面切換

一個(gè)瀏覽器肯定會(huì)有很多窗口满败,所以我們肯定要有方法來(lái)實(shí)現(xiàn)窗口的切換。切換窗口的方法如下:

driver.switch_to.window(

也可以使用 window_handles 方法來(lái)獲取每個(gè)窗口的操作對(duì)象叹括。例如:

<pre style="background:#F7F7F7">for handle in driver.window_handles:</pre>

    driver.switch_to_window(handle)

頁(yè)面前進(jìn)和后退

操作頁(yè)面的前進(jìn)和后退功能:

driver.forward()     
driver.back()        

Cookies

獲取頁(yè)面每個(gè)Cookies值算墨,用法如下

<pre style="background:#F7F7F7">for cookie in driver.get_cookies():</pre>


刪除Cookies,用法如下

<pre style="background:#F7F7F7"># By name</pre>

driver.delete_cookie(

<pre style="background:#F7F7F7"># all</pre>

driver.delete_all_cookies()

頁(yè)面等待

現(xiàn)在的網(wǎng)頁(yè)越來(lái)越多采用了 Ajax 技術(shù)汁雷,這樣程序便不能確定何時(shí)某個(gè)元素完全加載出來(lái)了净嘀。如果實(shí)際頁(yè)面等待時(shí)間過(guò)長(zhǎng)導(dǎo)致某個(gè)dom元素還沒(méi)出來(lái),但是你的代碼直接使用了這個(gè)WebElement侠讯,那么就會(huì)拋出NullPointer的異常挖藏。

為了避免這種元素定位困難而且會(huì)提高產(chǎn)生 ElementNotVisibleException 的概率。所以 Selenium 提供了兩種等待方式厢漩,一種是隱式等待膜眠,一種是顯式等待。

隱式等待是等待特定的時(shí)間溜嗜,顯式等待是指定某一條件直到這個(gè)條件成立時(shí)繼續(xù)執(zhí)行宵膨。

1.顯式等待

顯式等待指定某個(gè)條件,然后設(shè)置最長(zhǎng)等待時(shí)間粱胜。如果在這個(gè)時(shí)間還沒(méi)有找到元素柄驻,那么便會(huì)拋出異常了。

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

<pre style="background:#F7F7F7">from selenium.webdriver.common.by import By</pre>

<pre style="background:#F7F7F7"># WebDriverWait 庫(kù)焙压,負(fù)責(zé)循環(huán)等待</pre>

<pre style="background:#F7F7F7">from selenium.webdriver.support.ui import WebDriverWait</pre>

<pre style="background:#F7F7F7"># expected_conditions 類鸿脓,負(fù)責(zé)條件出發(fā)</pre>

<pre style="background:#F7F7F7">from selenium.webdriver.support import expected_conditions as EC</pre>

driver = webdriver.PhantomJS()
driver.get(

<pre style="background:#F7F7F7">try:</pre>


    element = WebDriverWait(driver, 
        EC.presence_of_element_located((By.ID, 
    )

<pre style="background:#F7F7F7">finally:</pre>

    driver.quit()

如果不寫(xiě)參數(shù)抑钟,程序默認(rèn)會(huì) 0.5s 調(diào)用一次來(lái)查看元素是否已經(jīng)生成,如果本來(lái)元素就是存在的野哭,那么會(huì)立即返回在塔。

下面是一些內(nèi)置的等待條件,你可以直接調(diào)用這些條件拨黔,而不用自己寫(xiě)某些等待條件了蛔溃。

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

2.隱式等待

隱式等待比較簡(jiǎn)單,就是簡(jiǎn)單地設(shè)置一個(gè)等待時(shí)間篱蝇,單位為秒贺待。

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

driver = webdriver.PhantomJS()
driver.implicitly_wait(
driver.get(
myDynamicElement = driver.find_element_by_id(

當(dāng)然如果不設(shè)置,默認(rèn)等待時(shí)間為0零截。

1.1.3 案例一:網(wǎng)站模擬登錄

<pre style="background:#F7F7F7"># -- coding:utf-8 -- </pre>

<pre style="background:#F7F7F7"># douban.py</pre>

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

<pre style="background:#F7F7F7">from selenium.webdriver.common.keys import Keys</pre>

<pre style="background:#F7F7F7">import time</pre>

driver = webdriver.PhantomJS()
driver.get(

<pre style="background:#F7F7F7"># 輸入賬號(hào)密碼</pre>

driver.find_element_by_name(
driver.find_element_by_name(

<pre style="background:#F7F7F7"># 模擬點(diǎn)擊登錄</pre>

driver.find_element_by_xpath(

<pre style="background:#F7F7F7"># 等待3秒</pre>

time.sleep(

<pre style="background:#F7F7F7"># 生成登陸后快照</pre>

driver.save_screenshot(

<pre style="background:#F7F7F7"># 保存源碼</pre>

<pre style="background:#F7F7F7">with open("douban.html", "w") as file:</pre>

    file.write(driver.page_source)
driver.quit()

1.1.4 案例二:動(dòng)態(tài)頁(yè)面模擬點(diǎn)擊

爬取斗魚(yú)直播平臺(tái)的所有房間信息:

<pre style="background:#F7F7F7"># python的測(cè)試模塊</pre>

<pre style="background:#F7F7F7">import unittest</pre>

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

<pre style="background:#F7F7F7">from bs4 import BeautifulSoup</pre>

<pre style="background:#F7F7F7">class douyuSelenium(unittest.TestCase):</pre>



        self.driver = webdriver.PhantomJS()


        self.driver.get(


            soup = BeautifulSoup(driver.page_source, 

            titles = soup.find_all(
            nums = soup.find_all(







            self.driver.find_element_by_class_name(



        self.driver.quit()

<pre style="background:#F7F7F7">if __name__ == "main":</pre>

    unittest.main()

1.1.5 案例三:執(zhí)行 JavaScript 語(yǔ)句

1.隱藏百度圖片

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

driver = webdriver.PhantomJS()
driver.get(

<pre style="background:#F7F7F7"># 給搜索輸入框標(biāo)紅的javascript腳本</pre>

js = 

<pre style="background:#F7F7F7"># 調(diào)用給搜索輸入框標(biāo)紅js腳本</pre>

driver.execute_script(js)

<pre style="background:#F7F7F7">#查看頁(yè)面快照</pre>

driver.save_screenshot(

<pre style="background:#F7F7F7">#js隱藏元素麸塞,將獲取的圖片元素隱藏</pre>

img = driver.find_element_by_xpath(
driver.execute_script(

<pre style="background:#F7F7F7"># 向下滾動(dòng)到頁(yè)面底部</pre>

driver.execute_script(

<pre style="background:#F7F7F7">#查看頁(yè)面快照</pre>

driver.save_screenshot(
driver.quit()

2.模擬滾動(dòng)條滾動(dòng)到底部

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

<pre style="background:

F7F7F7">import time</pre>

driver = webdriver.PhantomJS()
driver.get(

<pre style="background:#F7F7F7"># 向下滾動(dòng)10000像素</pre>

js = 

<pre style="background:#F7F7F7">#js="var q=document.documentElement.scrollTop=10000"</pre>

time.sleep(

<pre style="background:#F7F7F7">#查看頁(yè)面快照</pre>

driver.save_screenshot(

<pre style="background:#F7F7F7"># 執(zhí)行JS語(yǔ)句</pre>

driver.execute_script(js)
time.sleep(

<pre style="background:#F7F7F7">#查看頁(yè)面快照</pre>

driver.save_screenshot(
driver.quit()

1.1.6 機(jī)器視覺(jué)與Tesseract介紹

機(jī)器視覺(jué)

從 Google 的無(wú)人駕駛汽車到可以識(shí)別假鈔的自動(dòng)售賣(mài)機(jī),機(jī)器視覺(jué)一直都是一個(gè)應(yīng)用廣 泛且具有深遠(yuǎn)的影響和雄偉的愿景的領(lǐng)域涧衙。

我們將重點(diǎn)介紹機(jī)器視覺(jué)的一個(gè)分支:文字識(shí)別哪工,介紹如何用一些 Python庫(kù)來(lái)識(shí)別和使用在線圖片中的文字。

我們可以很輕松的閱讀圖片里的文字弧哎,但是機(jī)器閱讀這些圖片就會(huì)非常困難雁比,利用這種人類用戶可以正常讀取但是大多數(shù)機(jī)器人都沒(méi)法讀取的圖片,驗(yàn)證碼 (CAPTCHA)就出現(xiàn)了撤嫩。驗(yàn)證碼讀取的難易程度也大不相同偎捎,有些驗(yàn)證碼比其他的更加難讀。

將圖像翻譯成文字一般被稱為 **光學(xué)文字識(shí)別**(Optical Character Recognition, OCR)非洲⊙枷蓿可以實(shí)現(xiàn)OCR的底層庫(kù)并不多,目前很多庫(kù)都是使用共同的幾個(gè)底層 OCR 庫(kù),或者是在上面 進(jìn)行定制。

ORC庫(kù)概述

在讀取和處理圖像两踏、圖像相關(guān)的機(jī)器學(xué)習(xí)以及創(chuàng)建圖像等任務(wù)中败京,Python 一直都是非常出色的語(yǔ)言。雖然有很多庫(kù)可以進(jìn)行圖像處理梦染,但在這里我們只重點(diǎn)介紹: Tesseract

Tesseract

Tesseract 是一個(gè) OCR 庫(kù),目前由 Google 贊助(Google 也是一家以 OCR 和機(jī)器學(xué)習(xí)技術(shù)聞名于世的公司)赡麦。Tesseract 是目前公認(rèn)最優(yōu)秀、最精確的開(kāi)源 OCR 系統(tǒng)帕识,除了極高的精確度泛粹,Tesseract 也具有很高的靈活性。它可以通過(guò)訓(xùn)練識(shí)別出任何字體肮疗,也可以識(shí)別出任何 Unicode 字符晶姊。

安裝Tesseract

  1. Windows 系統(tǒng)

下載可執(zhí)行安裝文件:https://code.google.com/p/tesseract-ocr/downloads/list 安裝。

  1. Ubuntu Linux系統(tǒng)

可以通過(guò) apt-get 安裝: $sudo apt-get tesseract-ocr

  1. Mac OS X系統(tǒng)

Homebrew可以很方便地安裝: brew install tesseract

要使用 Tesseract 的功能伪货,比如后面的示例中訓(xùn)練程序識(shí)別字母们衙,要先在系統(tǒng)中設(shè)置一 個(gè)新的環(huán)境變量 $TESSDATA_PREFIX钾怔,讓 Tesseract 知道訓(xùn)練的數(shù)據(jù)文件存儲(chǔ)在哪里,然后搞一份tessdata數(shù)據(jù)文件蒙挑,放到Tesseract目錄下宗侦。

  1. 在大多數(shù) Linux 系統(tǒng)和 Mac OS X 系統(tǒng)上,你可以這么設(shè)置(假設(shè)Tesseract數(shù)據(jù)文件目錄在/usr/local/share/下): **$export TESSDATA_PREFIX=/usr/local/share/Tesseract**

  2. 在 Windows 系統(tǒng)上也類似,你可以通過(guò)下面這行命令設(shè)置環(huán)境變量: **#setx TESSDATA_PREFIX C:\Program Files\Tesseract OCR\Tesseract**

安裝pytesseract

Tesseract 是一個(gè)命令行工具,安裝之后忆蚀,要用 tesseract 命令在 Python 的外面運(yùn)行矾利,但我們可以通過(guò) pip 安裝支持 Python 版本的 Tesseract庫(kù):pytesseract

pip install pytesseract

1.1.7 處理一些格式規(guī)范的文字

處理給規(guī)范的文字

處理的大多數(shù)文字最好都是比較干凈、格式規(guī)范的馋袜。格式規(guī)范的文字通衬衅欤可以滿足一些需求,通常格式規(guī)范的文字具有以下特點(diǎn):

? 使用一個(gè)標(biāo)準(zhǔn)字體(不包含手寫(xiě)體欣鳖、草書(shū),或者十分“花哨的”字體)

? 即使被復(fù)印或拍照剑肯,字體還是很清晰,沒(méi)有多余的痕跡或污點(diǎn)

? 排列整齊观堂,沒(méi)有歪歪斜斜的字

? 沒(méi)有超出圖片范圍,也沒(méi)有殘缺不全呀忧,或緊緊貼在圖片的邊緣

文字的一些格式問(wèn)題在圖片預(yù)處理時(shí)可以進(jìn)行解決师痕。例如,可以把圖片轉(zhuǎn)換成灰度圖,調(diào)整亮度和對(duì)比度而账,還可以根據(jù)需要進(jìn)行裁剪和旋轉(zhuǎn)(詳情需要了解圖像與信號(hào)處理)等。

格式規(guī)范文字的理想示例

<v:shape id="圖片_x0020_42" o:spid="_x0000_i1033" type="#_x0000_t75" style="width:415.2pt;height:69.6pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image003.png" o:title=""></v:imagedata></v:shape>

通過(guò)下面的命令運(yùn)行 Tesseract泞辐,讀取文件并把結(jié)果寫(xiě)到一個(gè)文本文件中:

|

tesseract test.jpg text

cat text.txt

|

識(shí)別結(jié)果很準(zhǔn)確,不過(guò)符號(hào)^*分別被表示成了雙引號(hào)和單引號(hào)笔横。大體上可以讓你很舒服地閱讀。

通過(guò)Python代碼實(shí)現(xiàn)

<pre style="background:#F7F7F7">import pytesseract</pre>

<pre style="background:

F7F7F7">from PIL import Image</pre>

image = Image.open(
text = pytesseract.image_to_string(image)

<pre style="background:#F7F7F7">print(text)</pre>

運(yùn)行結(jié)果:

This is some text, written in Arial, that will be read by
Tesseract. Here are some symbols: !@#$%"&*()

對(duì)圖片進(jìn)行閾值過(guò)濾和降噪處理(了解即可)

很多時(shí)候我們?cè)诰W(wǎng)上會(huì)看到這樣的圖片:

<v:shape id="圖片_x0020_43" o:spid="_x0000_i1032" type="#_x0000_t75" style="width:415.2pt;height:69pt;visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png" o:title=""></v:imagedata></v:shape>

tesseract 不能完整處理這個(gè)圖片,主要是因?yàn)閳D片背景色是漸變的,最終結(jié)果是這樣:

<v:shape id="圖片_x0020_46" o:spid="_x0000_i1031" type="#_x0000_t75" style="width:414.6pt;height:72.6pt;visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image005.png" o:title=""></v:imagedata></v:shape>

隨著背景色從左到右不斷加深,文字變得越來(lái)越難以識(shí)別,Tesseract 識(shí)別出的 每一行的最后幾個(gè)字符都是錯(cuò)的咐吼。

遇到這類問(wèn)題,可以先用 Python 腳本對(duì)圖片進(jìn)行清理吹缔。利用 PIL 庫(kù),我們可以創(chuàng)建一個(gè)閾值過(guò)濾器來(lái)去掉漸變的背景色,只把文字留下來(lái)锯茄,從而讓圖片更加清晰厢塘,便于 Tesseract 讀取:

from PIL import Image

import subprocess

def cleanFile(filePath, newFilePath):

image = Image.open(filePath)

對(duì)圖片進(jìn)行閾值過(guò)濾(低于143的置為黑色,否則為白色)

image = image.point(lambda x: 0 if x < 143 else 255)

重新保存圖片

image.save(newFilePath)

調(diào)用系統(tǒng)的tesseract命令對(duì)圖片進(jìn)行OCR識(shí)別

subprocess.call(["tesseract", newFilePath, "output"])

打開(kāi)文件讀取結(jié)果

with open("output.txt", 'r') as f:

print(f.read())

if name == "main":

cleanFile("text2.png", "text2clean.png")

通過(guò)一個(gè)閾值對(duì)前面的“模糊”圖片進(jìn)行過(guò)濾的結(jié)果

<v:shape id="圖片_x0020_47" o:spid="_x0000_i1030" type="#_x0000_t75" style="width:415.2pt;height:69pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png" o:title=""></v:imagedata></v:shape>

除了一些標(biāo)點(diǎn)符號(hào)不太清晰或丟失了,大部分文字都被讀出來(lái)了肌幽。Tesseract 給出了最好的 結(jié)果:

<v:shape id="圖片_x0020_48" o:spid="_x0000_i1029" type="#_x0000_t75" style="width:418.2pt;height:158.4pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image007.png" o:title=""></v:imagedata></v:shape>

從網(wǎng)站圖片中抓取文字

用 Tesseract 讀取硬盤(pán)里圖片上的文字,可能不怎么令人興奮,但當(dāng)我們把它和網(wǎng)絡(luò)爬蟲(chóng)組合使用時(shí),就能成為一個(gè)強(qiáng)大的工具晚碾。

網(wǎng)站上的圖片可能并不是故意把文字做得很花哨 (就像餐館菜單的 JPG 圖片上的藝術(shù)字),但它們上面的文字對(duì)網(wǎng)絡(luò)爬蟲(chóng)來(lái)說(shuō)就是隱藏起來(lái) 了,舉個(gè)例子:

  1. 雖然亞馬遜的 robots.txt 文件允許抓取網(wǎng)站的產(chǎn)品頁(yè)面,但是圖書(shū)的預(yù)覽頁(yè)通常不讓網(wǎng)絡(luò)機(jī) 器人采集喂急。

  2. 圖書(shū)的預(yù)覽頁(yè)是通過(guò)用戶觸發(fā) Ajax 腳本進(jìn)行加載的,預(yù)覽圖片隱藏在 div 節(jié)點(diǎn) 下面;其實(shí),普通的訪問(wèn)者會(huì)覺(jué)得它們看起來(lái)更像是一個(gè) Flash 動(dòng)畫(huà),而不是一個(gè)圖片文 件格嘁。當(dāng)然,即使我們能獲得圖片,要把它們讀成文字也沒(méi)那么簡(jiǎn)單。

  3. 下面的程序就解決了這個(gè)問(wèn)題:首先導(dǎo)航到托爾斯泰的《戰(zhàn)爭(zhēng)與和平》的大字號(hào)印刷版 1, 打開(kāi)閱讀器,收集圖片的 URL 鏈接,然后下載圖片,識(shí)別圖片,最后打印每個(gè)圖片的文 字廊移。因?yàn)檫@個(gè)程序很復(fù)雜,利用了前面幾章的多個(gè)程序片段,所以我增加了一些注釋以讓 每段代碼的目的更加清晰:

<pre style="background:#F7F7F7">import time</pre>

<pre style="background:#F7F7F7">from urllib.request import urlretrieve </pre>

<pre style="background:

F7F7F7">import subprocess</pre>

<pre style="background:#F7F7F7">from selenium import webdriver</pre>

<pre style="background:

F7F7F7">#創(chuàng)建新的Selenium driver</pre>

driver = webdriver.PhantomJS()

<pre style="background:#F7F7F7"># 用Selenium試試Firefox瀏覽器:</pre>

<pre style="background:#F7F7F7"># driver = webdriver.Firefox()</pre>

driver.get(

<pre style="background:#F7F7F7"># 單擊圖書(shū)預(yù)覽按鈕 driver.find_element_by_id("sitbLogoImg").click() imageList = set()</pre>

<pre style="background:#F7F7F7"># 等待頁(yè)面加載完成</pre>

time.sleep(

<pre style="background:#F7F7F7"># 當(dāng)向右箭頭可以點(diǎn)擊時(shí),開(kāi)始翻頁(yè)</pre>

<pre style="background:#F7F7F7">while"pointer"in driver.find_element_by_id("sitbReaderRightPageTurner").get_attribute("style"):</pre>

    driver.find_element_by_id(
    time.sleep(

    pages = driver.find_elements_by_xpath(

        image = page.get_attribute(
        imageList.add(image)
driver.quit()

<pre style="background:#F7F7F7"># 用Tesseract處理我們收集的圖片URL鏈接 </pre>

<pre style="background:#F7F7F7">for image in sorted(imageList):</pre>


    urlretrieve(image, 
    p = subprocess.Popen([
    f = open(
    p.wait() print(f.read())

和我們前面使用 Tesseract 讀取的效果一樣糕簿,這個(gè)程序也會(huì)完美地打印書(shū)中很多長(zhǎng)長(zhǎng)的段落探入,第六頁(yè)的預(yù)覽如下所示:

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.

但是當(dāng)文字出現(xiàn)在彩色封面上時(shí),結(jié)果就不那么完美了:

   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"

如果想把文字加工成普通人可以看懂的效果冶伞,還需要花很多時(shí)間去處理新症。

比如,通過(guò)給 Tesseract 提供大量已知的文字與圖片映射集响禽,經(jīng)過(guò)訓(xùn)練 Tesseract 就可以“學(xué)會(huì)”識(shí)別同一種字體徒爹,而且可以達(dá)到極高的精確率和準(zhǔn)確率,甚至可以忽略圖片中文字的背景色和相對(duì)位置等問(wèn)題芋类。

1.1.8 案例:嘗試對(duì)驗(yàn)證碼進(jìn)行機(jī)器識(shí)別處理

許多流行的內(nèi)容管理系統(tǒng)即使加了驗(yàn)證碼模塊隆嗅,其眾所周知的注冊(cè)頁(yè)面也經(jīng)常會(huì)遭到網(wǎng)絡(luò) 機(jī)器人的垃圾注冊(cè)。

那么侯繁,這些網(wǎng)絡(luò)機(jī)器人究胖喳,竟是怎么做的呢?既然我們已經(jīng),可以成功地識(shí)別出保存在電腦上 的驗(yàn)證碼了贮竟,那么如何才能實(shí)現(xiàn)一個(gè)全能的網(wǎng)絡(luò)機(jī)器人呢?

大多數(shù)網(wǎng)站生成的驗(yàn)證碼圖片都具有以下屬性丽焊。

  1. 它們是服務(wù)器端的程序動(dòng)態(tài)生成的圖片。驗(yàn)證碼圖片的 src 屬性可能和普通圖片不太一 樣咕别,比如 <img src="WebForm.aspx?id=8AP85CQKE9TJ">技健,但是可以和其他圖片一樣進(jìn)行 下載和處理。

  2. 圖片的答案存儲(chǔ)在服務(wù)器端的數(shù)據(jù)庫(kù)里惰拱。

  3. 很多驗(yàn)證碼都有時(shí)間限制雌贱,如果你太長(zhǎng)時(shí)間沒(méi)解決就會(huì)失效。

  4. 常用的處理方法就是偿短,首先把驗(yàn)證碼圖片下載到硬盤(pán)里欣孤,清理干凈,然后用 Tesseract 處理 圖片昔逗,最后返回符合網(wǎng)站要求的識(shí)別結(jié)果降传。

|

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 "機(jī)器識(shí)別后的驗(yàn)證碼為:" + text

command = input("請(qǐng)輸入Y表示同意使用,按其他鍵自行重新輸入:")

if (command == "Y" or command == "y"):

return text

else:

return input('輸入驗(yàn)證碼:')

def zhihuLogin(username,password):

構(gòu)建一個(gè)保存Cookie值的session對(duì)象

sessiona = requests.Session()

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0'}

先獲取頁(yè)面信息纤子,找到需要POST的數(shù)據(jù)(并且已記錄當(dāng)前頁(yè)面的Cookie)

html = sessiona.get('https://www.zhihu.com/#signin', headers=headers).content

找到 name 屬性值為 _xsrf 的input標(biāo)簽搬瑰,取出value里的值

_xsrf = BeautifulSoup(html ,'lxml').find('input', attrs={'name':'_xsrf'}).get('value')

取出驗(yàn)證碼,r后面的值是Unix時(shí)間戳,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')

|

嘗試處理中文字符

如果手頭上有中文的訓(xùn)練數(shù)據(jù)控硼,也可以嘗試對(duì)中文進(jìn)行識(shí)別泽论。

命令:tesseract --list-langs可以查看當(dāng)前支持的語(yǔ)言,chi_sim表示支持簡(jiǎn)體中文卡乾。

那么在使用時(shí)候翼悴,可以指定某個(gè)語(yǔ)言來(lái)進(jìn)行識(shí)別,如:

tesseract -l chi_sim paixu.png paixu

<v:shape id="圖片_x0020_45" o:spid="_x0000_i1028" type="#_x0000_t75" style="width:415.2pt;height:121.8pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg" o:title=""></v:imagedata></v:shape>

表現(xiàn)在程序里,則可以這么寫(xiě):

|

from PIL import Image

import subprocess

def cleanFile(filePath):

image = Image.open(filePath)

調(diào)用系統(tǒng)的tesseract命令, 對(duì)圖片進(jìn)行OCR中文識(shí)別

subprocess.call(["tesseract", "-l", "chi_sim", filePath, "paixu"])

打開(kāi)文件讀取結(jié)果

with open("paixu.txt", 'r',encoding='utf-8') as f:

print(f.read())

if name == "main":

cleanFile("paixu.png")

|

結(jié)果如下:

<v:shape id="圖片_x0020_49" o:spid="_x0000_i1027" type="#_x0000_t75" style="width:417.6pt;height:309.6pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image009.png" o:title=""></v:imagedata></v:shape>

1.1.9 參考閱讀:訓(xùn)練Tesseract

大多數(shù)其他的驗(yàn)證碼都是比較簡(jiǎn)單的鹦赎。例如谍椅,流行的 PHP 內(nèi)容管理系統(tǒng) Drupal 有一個(gè)著名的驗(yàn)證碼模塊 https://www.drupal.org/project/captcha,可以生成不同難度的驗(yàn)證碼古话。

舉個(gè)例子:

<v:shape id="圖片_x0020_50" o:spid="_x0000_i1026" type="#_x0000_t75" style="width:415.2pt;height:189.6pt;
visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image010.png" o:title=""></v:imagedata></v:shape>

那么與其他驗(yàn)證碼相比雏吭,為什么這個(gè)驗(yàn)證碼更容易被人類和機(jī)器讀懂呢?

· 字母沒(méi)有相互疊加在一起,在水平方向上也沒(méi)有彼此交叉陪踩。也就是說(shuō)杖们,可以在每一個(gè)字 母外面畫(huà)一個(gè)方框,而不會(huì)重疊在一起肩狂。

· 圖片沒(méi)有背景色摘完、線條或其他對(duì) OCR 程序產(chǎn)生干擾的噪點(diǎn)。

· 雖然不能因一個(gè)圖片下定論傻谁,但是這個(gè)驗(yàn)證碼用的字體種類很少孝治,而且用的是 sans-serif 字體(像“4”和“M”)和一種手寫(xiě)形式的字體(像“m”“C”和“3”)。

· 白色背景色與深色字母之間的對(duì)比度很高审磁。

這個(gè)驗(yàn)證碼只做了一點(diǎn)點(diǎn)改變谈飒,就讓 OCR 程序很難識(shí)別。

· 字母和數(shù)據(jù)都使用了态蒂,這會(huì)增加待搜索字符的數(shù)量步绸。

· 字母隨機(jī)的傾斜程度會(huì)迷惑 OCR 軟件,但是人類還是很容易識(shí)別的吃媒。

· 那個(gè)比較陌生的手寫(xiě)字體很有挑戰(zhàn)性,在“C”和“3”里面還有額外的線條吕喘。

· 另外這個(gè)非常小的小寫(xiě)“m”赘那,計(jì)算機(jī)需要進(jìn)行額外的訓(xùn)練才能識(shí)別。

用下面的代碼運(yùn)行 Tesseract 識(shí)別圖片:

tesseract captchaExample.png output

我們得到的結(jié)果 output.txt 是: 4N\``氯质,募舟,,``C<3

1.1.10 創(chuàng)建樣本庫(kù)訓(xùn)練Tesseract

要訓(xùn)練 Tesseract 識(shí)別一種文字闻察,無(wú)論是晦澀難懂的字體還是驗(yàn)證碼拱礁,你都需要向 Tesseract 提供每個(gè)字符不同形式的樣本。

首先要收集大量的驗(yàn)證碼樣本辕漂,樣本的數(shù)量和復(fù)雜程度呢灶,會(huì)決定訓(xùn)練的效果。第二步是準(zhǔn)確地告訴 Tesseract 一張圖片中的每個(gè)字符是什么钉嘹,以及每個(gè)字符的具體位置鸯乃。

這里需要?jiǎng)?chuàng)建一些矩形定位文件(box file),一個(gè)驗(yàn)證碼圖片生成一個(gè)矩形定位文件跋涣,也可以通過(guò)jTessBoxEditor軟件來(lái)修改矩形的定位缨睡。

一個(gè)圖片的矩形定位文件如下所示:

      4  15 26 33 55 0
      M  38 13 67 45 0
      m  79 15 101 26 0
      C  111 33 136 60 0
      3  147 17 176 45 0

第一列符號(hào)是圖片中的每個(gè)字符鸟悴,后面的 4 個(gè)數(shù)字分別是包圍這個(gè)字符的最小矩形的坐標(biāo) (圖片左下角是原點(diǎn) (0,0)奖年,4 個(gè)數(shù)字分別對(duì)應(yīng)每個(gè)字符的左下角 x 坐標(biāo)细诸、左下角 y 坐標(biāo)、右上角 x 坐標(biāo)和右上角 y 坐標(biāo))陋守,最后一個(gè)數(shù)字“0”表示圖片樣本的編號(hào)震贵。

矩形定位文件必須保存在一個(gè) .box 后綴的文本文件中,(例如 4MmC3.box)嗅义。

博客園的一篇不錯(cuò)的訓(xùn)練教程:http://www.cnblogs.com/mjorcen/p/3800739.html?utm_source=tuicool&utm_medium=referral

前面的內(nèi)容只是對(duì) Tesseract 庫(kù)的字體訓(xùn)練和識(shí)別能力的一個(gè)簡(jiǎn)略概述屏歹。如果你對(duì) Tesseract 的其他訓(xùn)練方法感興趣,甚至打算建立自己的驗(yàn)證碼訓(xùn)練文件庫(kù)之碗,推薦閱讀 Tesseract 官方文檔:https://github.com/tesseract-ocr/tesseract/wiki蝙眶,加油!

1.2 Scrapy 框架

Scrapy是用純Python實(shí)現(xiàn)一個(gè)為了爬取網(wǎng)站數(shù)據(jù)褪那、提取結(jié)構(gòu)性數(shù)據(jù)而編寫(xiě)的應(yīng)用框架幽纷,用途非常廣泛。

框架的力量博敬,用戶只需要定制開(kāi)發(fā)幾個(gè)模塊就可以輕松的實(shí)現(xiàn)一個(gè)爬蟲(chóng)友浸,用來(lái)抓取網(wǎng)頁(yè)內(nèi)容以及各種圖片,非常之方便偏窝。

Scrapy 使用了 Twisted'tw?st?d異步網(wǎng)絡(luò)框架來(lái)處理網(wǎng)絡(luò)通訊收恢,可以加快我們的下載速度,不用自己去實(shí)現(xiàn)異步框架祭往,并且包含了各種中間件接口伦意,可以靈活的完成各種需求。

Scrapy架構(gòu)圖(綠線是數(shù)據(jù)流向):

<v:shape id="圖片_x0020_51" o:spid="_x0000_i1025" type="#_x0000_t75" style="width:415.8pt;height:292.8pt;visibility:visible;mso-wrap-style:square"><v:imagedata src="file:///C:/Users/10843/AppData/Local/Temp/msohtmlclip1/01/clip_image011.png" o:title=""></v:imagedata></v:shape>

  1. Scrapy Engine(引擎): 負(fù)責(zé)Spider硼补、ItemPipeline驮肉、Downloader、Scheduler中間的通訊已骇,信號(hào)离钝、數(shù)據(jù)傳遞等。

  2. Scheduler(調(diào)度器): 它負(fù)責(zé)接受引擎發(fā)送過(guò)來(lái)的Request請(qǐng)求褪储,并按照一定的方式進(jìn)行整理排列卵渴,入隊(duì),當(dāng)引擎需要時(shí)鲤竹,交還給引擎奖恰。

  3. Downloader(下載器):負(fù)責(zé)下載Scrapy Engine(引擎)發(fā)送的所有Requests請(qǐng)求,并將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spider來(lái)處理瑟啃,

  4. Spider(爬蟲(chóng)):它負(fù)責(zé)處理所有Responses,從中分析提取數(shù)據(jù)论泛,獲取Item字段需要的數(shù)據(jù),并將需要跟進(jìn)的URL提交給引擎蛹屿,再次進(jìn)入Scheduler(調(diào)度器)屁奏,

  5. Item Pipeline(管道):它負(fù)責(zé)處理Spider中獲取到的Item,并進(jìn)行進(jìn)行后期處理(詳細(xì)分析错负、過(guò)濾坟瓢、存儲(chǔ)等)的地方.

  6. Downloader Middlewares(下載中間件):你可以當(dāng)作是一個(gè)可以自定義擴(kuò)展下載功能的組件。

  7. Spider Middlewares(Spider中間件):你可以理解為是一個(gè)可以自定擴(kuò)展和操作引擎和Spider中間通信的功能組件(比如進(jìn)入Spider的Responses;和從Spider出去的Requests)

1.2.1 配置安裝

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末犹撒,一起剝皮案震驚了整個(gè)濱河市折联,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌识颊,老刑警劉巖诚镰,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異祥款,居然都是意外死亡清笨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)刃跛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抠艾,“玉大人,你說(shuō)我怎么就攤上這事桨昙〖旌牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵蛙酪,是天一觀的道長(zhǎng)谨敛。 經(jīng)常有香客問(wèn)我库说,道長(zhǎng),這世上最難降的妖魔是什么倚搬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任兔簇,我火速辦了婚禮,結(jié)果婚禮上绣檬,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好欲芹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著吟吝,像睡著了一般菱父。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天浙宜,我揣著相機(jī)與錄音官辽,去河邊找鬼。 笑死粟瞬,一個(gè)胖子當(dāng)著我的面吹牛同仆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裙品,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼俗批,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了市怎?” 一聲冷哼從身側(cè)響起岁忘,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎区匠,沒(méi)想到半個(gè)月后干像,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辱志,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蝠筑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揩懒。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡什乙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出已球,到底是詐尸還是另有隱情臣镣,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布智亮,位于F島的核電站忆某,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏阔蛉。R本人自食惡果不足惜弃舒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望状原。 院中可真熱鬧聋呢,春花似錦、人聲如沸颠区。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毕莱。三九已至器贩,卻和暖如春颅夺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛹稍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工吧黄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稳摄。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓稚字,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親厦酬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胆描,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容