Python精確指南——第三章 Selenium和爬蟲(Python第三章)
3?????? Selenium
3.1???? 介紹
網絡爬蟲在互聯網領域有著廣泛的應用。
Selenium是一個頁面自動化控制框架。能夠模擬實際操作,自動化獲取網站提供的頁面資源信息。
Selenium能夠自定義頁面操作的行為,按照用戶指定的跳轉路徑訪問,具有實現跟實際用戶一樣填充信息、提交表單請求的能力,適用于專門網站特定信息的獲取。比如:特定圖片網站圖片的獲取,購物網站商品信息的獲取等等。
3.2???? 下載與安裝
Selenium最新的版本是3.8.0,目前支持Python 2.7和3.4+版本。
在線安裝:pip install -U selenium
離線安裝:在PyPI網站上下載對應安裝包,參考1.4 章節Python安裝包 進行離線安裝。
3.3???? 關鍵技術要點
Selenium框架的開發步驟這里不做詳細介紹,可以參考如下鏈接進行開發前學習。
開發文檔及樣例:
http://seleniumhq.github.io/selenium/docs/api/py/
Selenimu開發包API:
https://seleniumhq.github.io/selenium/docs/api/py/api.html
下面就開發中遇到的幾個關鍵技術要點進行詳解。
3.3.1? 瀏覽器的選擇
Selenium針對不同的瀏覽器有對應的驅動引擎,在64位系統上,一般IE是64bit。如果用32bit的IEDriverServer.exe,在第二頁就會看到web browser not get,然后運行出錯。但是如果用64bit的IEDriverServer.exe,在填表格的時候就會特別慢,原因不明。
使用不同的瀏覽器,需要使用到不同瀏覽器的驅動Driver,下面是各個瀏覽器Selenium Client Drivers的下載頁面:
http://seleniumhq.org/download/
?? Selenium的WebDriver打開IE失敗的解決辦法
在運行IE瀏覽器時,會報下面的錯誤:
WebDriverException: Message: u'Unexpected error launching Internet Explorer. Protected Mode must be set to the same value (enabled or disabled) for all zones.'
兩種方法:
1)修改IE的安全策略,就像Exception里面提示的那樣。
2)在生成webdriver對象之前先執行這些代碼:
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
DesiredCapabilities.INTERNETEXPLORER['ignoreProtectedModeSettings'] = True
firefox速度會比較快,首選firefox。
3.3.2? XPath
XPath是XML路徑語言,用來查找定位xml樹狀結構中的節點,同樣適用html。
參考學習手冊:
http://www.w3school.com.cn/xpath/index.asp
判斷定義好的xpath規則是否找到定位節點:
uname = self.master.html_elem.xpath("http://tr/td/form[@name = 'frmResult']")
if len(uname) == 0:
return False
3.3.3? StringIO
StringIO模塊可以將內存中的xml結構字符串保存成離線html文件,實現在線轉離線的功能:
import StringIO
blank _xml = StringIO.StringIO(
root_test = etree.parse(blank_xml)
root_test.write("test.xml")
3.3.4? lxml
第三方安裝包lxml比Python自帶的xml模塊具有更為強大的解析xml或html的功能,推薦使用。最新的版本是4.1.1。
下載頁面:
https://pypi.python.org/pypi/lxml/4.1.1
找到對應的版本,使用pip安裝即可。
像3.3.3 StringIO章節中介紹的保存的離線xml或者html,lxml就可以像解析在線request請求到的網頁內容一樣進行解析:
url:
xxx_sample? = urllib2.urlopen(url).read()
Doc = html.fromstring(xxx_sample)
文件:
xxx_sample = HTML.parse('xxx_sample.htm')
Doc = xxx_sample.getroot()
3.3.5? 解析器的選擇
lxml有默認的解析器,但是對于非規則的網頁處理非常不好。相當多的網站不是規則的html語法,所以需要選擇外部的解析器。
在lxml里調用beautifulsoap。這個處理中文比較好,首選解析器。
3.3.6? 中文字符的處理
?? 基本支持
在源文件的第一行,在任何代碼和注釋前面,加入如下語句:
#encoding=utf-8
一般需要在寫任何中文和代碼之前加入這句話。如果先在代碼里出現了中文,然后貼這句話,或導致亂碼。
?? 保存中文網頁
保存網頁數據。通常的網頁數據是unicode,在eclispe中運行的時候可以正確的保存,但是單獨運行就不行了。錯誤如下:
使用encode("gb2312", "replace")就可以解決問題。
代碼如下:
file_object.write(page_text.encode("gb2312", "replace"))
?? Unicode字符串
所有涉及到中文的字符串前加上u,實際上,可以在使用#encoding=utf-8之后,任何字串,包括英文字前面加上u也都是沒有問題,例子:
text1 = u”中文字符”
text2 = u”mystring”
?? 中文字符的編碼轉換
在線request網頁:
在讀取文本或者讀取網頁內容的時候,有時候無法判斷字符,需要使用import chardet來自動判斷字符的編碼格式,然后裝換成unicode,例子:
content = urllib2.urlopen(url_to_fetch).read()
xxx_encode = chardet.detect(content)
unicode_content = unicode(content, xxx_encode["encoding"], "replace")
離線html網頁:
解析中文離線網頁的方法:
parser = etree.HTMLParser(encoding = 'gb2312')
xxx_sample = HTML.parse('xxx.htm', Parser)
xxx_root = xxx_sample.getroot()
ct = xxx_root.xpath('//title')
try_name = ct[0].text
這樣就可以得到中文title。
?? 中文字符的匹配
需要使用Python內置正則表達式re模塊,例子:
中文正則
cn_str = u"計算機科學與技術"
m = re.search(u"([u4e00-u9fa5]+)科學")
t=m.group(1)
這樣就可以得到“計算機”。
3.1.1? 開發要點
獲得頁面源碼
page_text = chr_browser.page_source
在頁面執行自定義script腳本
平時用到的比較多的全選,復制等。
chr_browser.execute_script('document.execcommand("selectall"))
查找document.execcommand可以得到各種命令。
打開多個Selenium控制的實例
Selenium控制的實例需要指定一個端口,默認使用的是一個,所以無法啟動多個selenium的實例。如果指定了不同的端口,就能在一臺PC上控制多個瀏覽器實例。
3.1.2? 驗證碼的識別
在使用Selenium訪問網站時,用戶名和密碼的表單提交相對簡單,最難的是各個網站登錄時的驗證碼識別。
Pytesseract:python封裝的tesseract
Tesseract是圖片識別的一個開源項目,托管網址:
https://github.com/tesseract-ocr/tesseract
PyPI下載路徑和基本使用介紹:
https://pypi.python.org/pypi/pytesseract
Pytesseract的使用還依賴于PIL庫,PIL只能使用exe安裝,否則很麻煩,而且只支持32位的python,下載網址:
http://www.pythonware.com/products/pil/
Pytesseract其實并沒有做什么事情,把命令打印出來,就是在線程里執行如下的結果:
['tesseract', 'C:\Users\L00163~1\AppData\Local\Temp\tess_2.bmp', 'C:\Users\L00163~1\AppData\Local\Temp\tess_3', '-l', 'eng']
很明顯,Pytesseract把輸入的流變成bmp文件,然后讓tesseract.exe去識別,然后從文本文件里獲得數據。
['tesseract', 'C:\Users\L00163~1\AppData\Local\Temp\tess_2.bmp', 'C:\Users\L00163~1\AppData\Local\Temp\tess_3', '-l', 'eng', 'batch.nochop', 'makebox']
D:\python\sdk\tesseract\tesseract-ocr-3.01-win32-portable\Tesseract-ocr\Tesseract.exe zhilian_code1.bmp ret.txt -l eng -psm 7
注意把tesseract的目錄加入到路徑里,必須重啟才能讓PATH生效。
追加:加到全局path里不行,會崩潰,把執行命令找出來,在dos下執行,就會看到崩潰的真正原因。是因為找不到“./tessdata/eng.traineddata”. 實際上是有這個文件的說明還是路徑問題。干脆修改tesseract_cmd。Pytesseract本身是非常簡單的,需要修改兩處。注意只能修改Pytesseract安裝包源碼,而不是修改安裝之后的文件,是改不了的。
在pytesseract.py里增加一個函數接口。
def set_tesseract_cmd(tesseract_file_path):
tesseract_cmd = tesseract_file_path
另外在pytesseract的__init__.py里增加一句。
from pytesseract import set_tesseract_cmd
改完之后,重新執行setup.py build和setup.py install就可以了。
不過為了簡單起見,直接把pytesseract.py復制到工程里就可以了,修改最方便。因為工程里import會優先找本地的文件,雖然同是也安裝了pytesseract,但是使用的是本地的pytesseract.py。
在函數里修改文件的全局變量,需要加上global。如下面的修改
tesseract_cmd = 'tesseract'
def set_tesseract_cmd(tesseract_file_path):
tesseract_cmd = tesseract_file_path
是沒有效果的,set_tesseract_cmd的tesseract_cmd是另外一個局部變量。
?? 去底噪預處理
適合于底噪不是很強烈,并且沒有單點深色噪音的識別碼。
例如下圖,來自于火車票訂票網站。
import Image,ImageFilter
# load a color image
im = Image.open('passCodeAction.do6.jpg')
big_img = im.resize((120, 40))
bim = big_img.filter(ImageFilter.SMOOTH)
Lim = big_img.convert('L')
Lim.save('fun_Level.jpg')
Lim.show()
# setup a converting table with constant threshold
threshold = 160
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
# convert to binary image by the table
bim = Lim.point(table, '1')
bim.show()
bim.save('fun_binary.tif')
?? 去單點強底噪的方法
原始圖片,雖然底噪簡單,但是有不少單點的深色底噪。
處理方法:
im = Image.open(image_name)
im = im.filter(ImageFilter.MedianFilter())
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(2)
im = im.convert('1')
im.show()
經過以上處理,按道理及按照網絡上的資料,簡單英文和數字的識別率是很高的,實際上在用驗證碼的時候,識別率很低。而絕大部分圖形實際上是非常清楚的,比tesseract自帶樣本還要清楚。最后覺得原因可能是因為圖片太小,只有80×80的原因。
嘗試用windows自帶的看圖軟件將識別碼圖片放大,然后截圖,另存為一個文件,結果一下子就認準了,說明就是圖片大小的問題,不是清晰度的原因。
?? 圖片放大方法
def dzoom_img(pic, ratio):
w = pic.size[0] * ratio
h = pic.size[1] * ratio
big_pic = pic.resize((w,h), Image.BILINEAR)
big_pic.show()
big_pic.save('auto_big.bmp')
return big_pic
?? 圖片CROP
手工放大后用hypersnap截圖如下,可以正常識別。
但是用軟件自動放大之后,什么都識別不出來,連亂碼都沒有了,對比下兩個圖,沒什么區別,只是旁邊多了一些黑點,猜想是不是因為這些黑點的原因導致認不出來。用工具將黑點全部去掉,結果能正常識別。所以還需要用軟件把旁邊的黑點crop掉。
CROP的方法
crop_reg = (20, 20, w-20, h-20)
big_pic = big_pic.crop(crop_reg)
?? 獲取驗證碼圖片
參考多篇文章,最可靠的是截屏
查看元素信息如下:
截屏處理:
用圖片軟件看截屏的圖和位置信息,可以看出,尺寸和size都是能對應上的,所以可以用crop函數精確的截取。不過同時要注意,圖片周邊的小點會產生明顯的影響,所以直接在crop階段去掉就可以了。在(515,165)處就能除掉邊界。所以截取的區域為
(x-2, y-2,x+w-4, y+h-4)
?? 白名單研究:只處理數字和字母
發現驗證碼的識別率很低,只有全數字的識別率高,嘗試只識別數字和字母
D:\python\sdk\tesseract\tesseract-ocr-3.01-win32-portable\Tesseract-ocr\Tesseract.exe big_test4.png ret.txt -psm 7 char_digits
新建文件:
D:\python\sdk\tesseract\tesseract-ocr-3.01-win32-portable\Tesseract-ocr\tessdata\configs\char_digits
內容如下,限制只能識別這些字符
tessedit_char_whitelist 0123456789ABCDEFGHIJKLMNOPQRSTYVWXKZ
3.1.1? 打包部署問題
Selenium工程開發完成后,在需要在不同機器上進行部署搭建,不能依賴開發環境,在打包完成后遇到有下面的問題,最終也是通過修改源碼的方式完美解決。
Python打包的部分在后面的章節會詳細介紹,這里只討論IE瀏覽器和firefox瀏覽器的selenium工程打包部署過程中遇到問題的解決。以下打包過程使用PyInstaller工具。
?? Firefox瀏覽器
webdriver里使用firefox打包報錯如下,找不到webdriver.xpi和webdriver_prefs.json兩個文件:
解決方法:
用的2.48 selenium,修改兩處firefox_profile.py源碼文件。
然后把這兩個文件復制到單文件的exe同路徑下就可以用了。
2.48 selenium代碼修改, 都是改了路徑為當前路徑。
修改為:
'''with open(os.path.join(os.path.dirname(__file__),
WEBDRIVER_PREFERENCES)) as default_prefs:
FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)'''
if os.path.exists(WEBDRIVER_PREFERENCES):
with open(WEBDRIVER_PREFERENCES) as default_prefs:
FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)
else:
with open(os.path.join(os.path.dirname(__file__),
WEBDRIVER_PREFERENCES)) as default_prefs:
FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)
修改為:
'''if addon == WEBDRIVER_EXT:
= os.path.join(os.path.dirname(__file__), WEBDRIVER_EXT)'''
if addon == WEBDRIVER_EXT:
if os.path.exists(WEBDRIVER_PREFERENCES):
addon = os.path.join(WEBDRIVER_EXT)
else:
addon = os.path.join(os.path.dirname(__file__), WEBDRIVER_EXT)
?? IE瀏覽器
webdriver里使用ie打包的結果
這些是包含的文件
打包報錯如下:
無論如何找不到dll,pyinstaller的各種方法都已經試過了,就是不行。
最后根據運行的提示信息,找到browser_man_lib.py的44行,點擊進去找到webdriver.py,發現是這么一行代碼:
try:
self.iedriver = CDLL(os.path.join(os.path.dirname(__file__),"win32", "IEDriver.dll"))
except WindowsError:
try:
self.iedriver = CDLL(os.path.join(os.path.dirname(__file__),"x64", "IEDriver.dll"))
except WindowsError:
raise WebDriverException("Unable to load the IEDriver.dll component")
肯定是CDLL(os.path.join(os.path.dirname(__file__),"x64", "IEDriver.dll"))執行不成功,猜測CDLL 只是要load一個dll而已,并不是要求確定的路由,因為輸入參數顯然就是全路徑的dll。所以先把這個路徑打印出來。代碼如下:
dll_test_path = os.path.join(os.path.dirname(__file__),"win32", "IEDriver.dll")
print dll_test_path
try:
self.iedriver = CDLL(dll_test_path)
except WindowsError:
try:
self.iedriver = CDLL(os.path.join(os.path.dirname(__file__),"x64", "IEDriver.dll"))
except WindowsError:
raise WebDriverException("Unable to load the IEDriver.dll component")
最后在執行的時候發現打印如下:
E:\project\ryan_soft\py_depot\xxx_reaper_browser\src\build\pyi.win32\xxx_reape
r\outPYZ1.pyz\win32\IEDriver.dll
Traceback (most recent call last):
File "
File "
File "
File "E:\project\ryan_soft\py_depot\xxx_reaper_browser\src\build\pyi.win32\xxx_reaper\outPYZ1.pyz/browser_man_lib", line 44, in __init__
File "E:\project\ryan_soft\py_depot\rsm_reaper_browser\src\build\pyi.win32\xxx_reaper\outPYZ1.pyz/selenium.webdriver.ie.webdriver", line 61, in __init__
selenium.common.exceptions.WebDriverException: Message: 'Unable to load the E:\\
project\\ryan_soft\\py_depot\\xxx_reaper_browser\\src\\build\\pyi.win32\\xxx_r
eaper\\outPYZ1.pyz\\win32\\IEDriver.dll'
說明執行程序試圖找到outPYZ1.pyz\\win32\\IEDriver.dll,這顯然是不可能的。所以需要修改一下路徑,先嘗試用絕對路徑,代碼修改如下,并且將
D:\Python27\Lib\site-packages\selenium-2.20.0-py2.7.egg\selenium\webdriver\ie\win32\ IEDriver.dll
復制到d:\\IEDriver.dll。重新編譯打包。
dll_tmp_path = os.path.join("d:\\IEDriver.dll")
try:
self.iedriver = CDLL(dll_tmp_path)
except WindowsError:
try:
self.iedriver = CDLL(os.path.join(os.path.dirname(__file__),"x64", "IEDriver.dll"))
except WindowsError:
raise WebDriverException("Unable to load the IEDriver.dll component")
這下問題解決了。當時使用相對路徑肯定是不行的,所以采用相對路徑測試。
將代碼改為
dll_tmp_path = os.path.join("IEDriver.dll")
try:
self.iedriver = CDLL(dll_tmp_path)
except WindowsError:
try:
self.iedriver = CDLL(os.path.join(os.path.dirname(__file__),"x64", "IEDriver.dll"))
except WindowsError:
raise WebDriverException("Unable to load the IEDriver.dll component")
然后將IEDriver.dll復制到調用打包后exe的路徑,而不是exe的路徑。區別在于,如果是命令行在其他目錄調用exe,那么IEDriver.dll需要復制到那個路徑。如果windows下雙擊,當然IEDriver.dll就是需要和exe同一個目錄下。最干脆的解決方法是放到一個系統path能找到的地方,不管如何執行exe都沒有問題。
3.1???? 注意事項
使用Selenium應用獲取網站信息的時候,最好與網站官方有合作關系,否則在訪問頻率上需要格外注意。如果網站沒有健全的后臺系統,無節制的快速訪問,有可能致使網站崩潰,或者IP地址及賬戶被官方記錄,列入訪問黑名單。
3.2???? 其他爬蟲框架
下面簡單介紹幾種其他Python中常用的爬蟲框架。
3.2.1? 內置模塊
Python內置的urllib和urllib2可以實現簡單的request請求,獲取服務器的反饋數據。
Post網頁:
import urllib
import urllib2
postdata=urllib.urlencode({
'username':'psstby',
'password':'by201109'
})
req = urllib2.Request(
url = 'http://hwrd.zhaopin.com/loginmgr/loginproc.asp',
data = postdata
)
urllib2.urlopen(req).read()
3.2.2? Scrapy
Scrapy是一個為了爬取網站數據,提取結構性數據而編寫的應用框架。 可以應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
Scrapy 使用 Twisted這個異步網絡庫來處理網絡通訊,架構清晰,并且包含了各種中間件接口,可以靈活的完成各種需求。
官網地址:
https://scrapy.org/
3.2.3? PySpider
PySpider 是一個非常方便并且功能強大的爬蟲框架,支持多線程爬取、JS動態解析,提供了可操作界面、出錯重試、定時爬取等等的功能,使用非常人性化。
官網地址:
http://www.pyspider.cn/
開源托管地址:
https://github.com/binux/pyspider/
附件: Python精確指南-第三章selenium和爬蟲.pdf 1.11M 下載次數:11次
編程語言 python Selenium
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。