1. 程式人生 > >【Python爬蟲】入門知識

【Python爬蟲】入門知識

爬蟲基本知識

本文目錄

寫在開頭

這陣子需要用爬蟲做點事情,於是系統的學習了一下python爬蟲,覺得還挺有意思的,比我想象中的能幹更多的事情,這裡記錄下學習的經歷。

網上有關爬蟲的資料特別多,寫的都挺複雜的,我這裡不打算講什麼大道理,因為其實爬蟲挺好理解的。就是下面一個流程:

Created with Raphaël 2.1.0網頁網頁原始碼正則表示式需要的內容

爬蟲的功能就是把網頁原始碼想辦法爬下來,然後分析出需要的內容。總結起來就是2個部分:
1. 爬
2. 提取

所以,整個爬蟲需要掌握的技能,就是如何高效的爬,如何快速的分析提取所需要的內容。

如何爬?

Requests

說實話,之前為了找爬蟲的教程,走了挺多彎路的,因為現在很多教程剛上來就介紹urllib,urllib2這兩個python自帶的有關網頁的包,所以剛開始我的單執行緒爬蟲實現也都是基於urllib的,不僅程式碼多,而且效率還低。實際上,目前來說,這兩個已經很過時了,目前用的比較多的是requests這個第三方包(這裡我也是偶然間發現極客學院有關爬蟲的視訊,讓我少走那麼多彎路,這裡我就不說是什麼視訊了,以免有廣告的嫌疑,大家有興趣的可以自己去搜)。
正如requests的官方網頁說的:

Requests: HTTP for Humans

它目前應該是python下最好的Http庫了。它還有很多別的特性:

Requests 使用的是 urllib3,繼承了urllib2的所有特性。Requests支援HTTP連線保持和連線池,支援使用cookie保持會話,支援檔案上傳,支援自動確定響應內容的編碼,支援國際化的 URL 和 POST 資料自動編碼。

上面介紹的是單執行緒爬蟲,然後,如果要提高爬的效率,並行化肯定必不可少,那麼scrapy就可以解決你的問題。然後還有js動態載入的問題。那些我以後也會慢慢加上來。

Requests安裝

pip install requests

所有的python第三方包的安裝都可以用pip,如果cmd中無法輸入pip命令,請把C:\Python27\Scripts加入PATH環境變數。

注:這裡不推薦使用easy_install 因為這個只管安裝,不管解除安裝。

Requests使用

基本知道一個requests.get()和requests.post()就行了。

同樣它還有
requests.head()
requests.delete()
功能,不過用的不多。需要的時候,查手冊就好了。
這裡有個文件寫requests寫的挺全面的。可以看看:requests快速上手

requests的返回值可以有多種形式輸出,最常用的是
“.text”和”.content”,前者輸出unicode,後者輸出二進位制

 import requests  
 url = 'http://www.baidu.com'
 html = requests.get(url)
 print html.text

輸出:

<!DOCTYPE html><!--STATUS OK--><html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"><meta http-equiv="X-UA-Compatible" content="IE=Edge"><meta content="always" name="referrer"><meta name="theme-color" content="#2932e1"><link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" /><link rel="search" type="application/opensearchdescription+xml" href="/content-search.xml" title="百度搜索" /><link rel="icon" sizes="any" mask href="//www.baidu.com/img/baidu.svg"><link rel="dns-prefetch" href="//s1.bdstatic.com"/><link rel="dns-prefetch" href="//t1.baidu.com"/><link rel="dns-prefetch" href="//t2.baidu.com"/><link rel="dns-prefetch" href="//t3.baidu.com"/><link rel="dns-prefetch" href="//t10.baidu.com"/><link rel="dns-prefetch" href="//t11.baidu.com"/><link rel="dns-prefetch" href="//t12.baidu.com"/><link rel="dns-prefetch" href="//b1.bdstatic.com"/><title>百度一下,你就知道</title>
……
……

如何提取?

正則表示式

正則表示式是一個大頭!很多也都聽過正則表示式,第一印象就是記不住,但是其實也不用特別記憶,因為在爬蟲裡,用的最多的基本就一個

(.*?)

( ) :表示這個內容是我們需要提取的
.* :表示匹配任意字元0到n次
?:表示非貪心,找對第一個就停下來

我來解釋下為什麼在爬蟲裡只要這個pattern就行了。
在html網頁原始碼中,我們需要找的內容一般都是被某些標籤包圍的,如果我們能保證找到我們需要的內容左右的標籤(並且他們是獨一無二的)那麼我們很容易寫出一個正則表示式:

<XXX>(.*?)</XXX>

把其中的內容提取出來

python正則模組使用

python的正則模組是re,主要用的函式是(re.S的意思是讓”.”可以匹配換行符,不然有些標籤頭和尾是分幾行的,就會匹配失敗

findall(pattern,str,re.S)

主力部隊,把所有滿足正則的內容提取出來,用於匹配滿足某個條件的大量我們需要的內容。(比如所有的圖片,所有的網址,所有的回覆,所有的連結……)。它在網頁提取中佔了主要地位,工作量大,任務重,所以是主力部隊。

search(pattern,str,re.S)

狙擊手,用來匹配第一個找到的元素,它的目標目的就是找到我們明顯知道只有一個的元素比如標題什麼的,一旦找到就結束,所以它的執行速度很快。它的目標明確,效率高,所以是狙擊手的角色。

sub(pattern,str,replace)

後勤,它的功能是替換,一般用於替換一個網頁地址中的關鍵詞,替換頁碼等。它看似不重要,但是往往能在很多方面給我們提供便利,所以是後勤。

注意:正則有時候一步不能完成我們需要的功能,可能需要進行幾步操作,這時候,我們一般先提取大的部分,在從大部分裡面提取我們需要的部分

我們看個很簡單的例子:

import re

#假設下面是一個原始碼,我想儲存裡面所有的連結
text = '<a href = "www.baidu.com">....'
urls = re.findall('<a href = (.*?)>',text,re.S)
for each in urls:
    print each

#假設我需要爬取當前網頁的頭部
html = '''
<html>
<title>爬蟲的基本知識</title>
<body>
……
</body>
</html>
'''
print re.search('<title>(.*?)</title>',html,re.S).group(1)
#這裡group(1)表示第一個括號的內容,如果正則裡面有多個括號,這裡可以通過group(i)返回第i個空格里的內容



#假設下面是一個貼吧的帖子地址,有很多頁,每一頁就是靠後面的pn=幾來區分的,我們輸出前10頁的網址
Pages = 'http://tieba.baidu.com/p/4342201077?pn=1'
for i in range(10): 
    print re.sub('pn=\d','pn=%d'%i,Pages)

輸出:

"www.baidu.com"
爬蟲的基本知識
http://tieba.baidu.com/p/4342201077?pn=0
http://tieba.baidu.com/p/4342201077?pn=1
http://tieba.baidu.com/p/4342201077?pn=2
http://tieba.baidu.com/p/4342201077?pn=3
http://tieba.baidu.com/p/4342201077?pn=4
http://tieba.baidu.com/p/4342201077?pn=5
http://tieba.baidu.com/p/4342201077?pn=6
http://tieba.baidu.com/p/4342201077?pn=7
http://tieba.baidu.com/p/4342201077?pn=8
http://tieba.baidu.com/p/4342201077?pn=9

XPath

如果說正則表示式就已經讓你覺得很神奇了,那XPath真是要嚇死你了。這真是個神器,它讓提取資訊網頁資訊變得更加輕鬆。XPath是一個樹型的結構,比較符合“html”的層次結構。

XPath即為XML路徑語言,它是一種用來確定XML(標準通用標記語言的子集)文件中某部分位置的語言。XPath基於XML的樹狀結構,提供在資料結構樹中找尋節點的能力。起初 XPath 的提出的初衷是將其作為一個通用的、介於XPointer與XSLT間的語法模型。但是 XPath 很快的被開發者採用來當作小型查詢語言。

我覺得視訊中老師的解釋超精彩:如果你提取資訊就像讓你找一棟建築,那麼正則就是告訴你建築左邊是什麼,右邊是什麼,但是全國可能有很多都滿足條件的,你找起來還是不方便。

紅房子(.*?)綠房子

XPath就是告訴你,這個建築在北京市——海淀區——中關村——15號街——那棟黃色的建築,你可以馬上找到對應的建築。如果一個地名只有一個地方有,那麼你更可以簡化成“中關村——15號街”

//北京市/海淀區/中關村/15號街[@房子顏色=黃色]/text()
//中關村/15號[@房子顏色=黃色]/text()

也許在這裡你還沒能體會到他們之間的差別,但是相信我,當你遇到複雜的html分析的時候,你會發現它的厲害之處的。比如下面的例子,我想把Hello,my world!打印出來用正則需要考慮一下吧?但是用XPath就簡單很多

<div id="class">Hello,
    <font color=red>my</font>
    <font color=green>world!</font>
<div>

XPath語法

XPath你只需要知道這些語法

// 根節點
/ 下一層路徑
[@XX=xx] 特定的標籤

/text() 以文字返回
/@para 返回引數

string(.) 當前層的所有內容作為一個字串輸出
start-with(str) 所有以這個str開頭的標籤

下面是一個簡單的例子

from lxml import etree
html=
'''
<div id="test1">content1</div>
<div id="test2">content2</div>
<div id="test3">content3</div>
'''

selector = etree.HTML(html)
content = selector.XPath('//div[start-with(@id,"test")]/text()')
for each in content:
    print each

html1=
'''
<div id="class">Hello,
    <font color=red>my</font>
    <font color=green>world!</font>
<div>
'''

selector = etree.HTML(html)
tmp = selector.XPath('//div[@id="class"]')[0]
info = tmp.XPath('string(.)')
content2 = info.replace('\n','')
print content2

輸出:

content 1
content 2
content 3
Hello,        my        world!