python實現一個簡單的爬蟲

來源:互聯網
上載者:User

標籤:爬蟲   程式設計語言   Regex   python   

今天第一次寫爬蟲,感覺非常有趣!,中途也遇到了許多問題,所以寫篇部落格~

目標:爬取豆瓣編程類書籍中9分以上的


剛接觸爬蟲,說下我的認識(不一定準確^_^)

我們知道網頁的呈現也是用程式設計語言寫出來的,有源碼,每個網頁我們都可以查看它的源碼,我的瀏覽器快速鍵是Ctrl+U,

一般點擊右鍵就可以看見查看源碼。因為要爬取豆瓣的資料,那看看豆瓣圖書頁面的部分源碼



它所對應的資料是這樣的



那麼我們知道了,網頁上所能看見的每個資料在源碼上都能找到,有的點擊會跳轉也是因為源碼上連結著其他地方。

所以我們直接分析源碼即可。


思路

其實很簡單,我們想要爬取編程類9分以上的書籍。那麼從編程類的首頁開始爬取資料。

用Regex來過濾資料,取出9分以上的資料,關於Regex如果不熟悉可參考我的上篇文章^_^

然後我們在每個網頁的最後可以發現跳轉到下一個網頁的連結,通過Regex過濾出這個連結即可。

儲存這個連結作為下一次爬取的url。

我設定的結束條件是爬取當前頁的9分以上書籍為0時跳出迴圈,為了簡單起見。



看代碼:

#!/usr/bin/env python#coding:UTF-8import urllibimport re#得到一個網頁的源碼def gethtml(url):    #返回類似檔案描述副,可進行讀操作,read返回str    page = urllib.urlopen(url)    html = page.read()    return html#根據正則得到我們想要的資料def getdata(obj):    patt = r'(<dl>.+?>(9\.\d).+?</dl>)'    #編譯正則模組    pattern = re.compile(patt, re.DOTALL)    #findall查詢此頁面所有合格資料,返回list    m = pattern.findall(obj)    Len = len(m)    print 'len: %d' % Len    if m is not None:        for i in m:            print i[1],        print    else:        print 'not found'    #如果此頁面的9分資料為0,則認為沒有9分資料    if Len == 0:        return False    else:        return True#得到下一個頁面def getnextpage(obj):    #我們要爬去的每個頁面url前面都一樣,所以只換後面的資料即可    nextpage = 'http://www.douban.com/tag/%E7%BC%96%E7%A8%8B/book?start='    #根據正則匹配後面的資料,然後串連到一起    patt = '<span class="break">...</span>.+start=(\d+)'    pattern = re.compile(patt, re.DOTALL)    #下一個頁面只有一個符合資料,所以用search    m = pattern.search(obj)    if m is not None:        nextpage += m.group(1)    else:        print 'not found'    return nextpageif __name__ == '__main__':    #起始頁面    html = gethtml("http://www.douban.com/tag/%E7%BC%96%E7%A8%8B/book")        while 1:        if getdata(html) == False:            break        ret = getnextpage(html)        html = gethtml(ret)


注釋寫的比較詳細。運行結果我輸出了每個頁面的書籍分數,len是此頁面9分以上書籍的數量

運行結果:部分資料



遇到的問題:貌似都是Regex的問題 - - 。

1.

網頁源碼中每個書籍是這樣的



可以看見我們要分析的資料中間包含中文,過濾中文首先必須要在開頭加上

#coding:UTF-8,‘  .  ‘符號在正則中可以過濾任何字元,\w只能是字母和數字

其次這段資料中包含了許多分行符號號,在Regex中\s只能過濾空白字元,不能過濾斷行符號。

我們需要在

    #編譯正則模組    pattern = re.compile(patt, re.DOTALL)
裡面設則,DOTALL的意思就是忽略斷行符號符號


2.findall的問題

findall是正則匹配中一次匹配多個資料的函數

它返回的是一個list。

先來看看遇到的問題吧

在getdata中,我的需求是擷取每本書的整個標籤,也就是上面的圖片,但是我還想順便過濾出分數,就是把分數也作為一個分組可以查看。

類似m = search.( ),m.group(0),m.group(1)。

但是findall返回的是list,裡面的資料是str。

和小夥伴討論後又仔細了書,結論:

當Regex只有一個子組的時候(()是一個子組),findall( )返回子組匹配的字串組成的列表,如果運算式有多個子組,返回的是一個元組的列表

元組中的每個元素都是一個子組的匹配內容,像這樣的元組構成了返回列表中的元素

這也是為什麼代碼中我用的是m[1],因為返回的是一個元組唄!

順便吐槽下python核心編程這本書,我目前發現的印刷錯誤至少十幾處了!

而且findall( )這部分除了簡單的介紹外沒有給任何例子,結尾還說了句 “這些內容初次聽到可能感到費解,但如果你看看各種例子,就會明白了”

它沒給例子...


3.貪婪匹配

很重要,當時我只是簡單的看了看,沒太在意,結果實戰就出錯了

原本我的getdata正則是這樣寫的

def getdata(obj):    patt = r'(<dl>.+>(9\.\d).+</dl>)'
怎麼匹配也出不來

於是發現了貪婪匹配的問題

Regex預設是貪心匹配的,簡單來說,如果Regex用到萬用字元(*, +, ?)等,它在從左至右匹配的時候會盡量匹配

最長的字串。

看個例子:

#!/usr/bin/env python#coding:UTF-8import res = 'helloworld800-333-3333'patt = '.+(\d+-\d+-\d+)'m = re.search(patt, s)print m.group(1)
我們想得出結果800-333-3333這個號碼

結果卻是

因為patt前面的‘.+‘預設是貪婪匹配的,它會匹配它能匹配盡量多的字元,所以我們後面的\d+只能匹配了一個0而已

解決辦法是用非貪婪操作符號 ‘ ? ‘

那麼‘.+‘就不會盡量讀取多的字元

patt = '.+?(\d+-\d+-\d+)'
可以運行下試試,結果為800-333-3333


Regex問題的卻比較多,一個學長建議我用BeautifulSoup來解析,會方便很多,感興趣可以試試。


簡單的入門,見笑了 ^_^


python實現一個簡單的爬蟲

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.