1、簡介php
接着幾個月以前的(數據科學學習手札31)基於Python的網絡數據採集(初級篇),在那篇文章中,咱們介紹了關於網絡爬蟲的基礎知識(基本的請求庫,基本的解析庫,CSS,正則表達式等),在那篇文章中咱們只介紹瞭如何利用urllib、requests這樣的請求庫來將咱們的程序模擬成一個請求網絡服務的一端,來直接取得設置好的url地址中樸素的網頁內容,再利用BeautifulSoup或pyspider這樣的解析庫來對獲取的網頁內容進行解析,在初級篇中咱們也只瞭解到如何爬取靜態網頁,那是網絡爬蟲中最簡單的部分,事實上,如今但凡是有價值的網站都或多或少存在着本身的一套反爬機制,例如利用JS腳原本控制網頁中部份內容的請求和顯示,使得最原始的直接修改靜態目標頁面url地址來更改頁面的方式失效,這一部分,我在(數據科學學習手札47)基於Python的網絡數據採集實戰(2)中爬取馬蜂窩景點頁面下蜂蜂點評區域用戶評論內容的時候,也詳細介紹過,但以前我在全部爬蟲相關的文章中介紹的內容,都離不開這樣的一個過程:node
整理url規則(靜態頁面直接訪問,JS控制的動態頁面經過瀏覽器的開發者工具來找到真實網址和參數)python
|web
假裝瀏覽器正則表達式
|chrome
利用urllib.urlopen()或requests.get()對目標url發起訪問數據庫
|npm
得到返回的網頁原始內容瀏覽器
|網絡
利用BeautifulSoup或PySpider對網頁原始內容進行解析
|
結合觀察到的CSS標籤屬性等信息,利用BeautifulSoup對象的findAll()方法提取須要的內容,利用正則表達式來完成精確提取
|
存入數據庫
而本文將要介紹的一種新的網絡數據採集工具就再也不是假裝成瀏覽器端,而是基於自動化測試工具selenium來結合對應瀏覽器的驅動程序,開闢真實的、顯性的瀏覽器窗口,來完成一系列動做,以應對更加動態靈活的網頁;
2、selenium
2.1 介紹
selenium也是一個用於Web應用程序測試的工具。selenium測試直接運行在瀏覽器中,就像真正的用戶在操做同樣。支持的瀏覽器包括IE、Mozilla Firefox、Mozilla Suite、Chrome等。這個工具的主要功能是測試與瀏覽器的兼容性,但因爲其可以真實模擬瀏覽器,模擬網頁點擊、下拉、拖拽元素等行爲的功能,使得其在網絡數據採集上開闢出一片天地;
2.2 環境搭建
要想基於Python(這裏咱們說的是Python3,Python2,就讓它在歷史的長河裏隱退吧。。。)來使用selenium建立爬蟲程序,咱們須要:
1.安裝selenium包,直接pip安裝便可
2.下載瀏覽器(廢話-_-!),以及對應的驅動程序,本文選擇使用的瀏覽器爲Chrome,須要下載chromedriver.exe,這裏提供一個收錄全部版本chromedriver.exe資源的地址:
http://npm.taobao.org/mirrors/chromedriver/
須要注意的是,要下載與你的瀏覽器版本兼容的資源,這裏給一個建議:將你的Chrome瀏覽器更新到最新版本,再到上述地址中下載發佈時間最新的chromedriver.exe;在下載完畢後,將chromedriver.exe放到你的Python根目錄下,和python.exe放在一塊兒,譬如我就將其放在個人anaconda環境下的對應位置:
3.測試一下~
在完成上述操做以後,咱們要檢驗一下咱們的環境有沒有正確搭建完成,在你的Python編輯器中,寫下以下語句:
from selenium import webdriver '''建立一個新的Chrome瀏覽器窗體''' browser = webdriver.Chrome() '''在browser對應的瀏覽器中訪問百度首頁''' browser.get('http://www.baidu.com')
若是在執行上述語句以後,順利地打開了Chrome瀏覽器並訪問到咱們設置的網頁,則selenium+Chrome的開發環境配置完成;
2.3 利用selenium進行網絡數據採集的基本流程
在本文的一開始咱們總結了以前進行網絡數據採集的基本流程,下面咱們以相似的形式介紹一下selenium進行網絡數據採集的基本流程:
建立瀏覽器(可能涉及對瀏覽器一些設置的預配置,如不須要採集圖片時設置禁止加載圖片以提高訪問速度)
|
利用.get()方法直接打開指定url地址
|
利用.page_source()方法獲取當前主窗口(瀏覽器中可能同時打開多個網頁窗口,這時須要利用頁面句柄來指定咱們關注的主窗口網頁)頁面對應的網頁內容
|
利用BeautifulSoup或pyspider等解析庫對指定的網頁內容進行解析
|
結合觀察到的CSS標籤屬性等信息,利用BeautifulSoup對象的findAll()方法提取須要的內容,利用正則表達式來完成精確提取
|
存入數據庫
能夠看出,利用selenium來進行網絡數據採集與以前的方法最大的不一樣點在於對目標網頁發起請求的過程,在使用selenium時,咱們無需再假裝瀏覽器,且有了很是豐富的瀏覽器動做能夠設置,譬如說以前咱們須要對頁面進行翻頁操做,主要是經過修改url中對應控制頁面值的參數來完成,因此在遇到JS控制的動態網頁時,能夠不須要去費心尋找控制對應資源翻頁的真實url地址,只須要在selenium中,經過其內置的豐富的定位方法對頁面中的翻頁按鈕進行定位 ,再經過對定位到的元素運用.click(),便可實現真實的翻頁操做,下面咱們根據上述過程當中列出的selenium部分,涉及到的經常使用方法進行介紹以及舉例說明:
3、selenium經常使用操做
3.1 瀏覽器配置部分
在調出一個真實的瀏覽器對象以前,咱們能夠結合實際須要對瀏覽器的設置進行參數配置,這在selenium中是經過對應瀏覽器的XXXOptions類來設置的,例如本文只介紹Chrome瀏覽器,則咱們經過ChromeOptions類中的方法來實現瀏覽器預配置,下面咱們來了解一下ChromeOptions類:
ChromeOptions:
ChromeOptions是一個在selenium建立Chrome瀏覽器以前,對該瀏覽器對象進行預配置的類,其主要功能有添加Chrome啓動參數、修改Chrome設置、添加擴展應用等,如:
1.禁止網頁中圖片加載
from selenium import webdriver '''建立一個新的Chrome瀏覽器窗體,經過add_experimental_option()方法來設置禁止圖片加載''' chrome_options = webdriver.ChromeOptions() prefs = {"profile.managed_default_content_settings.images": 2} chrome_options.add_experimental_option("prefs", prefs) browser = webdriver.Chrome(chrome_options=chrome_options) '''在browser對應的瀏覽器中,以禁止圖片加載的方式訪問百度首頁''' browser.get('http://www.baidu.com') '''查看當前瀏覽器中已設置的參數''' chrome_options.experimental_options
能夠看出,在進行如上設置後,咱們訪問的網頁中全部圖片都沒有加載,這在不須要採集圖片資源的任務中,對於提高訪問速度有着重要意義;
2.設置代理IP
有些時候,在面對一些對訪問頻率有所限制的網站時,一旦咱們爬取頻率太高,就會致使咱們本機的IP地址遭受短暫的封禁,這時咱們能夠經過收集一些IP代理來創建咱們的代理池,關於這一點咱們會在以後單獨開一篇博客來詳細介紹,下面簡單演示一下如何爲咱們的Chrome()瀏覽器對象設置IP代理:
from selenium import webdriver '''設置代理IP''' IP = '106.75.9.39:8080' '''爲Chrome瀏覽器配置chrome_options選項''' chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--proxy-server=http://{}'.format(IP)) '''將配置好的chrome_options選項傳入新的Chrome瀏覽器對象中''' browser = webdriver.Chrome(chrome_options=chrome_options) '''嘗試訪問百度首頁''' browser.get('http://www.baidu.com')
可是若是你不是付費購買的高速IP代理,而是從網上所謂的免費IP代理網站扒下來的一些IP地址,那麼上述設置以後打開的瀏覽器中不必定能在正常時間內顯示目標網頁(緣由你懂的);
另外一種思路:
除了使用ChromeOptions()中的方法來設置,還有一種簡單直接粗暴的方法,咱們能夠直接訪問對應當前瀏覽器設置頁面的地址:chrome://settings/content:
from selenium import webdriver browser = webdriver.Chrome() '''直接訪問設置頁面''' browser.get('chrome://settings/content')
接着再使用本身編寫的模擬點擊規則,便可完成對應的設置內容,這裏便再也不多說;
3.2 瀏覽器運行時的實用方法
通過了3.1中介紹的方式,對瀏覽器進行預配置,併成功打開對應的瀏覽器以後,selenium中還存在着很是豐富的瀏覽器方法,下面咱們就其中實用且經常使用的一些方法和類內的變量進行介紹:
假設咱們構造了一個叫作browser的瀏覽器對象,可使用的方法以下:
browser.get(url):在瀏覽器主窗口中打開url指定的網頁;
browser.title:得到當前瀏覽器中主頁面的網頁標題:
from selenium import webdriver browser = webdriver.Chrome() '''直接訪問設置頁面''' browser.get('https://hao.360.cn/?wd_xp1') '''打印網頁標題''' print(browser.title)
browser.current_url:返回當前主頁面url地址
browser.page_source:獲取當前主界面的頁面內容,至關於requests.get(url).content
browser.close():關閉當前主頁面對應的網頁
browser.quit():直接關閉當前瀏覽器
browser.maximize_window():將瀏覽器窗口大小最大化
browser.fullscreen_window():將瀏覽器窗口全屏化
browser.back():控制當前主頁面進行後退操做(前提是它有上一頁面)
browser.forward():控制當前主頁面進行前進操做(前提是它有下一頁面)
browser.refresh():控制當前主頁面進行刷新操做
browser.set_page_load_timeout(time_to_wait):爲當前瀏覽器設置一個最大頁面加載耗時容忍閾值,單位秒,相似urllib.urlopen()中的timeout參數,即當加載某個界面時,持續time_to_wait秒還未加載完成時,程序會報錯,咱們能夠利用錯誤處理機制捕捉這個錯誤,此方法適用於長時間採樣中某個界面訪問超時假死的狀況
browser.set_window_size(width, height, windowHandle='current'):用於調節瀏覽器界面長寬大小
關於主頁面:
這裏要額外介紹一下,咱們在前面一大段中提到過不少次主頁面這個概念,是由於在selenium控制瀏覽器時,不管瀏覽器中開了多少個網頁,都只將惟一一個網頁視爲主頁面,相應的不少webdriver()方法也都是以該主頁面爲對象,下面是一個示例,咱們以馬蜂窩地方遊記頁面爲例:
from selenium import webdriver browser = webdriver.Chrome() '''訪問馬蜂窩重慶遊記彙總頁''' browser.get('http://www.mafengwo.cn/search/s.php?q=%E9%87%8D%E5%BA%86&t=info&seid=71F18E8D-AA90-4870-9928-2BE01E53DDBD&mxid=&mid=&mname=&kt=1')
打開目標頁面以下:
這裏咱們手動點開一篇遊記(模擬點擊的方法下文會介紹),瀏覽器隨即跳轉到一個新的頁面:
這時咱們運行下列代碼:
'''打印網頁標題''' print(browser.title)
能夠看到,雖然在咱們的視角里,經過點擊,進入到一個新的界面,但當咱們利用對應方法獲取當前頁面標題時,仍然是以以前的頁面做爲對象,這就涉及到咱們以前提到的主頁面的問題,當在原始頁面中,由於點擊事件而跳轉到另外一個頁面(這裏指的是新開一個窗口顯示新界面,而不是在原來的窗口覆蓋掉原頁面),瀏覽器中的主頁面依舊是鎖定在原始頁面中,即get()方法跳轉到的網頁,這種狀況咱們就須要用到網頁的句柄來惟一標識每個網頁;
在selenium中,關於獲取網頁句柄,有如下兩個方法:
browser.current_window_handle:獲取主頁面的句柄,以上面馬蜂窩的爲例:
'''打印主頁面句柄''' print(browser.current_window_handle)
browser.window_handles:獲取當前瀏覽器中全部頁面的句柄,按照打開的時間順序:
'''打印當前瀏覽器下全部頁面的句柄''' print(browser.window_handles)
既然句柄至關於網頁的身份證,那麼咱們能夠基於句柄切換當前的主網頁到其餘網頁之上,延續上面的例子,此時的主網頁是.get()方法打開的網頁,以前打印browser.title也是指向的該網頁,如今咱們利用browser.switch_to.window(handle)方法,將主網頁轉到最近打開的網頁中,並打印當前主網頁的標題:
'''切換主網頁至最近打開的網頁''' browser.switch_to.window(browser.window_handles[-1]) '''打印當前主網頁的網頁標題''' print(browser.title)
能夠看到,使用主網頁切換方法後,咱們的主網頁轉到指定的網頁中,這在對特殊的網頁跳轉方式下新開的網頁內容的採集很受用;
3.3 頁面元素定位
在介紹selenium的精髓——模擬瀏覽器行爲以前,咱們須要知道如何對網頁內的元素進行定位,譬如說咱們要想定位到網頁中的翻頁按鈕,就須要對翻頁按鈕所在的位置進行定位,這裏的定位不是指在屏幕的平面座標上進行定位,而是基於網頁自身的CSS結構,其實selenium中對網頁元素進行定位的方式很是多,可是經過我大量的實踐,其中不少方法效果並不盡如人意,惟有其中基於xpath的定位方法十分方便,定位很是準確方便,所以本文不會浪費你的時間介紹其餘效果不太好的方法,直接介紹基於xpath的定位方法,咱們先了解一下什麼是xpath:
關於xpath:
xpath是一門在xml文檔中查找信息的語言,只是爲了在selenium中定位網頁元素的話,咱們只須要掌握xpath路徑表達式便可;
xpath使用路徑表達式來識別xml文檔中的節點或節點集,咱們先從一個示例出發來對xpath路徑表達式有一個認識:
仍是以馬蜂窩遊記頁面爲例:
from selenium import webdriver browser = webdriver.Chrome() '''訪問馬蜂窩重慶遊記彙總頁''' browser.get('http://www.mafengwo.cn/search/s.php?q=%E9%87%8D%E5%BA%86&t=info&seid=71F18E8D-AA90-4870-9928-2BE01E53DDBD&mxid=&mid=&mname=&kt=1')
經過瀏覽器的開發者工具,咱們找到「下一頁」按鈕元素在CSS結構中所在的位置:
先把該元素完整的xpath路徑表達式寫出來:
//div/div/a[@class='ti next _j_pageitem']
接着咱們使用基於xpath的定位方法,定位按鈕的位置並模擬點擊:
'''定位翻頁按鈕的位置並保存在新變量中''' ChagePageElement = browser.find_element_by_xpath("//div/div/a[@class='ti next _j_pageitem']") '''對按鈕位置變量使用click方法進行模擬點擊''' ChagePageElement.click()
上述代碼運行以後,咱們的瀏覽器執行了對翻頁按鈕的模擬點擊,實現了翻頁:
如今咱們來介紹一下xpath路徑表達式中的一些基本知識:
nodename:標明一個結點的標籤名稱
/:父節點與子節點之間的分隔符
//:表明父節點與下屬某個節點之間若干個中間節點
[]:指定最末端結點的屬性
@:在[]中指定屬性名稱和對應的屬性值
在xpath路徑表達式中還有不少其餘內容,但在selenium中進行基本的元素定位瞭解到上面這些規則就能夠了,因此咱們上面的例子中的規則,表示的就是定位
若干節點-<div>
... ...
<div>
... ...
<a class='ti next _j_pageitem'></a>
... ...
<div>
... ...
</div>
利用這樣的方式,基於browser.find_element_by_xpath()和browser.find_elements_by_xpath(),咱們就能夠找到頁面中單個獨特元素或多個同類型元素,再使用.click()方法便可完成對頁面內任意元素的模擬點擊;
3.4 基礎的瀏覽器動做模擬
除了上面一小節介紹的使用元素.click()控制點擊動做之外,selenium還支持豐富多樣的其餘常見動做,由於本文是我介紹selenium的上篇,下面只介紹兩個經常使用的動做,更復雜的組合動做放在以後的文章中介紹:
模擬網頁下滑:
不少時候咱們會遇到這樣的動態加載的網頁,如光點壁紙的各個壁紙板塊,這裏以風景板塊爲例http://pic.adesk.com/cate/landscape:
這個網頁的特色是,大多數狀況下沒有翻頁按鈕,而是須要用戶將頁面滑到底部以後,纔會自動加載下一頁的內容,而且這種機制持續固定幾回後,會參雜一個必須點擊才能夠進行翻頁操做的按鈕,咱們能夠在selenium中使用browser.execute_script()方法來傳入JavaScript腳原本執行瀏覽器動做,進而實現下滑功能;
對應下滑到底的JavaScript腳本爲'window.scrollTo(0, document.body.scrollHeight)',咱們用下面這段代碼來實現持續下滑,並及時捕捉翻頁按鈕進行點擊(利用錯誤處理機制來實現):
from selenium import webdriver import time browser = webdriver.Chrome() '''訪問光點壁紙風景板塊頁面''' browser.get('http://pic.adesk.com/cate/landscape') '''這裏嘗試的時候不要循環太屢次,快速加載圖片比較吃網速和內存''' for i in range(1, 20): '''這裏使用一個錯誤處理機制, 若是有定位到加載下一頁按鈕就進行 點擊下一頁動做,不然繼續每隔1秒,下滑到底''' try: '''定位加載下一頁按鈕''' LoadMoreElement = browser.find_element_by_xpath("//div/div[@class='loadmore']") LoadMoreElement.click() except Exception as e: '''瀏覽器執行下滑動做''' browser.execute_script('window.scrollTo(0, document.body.scrollHeight)') time.sleep(1)
模擬輸入:
有些時候,咱們須要對界面中出現的輸入框,即標籤爲<input></input>表明的對象進行模擬輸入操做,這時候咱們只須要對輸入框對應的網頁對象進行定位,而後使用browser.send_keys(輸入內容)來往輸入框中添加文本信息便可,下面是一個簡單的例子,咱們從百度首頁出發,模擬了點擊登錄-點擊註冊-在用戶名輸入框中輸入指定的文本內容,這樣一個簡單的過程:
from selenium import webdriver browser = webdriver.Chrome() '''訪問百度首頁''' browser.get('http://www.baidu.com') '''對頁面右上角的登錄超連接進行定位,這裏由於同名超連接有兩個, 因此使用find_elements_by_xpath來捕獲一個元素列表,再對其中 咱們指定的對象執行點擊操做''' LoginElement = browser.find_elements_by_xpath("//a[@name='tj_login']") '''對指定元素進行點擊操做''' LoginElement[1].click() '''這段while語句是爲了防止信息塊沒加載完成致使出錯''' while True: try: '''捕獲彈出的信息塊中的註冊按鈕元素''' SignUpElement = browser.find_elements_by_xpath("//a[@class='pass-reglink pass-link']") '''點擊彈出的信息塊中的註冊超連接''' SignUpElement[0].click() break except Exception as e: pass '''將主網頁切換至新彈出的註冊頁面中以便對其頁面內元素進行定位''' browser.switch_to.window(browser.window_handles[-1]) while True: try: '''對用戶名稱輸入框對應元素進行定位''' InputElement = browser.find_element_by_xpath("//input[@name='userName']") '''模擬輸入指定的文本信息''' InputElement.send_keys('Keras') break except Exception as e: pass
以上就是關於selenium進行網絡數據採集的上篇內容,其他的內容我會擠出時間繼續整理介紹,敬請關注,若有筆誤,望指出。