1. 程式人生 > >[python爬蟲] Selenium定向爬取海量精美圖片及搜尋引擎雜談

[python爬蟲] Selenium定向爬取海量精美圖片及搜尋引擎雜談

        我自認為這是自己寫過部落格中一篇比較優秀的文章,同時也是在深夜凌晨2點滿懷著激情和愉悅之心完成的。首先通過這篇文章,你能學到以下幾點:
        1.可以瞭解Python簡單爬取圖片的一些思路和方法
        2.學習Selenium自動、測試分析動態網頁和正則表示式的區別和共同點
        3.瞭解作者最近學習得比較多的搜尋引擎和知識圖譜的整體框架
        4.同時作者最近找工作,裡面的一些雜談和建議也許對即將成為應屆生的你有所幫助
        5.當然,最重要的是你也可以嘗試使用這個爬蟲去爬取自己比較喜歡的圖片

        總之,希望文章對你有所幫助。如果作者又不足之處或錯誤的地方,還請海涵~

一. Python定向爬取海量圖片

        執行效果如下圖所示:
        這是從遊訊相簿中爬取圖片(非常不錯的網站,推薦大家去瀏覽),其它網站方法類似去修改。執行py檔案後,輸入“極品飛車”可以爬取主題相關的圖集。

        程式原始碼如下圖所示:

# -*- coding: utf-8 -*-
"""
Crawling pictures by selenium and urllib
url: http://pic.yxdown.com/list/0_0_1.html
Created on 2015-10-02 @author: Eastmount CSDN 
"""  
  
import time          
import re          
import os  
import sys
import urllib
import shutil
import datetime
from selenium import webdriver      
from selenium.webdriver.common.keys import Keys      
import selenium.webdriver.support.ui as ui      
from selenium.webdriver.common.action_chains import ActionChains  
  
#Open PhantomJS  
driver = webdriver.PhantomJS(executable_path="G:\phantomjs-1.9.1-windows\phantomjs.exe")  
#driver = webdriver.Firefox()
wait = ui.WebDriverWait(driver,10)  

#Download one Picture
def loadPicture(pic_url, pic_path):
    pic_name = os.path.basename(pic_url) #delete path, get the filename
    urllib.urlretrieve(pic_url, pic_path + pic_name)

#Visit the picture page and get <script>(.*?)</script>  original
def getScript(elem_url,path):
    print elem_url
    print path
    '''
    #Error: Message: Error Message => 'Element does not exist in cache'
    driver.get(elem_url)
    pic_url = driver.find_element_by_xpath("//div[@id='wrap']/div/div[2]/a")
    print pic_url.text
    '''
    #By urllib to download the original pics
    count = 1
    html_content = urllib.urlopen(elem_url).read()
    html_script = r'<script>(.*?)</script>'
    m_script = re.findall(html_script,html_content,re.S|re.M)
    for script in m_script:
        res_original = r'"original":"(.*?)"' #原圖
        m_original = re.findall(res_original,script)
        for pic_url in m_original:
            loadPicture(pic_url, path)
            count = count + 1
    else:
        print 'Download ' + str(count) + ' Pictures'
    
#Get the Title of the URL
def getTitle(key, url):
    try:
        #print key,type(key)
        count = 0
        print 'Function getTitle(key,url)'
        driver.get(url)
        wait.until(lambda driver: driver.find_element_by_xpath("//div[@class='masonry']/div/div[2]/a")) 
        elem_title = driver.find_elements_by_xpath("//div[@class='masonry']/div/div[2]/a")
        for title in elem_title:
            #title.text-unicode  key-str=>unicode
            #print key,title.text
            elem_url = title.get_attribute("href")
            if key in title.text:
                #print key,title.text
                path="E:\\Picture_DM\\"+title.text+"\\"
                if os.path.isfile(path):  #Delete file
                    os.remove(path)
                elif os.path.isdir(path): #Delete dir
                    shutil.rmtree(path,True)
                os.makedirs(path)         #create the file directory
                count = count + 1
                #print elem_url
                getScript(elem_url,path)  #visit pages 
                
    except Exception,e:
        print 'Error:',e
    finally:
        print 'Find ' + str(count) + ' pages with key\n'
    
#Enter Function
def main():
    #Create Folder
    basePathDirectory = "E:\\Picture_DM"
    if not os.path.exists(basePathDirectory):
        os.makedirs(basePathDirectory)

    #Input the Key for search  str=>unicode=>utf-8
    key = raw_input("Please input a key: ").decode(sys.stdin.encoding)
    print 'The key is : ' + key

    #Set URL List  Sum:1-73 Pages
    print 'Ready to start the Download!!!\n\n'
    starttime = datetime.datetime.now() 
    num=1
    while num<=73:
        url = 'http://pic.yxdown.com/list/0_0_'+str(num)+'.html'
        print '第'+str(num)+'頁','url:'+url
        #Determine whether the title contains key
        getTitle(key,url)
        time.sleep(2)
        num = num + 1
    else:
        print 'Download Over!!!'

    #get the runtime
    endtime = datetime.datetime.now()
    print 'The Running time : ',(endtime - starttime).seconds
        
main()
        該程式的基本框架如下圖所示:三個圖示Python+Phantomjs+Selenium


        參考:[python學習] 簡單爬取圖片網站相簿中圖片 其基本步驟為:
        第一步:迴圈遍歷圖集列表URL
        main()函式中迴圈輸入列表url,並呼叫getTitle(key,url)判斷主題(title)中是否包含輸入的關鍵詞(key),如"極品飛車"。

        這裡因為遊訊相簿存在第一個BUG,圖集共73頁,url採用順序形式儲存,每頁存在很多主題(title);當然很多網站都是這樣的,如CSDN部落格、CSDN下載、搜狐部落格等。
        
http://pic.yxdown.com/list/0_0_1.html

        http://pic.yxdown.com/list/0_0_73.html
        第二步:通過Selenium和Phantomjs尋找路徑爬取主題和URL
        如下圖所示,在函式getTitle()中通過selenium訪問無介面的瀏覽器Phantomjs,再遍歷HTML的DOM樹結構。
        其中核心程式碼如下:
driver.find_elements_by_xpath("//div[@class='masonry']/div/div[2]/a")
        它是Selenium按照路徑定位元素,其中DOM樹表示尋找class='masonry'的div,然後是子div,第二個div,最後是<a target='_blank',此處可以爬取主題title.text和屬性title.get_attribute("href")。對應原始碼:
        <div class='masonry'>
        <div class='conbox'>
            <div></div><div class='cbmiddle'>                                                //div[2]
                <a target="_blank"href="/html/6299.html" class="proimg">  //URL
                    <img src="http://yxdown.com/xxx.jpg" alt="...韓國女主播...">
                    <p><span>1440人看過</span> <em>8張</em></p>
                    <b class="imgname">...韓國女主播....</b>                              //主題
                 </a>
            </div>
        </div>
        <div class='conbox'>第二個主題</div>
<div class='conbox'>第二個主題</div>
           ....
        </div>
        同時獲取的href屬性 自動補齊,如:http://pic.yxdown.com/html/6299.html
        此處的driver.find_elements_by_xpath方法就比傳統的正則表示式好,同時它返回一個List。後面第三部分會詳細介紹下Selenium常用的方法。在raw_input輸入關鍵字比較過程中如果存在中文亂碼問題,參考這篇文章
第三步:根據主題建立資料夾並去到具體頁面爬取圖片
        通過比較關鍵詞key是否包含在主題title,如"主播"。如果在則建立該資料夾,同時移除同名檔案:
        if os.path.isfile(path):   #Delete file
            os.remove(path)
        elif os.path.isdir(path):#Delete dir
           shutil.rmtree(path,True)
        os.makedirs(path)       #create the file directory
        再呼叫getScript(elem_url,path)函式去爬取所在頁面的圖集
第四步:通過正則表示式獲取圖片url
        此時去到具體的圖集如:http://pic.yxdown.com/html/5533.html#p=1
        你有兩種方法獲取圖片,第一種方法是通過Selenium模擬滑鼠操作,點選"檢視原圖"再另存為圖片或者獲取原始碼<img src="http://xxx.jpg">屬性,再或者是獲取"檢視原圖"的當前url,driver.current_url。
        <a href="javascript:;" onclick="return false;" id="Original">檢視原圖</a>
        第二種方法是由於遊訊相簿的第二個bug,它把所有圖片都儲存在<script></script>,則通過urllib2獲取原圖original即可。
        res_original = r'"original":"(.*?)"' #原圖
        m_original = re.findall(res_original,script)
       
第五步:最後呼叫函式loadPicture(pic_url, pic_path)下載圖片即可
def loadPicture(pic_url, pic_path):
    pic_name = os.path.basename(pic_url) 
    urllib.urlretrieve(pic_url, pic_path + pic_name)
       總結
        講到此處,整個爬蟲基本講述完畢。使用Selenium的優點主要是:DOM樹結構爬取,可能Spider也是採用find_element_by_xpath()方法,都是類似的;第二個優點是獲取動態JS、AJax的資源或檔案。但是正則表示式也有它的好處和方便。
        前面我為什麼畫了一副框架圖,主要是最近研究搜尋引擎和知識圖譜比較多,所以這幅圖給人更直觀的感覺。第二部分會詳細說到。
        同時,你可能會遇到如下錯誤,可能和載入響應有關:

二. 搜尋引擎和知識圖譜雜談

       1.搜尋引擎
        下面是《這就是搜尋引擎·張俊林》裡面那張經典的框架圖。

搜尋引擎的通常是使用者輸入查詢詞,搜尋引擎返回搜尋結果。主要包括流程是:
搜尋引擎後臺計算系統
        搜尋引擎的資訊來源於網際網路網頁,通過網路爬蟲將整個網際網路的資訊獲取到本地,因此網際網路頁面中有很大部分內容是相同或相似的,“網頁去重”模組會對此作出檢測並去除重複內容。
        之後,搜尋引擎會對網頁進行解析,抽取出網頁主體內容及頁面中包含的指向其他頁面的連結。為加快響應使用者查詢的速度,網頁內容通過“倒排索引”這種高效查詢資料結構儲存,網頁之間的連結關係也會儲存。因為通過“連結分析”可以判斷頁面的相對重要性,對於為使用者提供準確的搜尋結果幫助很大。
        同時由於海量資料資訊巨大,所以採用雲端儲存與雲端計算平臺作為搜尋引擎及相關應用的基礎支撐。上述是關於搜尋引擎如何獲取及儲存海量的網頁相關資訊,不需要進行實時計算,所以被看做是搜尋引擎的後臺計算系統。
搜尋引擎前臺計算系統
        搜尋引擎的最重要目的是為使用者提供準確全面的搜尋結果,如何響應使用者查詢並實時地提供準確結果構成了搜尋引擎前臺計算系統。
        當搜尋引擎接到使用者的查詢詞後,首先對查詢詞進行分析,希望能夠結合查詢詞和使用者資訊來正確推導使用者的真正搜尋意圖。先在快取中查詢,快取系統中儲存了不同的查詢意圖對應的搜尋結果,如果能在快取中找到滿足使用者需求的資訊,則直接返回給使用者,即節省資源又加快響應速度。如果快取中不存在,則呼叫“網頁排序”模組功能。
        “網頁排序”會根據使用者的查詢實時計算哪些網頁是滿足使用者資訊需求的,並排序輸出作為搜尋結果。而網頁排序中最重要的兩個因素是:內容相似性因素(哪些網頁和使用者查詢相關)和網頁的重要性因素(哪些網頁質量好或相對重要,通過連結分析結果獲得)。然後網頁進行排序,作為使用者查詢的搜尋結果。
        同時,搜尋引擎的“反作弊”模組主要自動發現那些通過各種手段將網頁的搜尋排名提高到與其網頁質量不相稱的位置,這會嚴重影響搜尋體驗。現在也出現一種成功的新網際網路公司遮蔽搜尋引擎公司爬蟲的現象,比如Facebook對Google的遮蔽,國內淘寶對百度的遮蔽,主要是商業公司之間的競爭策略,也可看做是垂直搜尋和通用搜索的競爭。

        2.知識圖譜
        這是搜狗知立方知識圖譜的框架圖。詳見:文章
知識圖譜(Knowledge Graph)於2012年5月首先由Google提出,其目標在於描述真實世界中存在的各種實體和概念,及實體、概念之間的關聯關係,從而改善搜尋結果。緊隨其後,國內搜狗提出了“知立方”、微軟的Probase和百度的“知心”。其實質就是真正讓計算機去"理解"使用者的需求和搜尋意圖。
知立方資料庫構建包括本體構建(各型別實體挖掘、屬性名稱挖掘、編輯系統)、例項構建(純文字屬性、實體抽取、半結構化資料抽取)、異構資料整合(實體對齊、屬性值決策、關係建立)、實體重要度計算、推理完善資料。
3.爬蟲框架猜想
        說了這麼多,你是不是還不知道我想表達什麼內容啊?
        我在設想如果上面那個Python圖片爬蟲新增以下幾點:
        (1)圖片來源不是定向的某個網站,而是開放的網路、相簿、資料庫
        (2)搜尋中加入一些更復雜的搜尋,利用一些NLP分詞技術處理
        (3)通過一些相似計算,採用更高效的VSM、聚類、神經網路、人臉識別等,計算關鍵詞和圖片或標題的相似度,甚至實現百度圖片那種識別某張圖是某某明星
        (4)再建立倒排索引、連結分析加快檢索系統
        (5)海量圖片利用雲端計算、雲端儲存的支援,採用分散式並行更高效的操作
        (6)實現圖片智慧識別、圖片相關推薦,再根據不同使用者的log分析使用者的真正搜尋意圖

        通過以上幾點是不是能實現一個智慧的圖片知識計算引擎呢?谷歌、百度圖片那些智慧應用是不是也是這樣的呢?感覺非常有意思啊!
        PS:以後有機會自己研究研究,並且自己再寫一些更強大的智慧爬取吧!


三. Selenium簡單基礎學習

        前面我在Python爬蟲這個專題中講述了很多關於Selenium的文章,包括爬取百度百科InfoBox、配置安裝過程等。下面是簡單的Selenium定位元素的介紹:
        1.定位元素方法
        官網地址:http://selenium-python.readthedocs.org/locating-elements.html
        這裡有各種策略用於定位網頁中的元素(locate elements),你可以選擇最適合的方案,Selenium提供了一下方法來定義一個頁面中的元素:

  • find_element_by_id
  • find_element_by_name
  • find_element_by_xpath
  • find_element_by_link_text
  • find_element_by_partial_link_text
  • find_element_by_tag_name
  • find_element_by_class_name
  • find_element_by_css_selector
        下面是查詢多個元素(這些方法將返回一個列表):
  • 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

        除了上面給出的公共方法,這裡也有兩個在頁面物件定位器有用的私有方法。這兩個私有方法是find_element和find_elements。
        常用方法是通過xpath相對路徑進行定位,同時CSS也是比較好的方法。舉例:

  1. <html>
  2.  <body>
  3.   <formid="loginForm">
  4.    <inputname="username"type="text"/>
  5.    <inputname="password"type="password"/>
  6.    <inputname="continue"type="submit"value="Login"/>
  7.    <inputname="continue"type="button"value="Clear"/>
  8.   </form>
  9. </body>
  10. <html>
        定位username元素的方法如下:
  1. username = driver.find_element_by_xpath("//form[input/@name='username']")  
  2. username = driver.find_element_by_xpath("//form[@id='loginForm']/input[1]")  
  3. username = driver.find_element_by_xpath("//input[@name='username']")  
        [1] 第一個form元素通過一個input子元素,name屬性和值為username實現
        [2] 通過id=loginForm值的form元素找到第一個input子元素
        [3] 屬性名為name且值為username的第一個input元素

        2.driver介面獲取值
        通過WebElement介面可以獲取常用的值,這些值同樣非常重要。
  • size 獲取元素的尺寸
  • text 獲取元素的文字
  • get_attribute(name) 獲取屬性值
  • location 獲取元素座標,先找到要獲取的元素,再呼叫該方法
  • page_source 返回頁面原始碼
  • driver.title 返回頁面標題
  • current_url 獲取當前頁面的URL
  • is_displayed() 設定該元素是否可見
  • is_enabled() 判斷元素是否被使用
  • is_selected() 判斷元素是否被選中
  • tag_name 返回元素的tagName

四. 找工作雜談:你只是看起來很努力

        最近找工作之餘看了《你只是看起來很努力》,非常喜歡裡面的故事,而這些故事彷彿就是自己的折射,倒映著我們的身影,在此分享與君卿共勉,希望能引起你的共鳴。
我們看起來每天熬夜,卻只是拿著手機點了無數個贊;
        看起來在圖書館坐了一天,卻真的只是坐了一天;
        看起來買了很多書,只不過晒了個朋友圈;
        看起來每天很晚地離開辦公室,上班的時間卻在偷懶;
        那些所謂的努力時光,是真的頭腦風暴了,還是,只是看起來很努力而已?
        任何沒有計劃的學習,都只是作秀而已;
        任何沒有走心的努力,都只是看起來很努力。
        牢記一句話:“所有的努力都不是給別人看的,很多時候,英雄都是孤獨的”。

        書中第一個故事就是一個女孩垂頭喪氣的對老師說:“老師,我考了四次四級,還沒過,究竟是為什麼?”看著學生滿滿的筆記,老師心想,看起來很努力啊,沒理由不通過啊!哎,這不就是活生生的自己嗎?自己四級六次才通過,六級還由於分數太低被禁考。究其原因…
我們總會陷入這樣一個怪圈:看似忙碌,實則焦慮。當我們心血來潮想學習時,買了很多書,卻再也沒有翻開過;當我們備受刺激想健身時,於是找了很多攻略,但再也沒有動過;當我們在社交網路上花費很多時間把認為有用的東西另存為時,直到你的硬碟存得滿滿當當,然而你卻沒有看過。我們忙碌,卻沒有去了解那些精挑細選留下的內容;我們花時間收集,卻忘了最重要的其實是花時間去消化。你將會在一遍遍地逃避和自我安慰中變得惴惴不安,拖延和等待也終將擊垮你的鬥志。這就是我支教時分享過的一句話:“記筆記如果光記不看還不如不記”。
        不要看到別人做什麼好,就去嘗試做什麼,那些看起來的光鮮也有屬於他們自己的苦逼。當然,打敗焦慮的最好辦法就是去做那些讓你焦慮的事情。希望你看完這幾句話之後,也會有所感悟,真切地做起身邊的每一件事。路還是要一步步地走,可以走得很慢,但卻不能停止。
        最近找工作也不是很順利,這也驗證了一點:
       
努力的人很多,這個世界最不差的就是優秀的人,尤其是程式設計師,除非你無可替代。優秀是不夠的,一定要卓越,多學點拿得出手的東西,否則你只會得到一句“雖然你很優秀,但你運氣不好,這位置已經有人了”。
       
如果你的簡歷上都是些會Java語言、MySQL、HTML、Python等,這並沒有什麼亮點。還不如:對Java虛擬核心有過研究、熟悉PHP什麼開發框架、MongoDB做過應用、Ajax\JavaScript非同步訊息傳輸、熟悉Socket\TCP通訊、研究過SVM\神經網路演算法等。
         同時能內推的,儘量找師兄師姐幫忙內推,筆試題目各式各樣,各種基礎而且較深的知識,包括OS、計算機網路、資料庫、Linux、JavaScript、C++面向物件、演算法等,所以推薦內推直接考察你的專案能力。如果實在沒有機會內推,就需要儘早的準備基礎知識,否則你會吃虧。一般8月、9月、10月高峰期,所以暑假6-7月就要開始準備了。同時推薦兩個地方:一個是LeetCode刷題,這個是真心的有用啊!還有一個就是牛客網刷基礎的選擇題。
        再強調一遍:這個世界優秀的人太多,你需要的是卓越!
        其實不論多忙,我都會堅持寫部落格,我認為這個過程並不是學習了,而是休息放鬆的過程,而且能夠讓我體會到分享的樂趣,非常開心~困得不行了
        總之,希望文章對你有所幫助,如果不喜歡我的這種雜亂的敘述方式,請不要噴我,畢竟花費了自己的一夜經歷完成,尊重作者的勞動果實吧!歡迎隨便轉載~
       (By:Eastmount 2015-10-2 早上9點http://blog.csdn.net/eastmount/