這陣子須要用爬蟲作點事情,因而系統的學習了一下python爬蟲,以爲還挺有意思的,比我想象中的能幹更多的事情,這裏記錄下學習的經歷。css
網上有關爬蟲的資料特別多,寫的都挺複雜的,我這裏不打算講什麼大道理,由於其實爬蟲挺好理解的。就是下面一個流程:html
爬蟲的功能就是把網頁源代碼想辦法爬下來,而後分析出須要的內容。總結起來就是2個部分:
1. 爬
2. 提取python
因此,整個爬蟲須要掌握的技能,就是如何高效的爬,如何快速的分析提取所須要的內容。正則表達式
說實話,以前爲了找爬蟲的教程,走了挺多彎路的,由於如今不少教程剛上來就介紹urllib,urllib2這兩個python自帶的有關網頁的包,因此剛開始個人單線程爬蟲實現也都是基於urllib的,不只代碼多,並且效率還低。實際上,目前來講,這兩個已經很過期了,目前用的比較多的是requests這個第三方包(這裏我也是偶然間發現極客學院有關爬蟲的視頻,讓我少走那麼多彎路,這裏我就不說是什麼視頻了,以避免有廣告的嫌疑,你們有興趣的能夠本身去搜)。
正如requests的官方網頁說的:markdown
Requests: HTTP for Humanscookie
它目前應該是python下最好的Http庫了。它還有不少別的特性:數據結構
Requests 使用的是 urllib3,繼承了urllib2的全部特性。Requests支持HTTP鏈接保持和鏈接池,支持使用cookie保持會話,支持文件上傳,支持自動肯定響應內容的編碼,支持國際化的 URL 和 POST 數據自動編碼。app
上面介紹的是單線程爬蟲,而後,若是要提升爬的效率,並行化確定必不可少,那麼scrapy就能夠解決你的問題。而後還有js動態加載的問題。那些我之後也會慢慢加上來。python爬蟲
pip install requests
全部的python第三方包的安裝均可以用pip,若是cmd中沒法輸入pip命令,請把C:\Python27\Scripts加入PATH環境變量。scrapy
注:這裏不推薦使用easy_install 由於這個只管安裝,無論卸載。
基本知道一個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的正則模塊是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是一個樹型的結構,比較符合「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你只須要知道這些語法
// 根節點
/ 下一層路徑
[@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!