目录


上篇文章我们已经介绍过BeautifulSoup是一个提供一些简单的、python式的函数用来处理HTMLXML导航、搜索、修改分析树等功能的解释器。本文我们来详细介绍Advanced HTML Parsing。

1 BeautifulSoup的4大类

1.1 BeautifulSoup

BeautifulSoup:该对象表示的是一个文档的全部内容(文档树).大部分时候,可以把它当作Tag对象,是一个特殊的Tag,,我们可以分别获取它的名称,属性,文本以及类型来感受一下。

1
2
3
4
5
6
bsObj = BeautifulSoup(htmlHandler.read(), "html.parser")

print("name:", bsObj.name)                             #name: [document]
print("attrs:", bsObj.attrs)                           #attrs: {}
print("string:", bsObj.string)                         #string: None
print("type:", type(bsObj))                            #type: <class 'bs4.BeautifulSoup'>

1.2 Tag

Tag:就是HTML中的一个个标签。

1
2
3
4
5
6
people = bsObj.find("span",{"class":"green"})
print(people)                                          #<span class="green">Anna Pavlovna Scherer</span>
print("name:", people.name,type(people.name))          #name: span <class 'str'>
print("attrs:", people.attrs,type(people.attrs))       #attrs: {'class': ['green']} <class 'dict'>
print("string:", people.string,type(people.string))    #string: Anna Pavlovna Scherer <class 'bs4.element.NavigableString'>
print("type:", type(people))                           #type: <class 'bs4.element.Tag'>

1.3 NavigableString

NavigableString:Tag的string属性的类型,上面的type(people.string)即是NavigableString

1.4 Comment

Comment:是一个特殊类型的NavigableString对象,表示注释。

2 搜索文档树

2.1 findAll()

findAll(tag, attributes, recursive, text, limit, keywords):是BeautifulSoup类型里最重要的方法,它搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。返回值是bs4.element.ResultSet,ResultSet是List的子类。

在95%的情况下,你只需用到前两个参数。但是我们还是全面的深入了解各参数的意义。

tag:可以输入a string或者 a list(tuple也可以,因为list是mutable,tuple是immutable list在Python里) of string as tag names, 例如:

1
2
3
4
htmlHandler = urlopen("http://www.pythonscraping.com/pages/warandpeace.html")
bsObj = BeautifulSoup(htmlHandler.read(), "html.parser")
names = bsObj.findAll(("h1","h2","h3","h4","h5"))       #输出所有的h1,h2,h3,h4,h5
print(names)

attributes:用attributes dictionary 作为filter条件,返回任何满足attribute的tag,可以用于帅选CSS的Class,例如:

1
2
3
...
names = bsObj.findAll("span",{"class":"green"})         #输出class=green的span
...

recursive:是否搜索所有子孙节点,默认是。如果只想搜索tag的直接子节点,可以使用参数 recursive=False

1
2
3
...
names = bsObj.findAll("span",{"class":"green"}False)   #输出bsObj的满足class=green的直接子节点span
...

text:搜索满足相应条件的text的Tag。比如要text是”the princce”,即tag的text完全等同于the princce,并不是包括,代码如下:

1
2
3
...
names = bsObj.findAll(text="the prince")                #输出bsObj的满足text="the prince"的所有tag
...

limit:limit是结果的个数。find() 是findAll(limit=1)的情况。

keyword:在上述参数不能满足的情况下,keyword可以用来搜索一个特定的attribute(当然也可以用attributes参数)例如搜索所有id=”text”的tag:

1
2
3
...
names = bsObj.findAll(id="the prince")                   #输出bsObj的满足id="text"的所有tag
...

2.2 find()

find(tag, attributes, recursive, text, limit, keywords):是BeautifulSoup类型里findAll()方法limit=1的特殊情况,返回当前Tag的子孙节点里第一个满足筛选条件的Tag。

1
2
3
...
people = bsObj.find("span",{"class":"green"})           #返回第一个clss=grenn的span
...

3 遍历文档树

基于目标Tag的name和attribute,我们可以用find()findAll()来搜索满足条件的Tag。考虑另一种情况,如果需要基于一个Tag的当前位置来搜索相应的Tag(如兄弟,孩子,子孙,父母Tag),那么我们就要通过遍历文档树来访问。

3.1 Children and Descendants

Children:指直接子节点,类比于人类的孩子。通过tag的.children 生成器,可以对tag的孩子子节点进行循环。

1
2
3
4
...
for child in bsObj.find("table",{"id":"giftList"}).children:      #输出第一个id="giftList"的table Tag的所有孩子Tag
    print(child)                
...

Descendants:指所有子节点,类比于人类的子孙。Descendant包括Children。通过tag的.descendants 生成器,可以对tag的子孙节点进行循环。

1
2
3
4
...
for child in bsObj.find("table",{"id":"giftList"}).descendants:      #输出第一个id="giftList"的table Tag的所有子孙Tag,因此包括孩子,然后一层层剥离。
    print(child)                
...

3.2 Siblings

Siblings:指兄妹。通过tag的.next_sibling(s) 生成器,可以对tag的下一兄妹(们)循环;通过tag的.previous_sibling(s) 生成器,可以对tag的上一兄妹(们)循环。

1
2
3
4
...
for child in bsObj.find("table",{"id":"giftList"}).tr.next_siblings: #输出第一个id="giftList"的table Tag的第一个tr Tag的下一兄妹们。
    print(child)               
...
1
2
3
4
...
for child in bsObj.find("table",{"id":"giftList"}).tr.next_siblings: #输出第一个id="giftList"的table Tag的第一个tr Tag的所有上一兄妹们。
    print(child)               
...

3.3 Parents

Parents:指父辈。通过tag的.parent 生成器,可以定位到父亲;通过tag的.parents 生成器,可以定位到父亲,爷爷,一直往上到根Tag。

1
2
3
...
print(bsObj.find("img",{"src":"../img/gifts/img1.jpg"}).parent.previous_sibling.get_text())       //找到符合条件的img Tag的父亲的哥哥的文本。       
...

4 Regular Expression

4.1 Regular Expression Basics

Regular Expression的基础请见这里

4.2 Regular Expression With BeautifulSoup

find()findAll()函数里,可以应用正则表达式表示函数参数的pattern,进行更高级的Tag搜索。例如

1
2
3
4
5
...
import re
...
print(bsObj.findAll("img", src=re.compile("\.\./img/gifts/img.*\.jpg")))       //Regular Expressionsrcpattern进行match       
...

5 Accessing Attributes

Tag的.attrs是一个字典,可以对其访问,例如:

1
2
3
4
5
...
images= bsObj.findAll("img", src=re.compile("\.\./img/gifts/img.*\.jpg"))
image = images[0]
print(image.attrs['src'])								//输出image这个Tagsrc属性。
...

6 Lambda Expression

关于Lambda Expression的介绍见这里。简单来说,λ表达式是接受一个变量的匿名函数,可以作为高阶函数的输入或者输出参数。

BeautifulSoup的find()findAll()函数也支持λ表达式,如下:

1
2
3
4
5
...
tagWith2Attrs = bsObj.findAll(lambda tag: len(tag.attrs)==2) //找到所有属性数为2Tag
for tag in tagWith2Attrs:
    print(tag.attrs)									
...

7 Summary

Beautiful Soup框架是一个强大的解析HTML的框架,包括BeautifulSoupTagNavigableStringComment4个类。 其中BeautifulSoup对象表示的是一个html后者xml文档的全部内容(文档树)。它的find()findAll()(通过tag的name, attribute, keywords等参数, 以及正则表达式, λ表达式等)可以快速便捷地搜索Tag, 一个Tag有.name.attrs.string(NavigableString类)组成,且可以通过.children.descendants.next_sibling(s).previous_sibling(s).parents(s)等属性遍历文档树。

最后本文总结成下图以供参考。

web scraping

8 参考资料


Share Post

Twitter Google+

Shunmian

The only programmers in a position to see all the differences in power between the various languages are those who understand the most powerful one.