Python 处理HTML/XML——Beautiful Soup4

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.

本文为Beautiful Soup属性方法总结,更多例子请查阅官方文档


$ pip install beautifulsoup4    

#debian或Ubuntu下可以
$ apt-get install Python-bs4

加载BeautifulSoup库

>>> from bs4 import BeautifulSoup
>>> a = Beautiful('<html><head></head><body></body></html>')    #创建BeautifulSoup对象


文档解析器

    Beautiful Soup会自动选择一个解析器来解析文档,也可以通过参数来制定使用哪种解析器

            第一次使用BeautifulSoup构造函数如果没有指定解析器,会出现提示,无需担心,后续调用就不会出现该提示

    BeartifulSoup第一个参数为被解析的文档字符串或文件句柄,第二个参数来表示选择什么解析器,

    如果第二个参数为空,则根据当前系统安装的库自动选择解析器,lxml,html5lib,python标准库

>>> BeautifulSoup(markup, "html.parser")   
>>> BeautifulSoup(markup, "lxml")
>>> BeautifulSoup(markup, "lxml-xml")  #使用lxml库解析xml文档
>>> BeautifulSoup(markup, "xml")        
>>> BeautifulSoup(markup, "html5lib")

        推荐使用lxml作为解析器,速度快,容错能力强,效率高

安装lxml

$ pip install lxml

安装html5lib

$ pip install html5lib


Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment .


BeautifulSoup类

BeautifulSoup对象表示一个文档的全部内容,大部分时候,可以把它当作 Tag 对象,它支持遍历文档树和搜索文档树中描述的大部分的方法.

    它的name属性返回字符串'[document]'

    BeautifulSoup没有标签属性,所以无法使用字典索引或attrs属性

NavigableSoup类

        字符串通常被包含在tag对象内,而这些字符串使用NavigableSoup类用来包装

        标签与标签之间的字符串同样会被作为文档的节点,包装为NavigableSoup对象

        NabigableSoup对象支持遍历和搜索的大部分属性方法  但是不能包含其他对象,所以不支持.contents,.strings或find()等针对子节点的方法  

        在tag中包含的NavigableSoup对象可以通过replace_with()方法来替换字符串

          可以通过unicode()函数将NaviableSoup对象转会成Unicode字符串,通常在BeautifulSoup之外使用时进行转换


Comment类

        Comment对象是一个特殊类型的NavigableString对象,包含文档中的注释

Beautiful Soup还定义了一些其他类型CData , ProcessingInstruction , Declaration , Doctype用于处理XML文档,这些类也都是NavigableString类的子类

Tag对象

        tag.name        获取标签名字,可以更改该属性

        tag[attr_name]    获取该标签的attr_name属性,可以更改和添加attr_name属性的值

        tag.attrs            获取该标签的所有属性,返回一个字典

            如果属性为多值属性,则返回一个列表,但是如果该属性在html中没有被定义为多值属性,则返回字符串,

            对于xml,tag不包含多值属性,返回的都是字符串

Tag对象属性遍历文档树

子节点

        tag.tag_name    使用tag的名称tag_name属性来获取相应第一个匹配的后代节点tag对象

>>> a = bs('<html id="df"><body id="df"> <div> <p>body元素后代节点第一个p</p> </div> <p>body子节点第一个</p></body></html>')
>>> a.body.p
<p>body元素后代节点第一个p</p>

        tag.contents       将tag的子节点以列表的方式输出

>>> a = bs('<html id="df"><body id="df"> <p>sdfd</p><div><p></p></div></body></html>')
>>> a.body.contents
['',<p>sdfd</p>, <div><p></p></div>]
                                    注意空白符同样会作为子节点输出

        tag.children        生成器属性,可以对tag的子节点进行循环

        tag.descendants    生成器属性,可以对所有tag的后代节点进行递归循环 

        tag.string            如果tag只有一个NavigableString类型子节点,则string属性可以得到子节点

                                    如果tag仅有一个子节点,那么输出和该子节点的输出一样

                                    如果tag包含多个子节点,tag就不确定.string方法由谁来调用

        tag.strings           如果tag中包含多个字符串,可以使用.strings来循环获取

        tag.stripped_strings    输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容

                                                段首和段末的空白会被删除

父节点   

        tag.parent        获取tag元素的父节点

        tag.parents       生成器属性,递归获取所有父辈节点,最后返回None

兄弟节点

        tag.next_sibling    查询下一个同级节点,同级最后一个节点没有该属性

        tag.previous_sibling    查询上一个同级节点,同级第一个节点没有该属性

        tag.netx_siblings        生成器属性可以对当前节点的兄弟节点迭代输出

        tag.previous_siblings

          实际兄弟节点也许并不是下一个标签元素,而是文本节点NavigableString节点或者注释Comment对象


HTML解析器会根据标签的打开关闭和字符串来解析文档

<html><head><title>The Dormouse's story</title></head>
<p class="title"><b>The Dormouse's story</b></p>

                        针对上面的字段HTML解析器会打开html标签,打开head标签,打开title标签,添加字符串,关闭title标签,关闭head标签,打开p标签...

        tag.next_element        根据解析过程返回下一个被解析的对象(字符串或tag),结果可能于.next_sibling相同,但通常不一样

                                                next_element属性会先返回后代节点,然后返回兄弟节点

>>> a = BeautifulSoup('<html id="df"><body id="df"><div><p>xixi</p></div><p>sdfd</p></body></html>')
>>> a.div.next_sibling
<p>sdfd</p>
>>> a.div.next_element
<p>xixi</p>

        tag.previous_element       根据解析过程返回上一个被解析对象

        tag.next_elements         生成器属性

        tag.previous_elements


Tag对象方法搜索文档树

          搜索过滤器,用于搜索方法的参数来过滤搜索结果

                字符串

                正则表达式        如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的match()来匹配内容.

                列表                  将与列表中任一元素匹配的内容返回

                True                  可以匹配任何值

                方法                    定义一个方法,方法只接受一个元素参数,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False           

>>> def has_class(tag):
...     return tag.has_attr('class')
...
>>> a = bs('<html id="df"><body id="df"><div><p class="1">xixi</p></div><p>sdfd</p></body></html>')
>>> a.find(has_class)
<p class="1">xixi</p>

        tag.find_all(name, attrs, recursive, text ,limit, **kwargs)

                搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件

                name 参数可以查找所有名字为name的tag,字符串对象会被自动忽略掉

                text 参数用于搜索文档中字符串内容,可以和其他参数混合使用来过滤

soup.find_all("a", text="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]

                recursive 参数设为False,表示只搜索tag的直接子节点

                **kwargs 关键字参数

                                如果指定的名字参数不是搜索内置的参数名,搜索时会把该参数作为name参数的属性,参数的值可以是任何类型的过滤器

                         对于标签的class属性,可以使用class_参数,因为class为Python的保留字

                         对于多值属性可以使用字符串过滤器来全值完全匹配

>>> css_soup.find_all("p", class_="body strikeout")

                attrs 参数定义一个字典参数作为name参数的属性搜索,可以搜索包含特殊属性的tag

                limit 参数会限制返回的数量,否则如果文档很大的话搜索会很慢


        tag.find (name, attrs, recursive, text , **kwargs)

                等同于.find_all(...,limit=1),不过带有limit=1参数的find_all()方法返回的值是一个包含一个元素的列表,而find()方法返回单个tag对象

                当find_all()方法找不到目标时返回空列表,find()方法找不到目标,返回None


        tag.find_parents(...)

        tag.find_parent(...)

                用来搜索当前节点的父辈节点,这两个方法实际是对.parents属性的迭代


        tag.find_next_siblings(...)

        tag.find_next_sibling(...)        

                通过.next_siblings属性对当前tag之后的所有兄弟节点进行迭代


        tag.find_previous_siblings(...)

        tag.find_previous_sibling(...)

                通过.previous_siblings属性对当前tag之前的所有兄弟节点进行迭代


        tag.find_all_next(...)

        tag.find_next(...)

                通过.next_elements属性对当前tag之后的tag和字符串进行迭代


        tag.find_all_previous(...)

        tag.find_previous(...)

                通过.previous_elements属性对当前节点前面的tag和字符串进行迭代

CSS选择器

        .select()

                传入字符串参数可以使用CSS选择器的语法找到对应tag

>>> soup.select("body a")
>>> soup.select("head > title")
>>> soup.select(".sister")
>>> soup.select("#link1")
>>> soup.select('a[href$="tillie"]')


解析部分文档

         如果仅仅因为想要查找文档中的<a>标签而将整片文档进行解析,实在是浪费内存和时间

         可以使用SoupStrainer类定义某段内容,这样搜索文档时,就不必先解析全部文档了 


        创建一个SoupStrainer对象并作为parse_only参数传递给BeautifulSoup()构造方法

        SoupStrainer类接受与搜索方法相同的参数

        SoupStrainer(name, attrs,recursive,text,**kwargs)

                                  

>>> from bs4 import SoupStrainer
>>> only_a_tags = SoupStrainer("a")
>>> only_tags_with_id_link2 = SoupStrainer(id="link2")
>>> def is_short_string(string):
...    return len(string) < 10
...
>>> only_short_strings = SoupStrainer(text=is_short_string)    

>>> BeautifulSoup(html_doc, "html.parser", parse_only=only_a_tags)


修改文档树

                  tag.name            更改name属性可以重命名tag

                  tag[attr_name]    添加删除更改tag的属性值

                  tag.string            给string属性赋值相当于替代使用当前内容替代原来内容


                  beautifulsoup.new_string(string, class_name)       

                                              BeautifulSoup对象方法,创建一个NavigableString对象

                                               可传入NavigableString的任何子类作为第二个参数,构建相应对象,比如Comment

                  beautifulsoup.new_tag(name, **kwargs)   

                                             BeautifulSoup对象方法,创建一个Tag对象,第一个参数为标签的名字,其他关键字参数为标签属性

                  tag.append()        向tag中添加内容

                  tag.insert()            与列表的insert方法类似,在指定位置插入内容

>>> markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>'
>>> soup = BeautifulSoup(markup)
>>>soup.a.tag.insert(1, "but did not endorse ")
# <a href="http://example.com/">I linked to but did not endorse <i>example.com</i></a>

                  tag.insert_before()        在当前tag或文本节点前插入元素

                  tag.insert_after()            在当前tag或文本节点后插入元素

>>> soup = BeautifulSoup("<b>stop</b>")
>>> tag = soup.new_tag("i")
>>> tag.string = "Don't"
>>> soup.b.string.insert_before(tag)
>>> soup.b
<b><i>Don't</i>stop</b>

                  tag.clear()            移除当前tag中的内容

                  tag.extract()         将当前tag移除文档树,并作为方法结果返回,返回的依然是Tag对象

                  tag.decompose()    将当前节点移除文档树并完全销毁

                  tag.replace_with()        移除文档树中的某段内容,并用新tag或文本节点替代它

                  tag.wrap()        对指定的tag元素进行包装,并返回包装后的结果

>>> soup = BeautifulSoup("<p>I wish I was bold.</p>")
>>> soup.p.string.wrap(soup.new_tag("b"))
<b>I wish I was bold.</b>

                  tag.unwrap()    移除tag的当前tag.name标签,该方法常被用来进行标记的解包

>>> bs = BeautifulSoup('<a href="http://example.com/">I linked to <i>example.com<i></i></i></a>')
>>> bs.i.unwrap()
<i></i>
>>> bs
<html><body><a href="http://example.com/">I linked to example.com<i></i></a></body></html>


输出

            tag.prettify() 将文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行

>>> a = Beautiful('<html id="df">\n<body id="df"><div><p class="1">xixi</p></div><p>sdfd</p></body>\n</html>')
>>> a.prettify()
'<html id="df">\n <body id="df">\n  <div>\n   <p class="1">\n    xixi\n   </p>\n  </div>\n  <p>\n   sdfd\n  </p>\n </body>\n</html>'           

        如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup 对象或 Tag 对象使用Python的 unicode()str() 函数

>>> a
<html id="df">
<body id="df"><div><p class="1">xixi</p></div><p>sdfd</p></body>
</html>

       tag .get_text()    获取到tag中包含的所有文本内容包括子孙tag中的内容,并将结果作为Unicode字符串返回

>>> a = bs('<html id="df"><body id="df"><div><p class="1">\nxixi</p></div><p>\nsdfd</p></body></html>')
>>> a.get_text()
'\nxixi\nsdfd'    
>>> a.get_text('|')        #使用参数来指定文本之间的分隔符
'\nxixi|\nsdfd'
>>> a.get_text('|',strip=True)    #使用strip参数去除文本内容中的前后空白符
'xixi|sdfd'

编码

            任何HTML/XML文档都有自己的编码方式,但是使用Beautiful Soup解析后文档转换为Unicode编码        

           

            .orginal_encoding属性记录了自动识别的编码

            BeautifulSoup构造方法时可传入from_encoding参数指定编码方式

soup = BeautifulSoup(markup, from_encoding="iso-8859-8")

        #####未完待续####

猜你喜欢

转载自blog.csdn.net/DAGU131/article/details/79377293