序言
之前用python爬取網頁的時候,一直用的是regex或者自帶的庫sgmllib里的SGMLParser。但是遇到復雜一點的情況時,SGMLParser往往就不那么給力了!(哈,難道說我 too native了?畢竟beautifulSoup是繼承sgmlparser的么~)所以,我尋尋覓覓尋尋覓覓,發現了BeautifulSoup這么個玩意。BeautifulSoup提供了很人性化的parser tree,有了它,我們可以簡單的抽取出tagname, attrs, text等等等等...
install什么的,看這里 -> http://www.crummy.com/software/BeautifulSoup/
入門
(ps:其實入門什么的看官方文檔是最好的了,這里只是記錄一下簡單的用法。)
(official document link : http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html)
首先先介紹實際工作中最常用的幾個方法:
舉例的html代碼(就用官方例子好了):
1 <html> 2 <head> 3 <title>Page title</title> 4 </head> 5 <body> 6 <p id="firstpara" align="center"> 7 This is paragraph<b>one</b>. 8 </p> 9 <p id="secondpara" align="blah"> 10 This is paragraph<b>two</b>. 11 </p> 12 </body> 13 </html>
0、初始化:
1 soup = BeautifulSoup(html) # html為html源代碼字符串,type(html) == str
1、用tag獲取相應代碼塊的剖析樹:
既然要分析html,首先要找到對我們有用的tag塊,beautiful提供了非常便利的方式。
#當用tag作為搜索條件時,我們獲取的包含這個tag塊的剖析樹: #<tag><xxx>ooo</xxx></tag> #這里獲取head這個塊 head = soup.find('head') # or # head = soup.head # or # head = soup.contents[0].contents[0]
運行后,我們會得到:
1 <head> 2 <title>Page title</title> 3 </head>
這里小燈我還是推薦使用第二種方法,find方法在當前tag剖析樹(當前這個html代碼塊中)尋找符合條件的子樹並返回。find方法提供多種查詢方式,包括用喜聞樂見的regex哦~之后會詳細介紹。
contents屬性是一個列表,里面保存了該剖析樹的直接兒子。
如:
1 html = soup.contents[0] # <html> ... </html> 2 head = html.contents[0] # <head> ... </head> 3 body = html.contents[1] # <body> ... </body>
2、用contents[], parent, nextSibling, previousSibling尋找父子兄弟tag
為了更加方便靈活的分析html代碼塊,beautifulSoup提供了幾個簡單的方法直接獲取當前tag塊的父子兄弟。
假設我們已經獲得了body這個tag塊,我們想要尋找<html>, <head>, 第一個<p>, 第二個<p>這四個tag塊:
# body = soup.bodyhtml = body.parent # html是body的父親
head = body.previousSibling # head和body在同一層,是body的前一個兄弟
p1 = body.contents[0] # p1, p2都是body的兒子,我們用contents[0]取得p1 p2 = p1.nextSibling # p2與p1在同一層,是p1的后一個兄弟, 當然body.content[1]也可得到 print p1.text # u'This is paragraphone.' print p2.text # u'This is paragraphtwo.' # 注意:1,每個tag的text包括了它以及它子孫的text。2,所有text已經被自動轉 #為unicode,如果需要,可以自行轉碼encode(xxx)
然而,如果我們要尋找祖先或者孫子tag怎么辦呢?? 用while循環嗎? 不, beautifulsoup已經提供了方法。
3、用find, findParent, findNextSibling, findPreviousSibling尋找祖先或者子孫 tag:
有了上面的基礎,這里應該很好理解了,例如find方法(我理解和findChild是一樣的),就是以當前節點為起始,遍歷整個子樹,找到后返回。
而這些方法的復數形式,會找到所有符合要求的tag,以list的方式放回。他們的對應關系是:find->findall, findParent->findParents, findNextSibling->findNextSiblings...
如:
1 print soup.findAll('p') 2 # [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>, <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
這里我們重點講一下find的幾種用法,其他的類比:
find(name=None, attrs={}, recursive=True, text=None, **kwargs)
(ps:只講幾種用法,完整請看官方link : http://www.crummy.com/software/BeautifulSoup/bs3/documentation.zh.html#The%20basic%20find%20method:%20findAll%28name,%20attrs,%20recursive,%20text,%20limit,%20**kwargs%29)
1) 搜索tag:
1 find(tagname) # 直接搜索名為tagname的tag 如:find('head') 2 find(list) # 搜索在list中的tag,如: find(['head', 'body']) 3 find(dict) # 搜索在dict中的tag,如:find({'head':True, 'body':True}) 4 find(re.compile('')) # 搜索符合正則的tag, 如:find(re.compile('^p')) 搜索以p開頭的tag 5 find(lambda) # 搜索函數返回結果為true的tag, 如:find(lambda name: if len(name) == 1) 搜索長度為1的tag 6 find(True) # 搜索所有tag
2) 搜索屬性(attrs):
1 find(id='xxx') # 尋找id屬性為xxx的 2 find(attrs={id=re.compile('xxx'), algin='xxx'}) # 尋找id屬性符合正則且algin屬性為xxx的 3 find(attrs={id=True, algin=None}) # 尋找有id屬性但是沒有algin屬性的
3) 搜索文字(text):
注意,文字的搜索會導致其他搜索給的值如:tag, attrs都失效。
方法與搜索tag一致
4) recursive, limit:
recursive=False表示只搜索直接兒子,否則搜索整個子樹,默認為True。
當使用findAll或者類似返回list的方法時,limit屬性用於限制返回的數量,如findAll('p', limit=2): 返回首先找到的兩個tag
*4、用next,previous尋找上下文tag(少用)
這里我們主要看看next, next是取得當前的tag的下一個(按代碼從上到下的順序)tag塊。這與contents是不一樣的,千萬別混淆了哦^ ^
我們舉個栗子來看看
1 <a> 2 a 3 <b>b</b> 4 <c>c</c> 5 </a>
我們看看next的實際效果:
1 a = soup.a 2 b = soup.b 3 n1 = b.next 4 n2 = n1.next
輸出一下:
1 print a.next 2 # u'a' 3 print n1 4 # u'b' 5 print n2 6 # <c>c</c>
所以,next僅僅是獲取文檔上的“下一個”的tag,和剖析樹中的位置無關。
當然也有findNext和findAllNext方法。
至於previous, 表示上一個tag塊,就類比一下吧~^ ^
=======
待續