zoukankan      html  css  js  c++  java
  • Python beautifulSoup

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

    Tag、NavigableString、BeautifulSoup 、Comment

    Tag对象与XML或HTML原生文档中的Tag相同,比如<title>The Dormouse's story </title>或者<a href ="http://example.com/elsie" class="sister" id="link1">Elsie</a>,title和a标记及其里面的内容称为Tag对象

    怎么样从soup对象中抽取Tag呢?示例如下html

    <html><head><title> The Dormouse's story</title></head>
    <body>
    <p class="title"><b> The Dormouse's story </b></p>
    <p class="story"> Once upon a time there were three little sisters;and their names were

    <a href="http://example.com/elsie" class="sister" id='link1'><!--Elsie--></a>
    <a href="http://example.com/lacie" class="sister" id='link2'><!--Lacie--></a>
    <a href="http://example.com/tillie" class="sister" id='link3'>Tillie</a>

    and they lived at the bottom of a well</p>

    <p class="story">...</p>

    </body></html>

    #coding:utf-8

    from bs4 import BeautifulSoup

    soup = BeautifulSoup(open('index.html'),'lxml')

    print soup.title  #抽取title

    print soup.a    #抽取 a

    print soup.p   #抽取p

    从例子中可以看到利用soup加标记名就可以获取这些标记的内容。不过利用这种方式,查找的是所有内容中第一个符合要求的标记

    Tag中有两个重要的属性:name 和attributes。每个Tag都有自己的名字,通过.name来获取

    print soup.p.name

    print soup.a.name

    Tag不仅可以获取name,还可以修改name,改变之后将影响所有通过当前Beautiful Soup对象生成的HTML文档

    soup.title.name = 'mytitle'

    print soup.title

    print soup.mytitle

    这里已经将title标记成功修改为mytitle

    Tag中的属性,<p class="title"><b> The Dormouse's story</b></p> 有一个class属性值为title,Tag的属性的操作方法与字典相同:

    print soup.p['class']

    print soup.p.get('class')

    也可以直接点取属性,比如.attrs,用于获取Tag中所有属性:

    print soup.p.attrs

    和name一样,我们可以对标记中的这些属性和内容进行修改,示例如下

    soup.p['class'] = 'myClass'

    NavigableString

    我们已经得到了标记的内容,要想获取标记内部的文字,需要用到.string示例如下:

    print soup.p.string

    print type(soup.p.string)

    BeautifulSoup用NavigableString类来包装Tag中的字符串,一个NavigableString字符串与Python中的Unicode字符串相同,通过unicode()方法可以直接将NavigableString对象转换成Unicode字符串

    BeautifulSoup

    BeautifulSoup对象表示的是一个文档的全部内容。大部分时候,可以把它当做Tag对象,是一个特殊的Tag,因为BeautifulSoup对象并不是真正的HTML或XML的标记,所以它没有name和attribute属性

    Comment

    Tag,NavigableString、BeautifulSoup几乎覆盖了HTML和XML中的所有内容,但是还有一些特殊对象,容易让人担心的内容是文档的注释部分

    print soup.a.string

    a标记里的内容实际上是注释,但是如果我们利用.string来输出它的内容,会发现它已经把注释符号去掉了。另外如果打印输出它的类型,会发现它是一个Comment类型。如果在我们不清楚这个标记.string的情况下,可能造成数据提取混乱。因此在提取字符串时,可以判断一下类型

    if type(soup.a.string) == "bs4.element.Comment":

    print soup.a.string

    遍历文档树

    BeautifulSoup 会将HTML转换为文档树进行搜索,既然是树形结构,节点的概念必不可少

    子节点:

    首先说一下直接子节点,Tag中的.contents和.children是非常重要的。Tag的.content属性可以将Tag子节点以列表的方式输出

    print soup.head.contents

    有一点需要注意:字符串没有.contents属性,因为字符串没有子节点 .children属性返回的是一个生成器,可以对Tag的子节点进行循环

    for child in soup.head.children:

    print(child)

    .contents和.children属性仅包含Tag的直接子节点。例如,<head>标记只有一个直接子节点<title>。但是<title>标记也包含一个子节点:字符串"The  Dormouse's story",这种情况下字符串也属于<head>标记的子孙节点。.descendants属性可以对所有tag的子孙节点进行

    递归循环


    for child in soup.head.descendants:

    print child

    以上都是关于如何获取子节点,接下来说一下如何获取节点的内容,这就涉及.string、strings、stripped_strings三个属性.

    .string这个属性很有特点:如果一个标记里面没有标记了,那么.string就会返回标记里面的内容。如果标记里面只有唯一的一个标记了,那么.string也会返回最里面的内容。如果tag包含了多个子节点,tag就无法确定,string方法应该调用那个子节点的内容,

    .string的输出结果是None

    .strings属性主要应用于tag中包含多个字符串的情况,可以进行循环遍历

    for string in soup.strings:

    print repr(string)

    .stripped_strings和strings类似,.stripped_strings属性可以去掉输出字符串中包含的空格或空行,示例如下:

    for string in soup.stripped_strings:

    print repr(string)

    父节点: 

    每个Tag或字符串都有父节点:被包含在某个Tag中 通过.parent属性来获取某个元素的父节点

    print soup.title.parent

    通过元素的.parents属性可以递归得到元素的所有父辈节点

    for parent in soup.a.parents:

    if parent is None:

    print(parent)
    else:

    print(parent.name)

    兄弟节点:

    兄弟节点可以理解为和本节点处在同一级的节点,.next_sibling属性可以获取该节点的下一个兄弟节点 .previous_sibling则与之相反,如果节点不存在,则返回None

    print soup.p.next_sibling

    print soup.p.prev_sibling

    print soup.p.next_sibling.next_sibling

    第一个输出结果为空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行

    通过.next_siblings和.previous_siblings属性可以对当前节点的兄弟节点迭代输出:

    for sibling in soup.a.next_siblings:

    print(repr(sibling))

    前后节点:

    前后节点需要使用.next_element、previous_element这两个属性与.next_sibling  .previous_sibling不同,它并不是针对于兄弟节点,而是针对所有节点,不分层次

    print soup.head

    print soup.head.next_element

    如果想遍历所有的前节点或者后节点,通过.next_elements 和.previous_elements 的迭代器就可以向前或向后访问文档的解析内容


    for element in soup.a.next_elements:

    print(repr(element))

    搜索文档树:

    BeautifulSoup定义了很多搜素方法,这里着重介绍find_all()方法

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

    name 参数可以查找所有名字为name的标记,字符串对象会被自动忽略掉。name参数取值可以是字符串、正则表达式、列表、True和方法,最简单的过滤器是字符串。在搜索方法中传入一个字符串参数,BeautifulSoup会查找与字符串完整匹配的内容

    print soup.find_all('b')

    如果传入正则表达式作为参数,BeautifulSoup会通过正则表达式的match()来匹配内容。下面的列子中找出所有以b开头的标记

    import re

    for tag in soup.find_all(re.compile("^b"))

        print(tag.name)

    如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回,下面的代码找到文档中所有<a>标记和<b>标记

    print soup.find_all(['a','b'])

    如果传入的参数是True,True可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点(只会返回标签)

    for tag in soup.find_all(True):
    print tag.name

    如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数Tag节点,如果这个方法返回True表示当前元素匹配并且被找到,如果不是则返回False。比如过滤包含class属性,也包含id属性的元素

    def hasClass_Id(tag):

    return tag.has_attr('class') and tag.has_attr('id')
    tag = soup.find_all(hasClass_Id)

    print tag

    2 kwargs 参数

    kwargs参数在python 中表示keyword参数。如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当做指定名字Tag的属性来搜索。搜索指定名字的属性时可以使用的参数值包括字符串、正则表达式、列表、True

    如果包含id参数,Beautiful Soup会搜索每个tag的id属性

    print soup.find_all(id='link2')

    如果出入href参数,BeautifulSoup会搜索每个Tag的href属性。比如查找href属性中含有elsie的tag

    print soup.find_all(href = re.compile('elsie'))

    下面的代码在文档树中查找所有包含id属性的Tag,无论id的值是什么

    print soup.find_all(id = True)

    如果我们想用class过滤,但是class是python的关键字,需要在class后面加个下划线:

    print soup.find_all('a',class_='sister')

    使用多个指定名字的参数可以同时过滤tag的多个属性

    print soup.find_all(href = re.compile('elsie'),id = 'link1')

    有些tag属性在搜索中不能使用,比如HTML5中的data-*属性

    data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')

    data_soup.find_all(data-foo = "value")这样的代码在Python中是不合法的,但是可以通过find_all()方法的attrs参数定义一个字典参数来搜索包含特殊属性的tag

    data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')

    data_soup.find_all(attrs={"data-foo":"value"})

    text 参数

    通过text参数可以搜索文档中的字符串内容。与name参数的可选值一样,text参数接受 字符串、正则表达式、列表、True。

    print soup.find_all(text = "Elsie")

    print soup.find_all(text = ["Tillie","Elsie","Lacie"])

    print soup.find_all(text = re.compile("Dormouse"))

    limit 参数

    find_all()方法返回全部的搜索结构,如果文档树很大那么搜索会很慢。如果我们不需要全部结果,可以使用limit参数限制返回结果的数量

    print soup.find_all('a',limit = 1)

    print soup.find_all('a',limit = 3)

    recursive参数

    调用tag的find_all()方法时,BeautifulSoup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数recursive=Fasle

    CSS选择器:

    用到的方法是soup.select(),返回类型是list

     1:通过标记名称进行查找

    通过标记名称可以直接查找、逐层查找,也可以找到某个标记下的直接子标记和兄弟节点标记

    #直接查找title标记
    print soup.select('title')

    #逐层查找title标记

    print soup.select('html head title')

    #查找直接子节点
    #查找head下的title标记

    print soup.select("head > title")

    #查找p下的id =‘link1’的标记

    print soup.select("p > #link1")

    #查找兄弟节点
    #查找id=link1之后class=sister的所有兄弟标记

    print soup.select("#link1 ~ .sister")

    #查找紧跟着id = "link1"之后class=sister的子标记
    print soup.select("#link1 + .sister")

    通过css的类名查找:

    print soup.select(".sister")

    print soup.select("[class~=sister]")

    通过tag的id查找: 

    print soup.select("#link1")

    print soup.select("a#link1")

    通过属性值来查找:

    print soup.select('a[href="http://example.com/elsie"]') 

  • 相关阅读:
    怎样把顶级控件加在另一个控件上?
    GML规范相关资源
    VB6.0的事件、回调函数等
    [转]使用ErrorProvider改善用户体验
    今日新增3D词汇
    [转]在用数据绑定的时候我为什么不能把焦点移出(Tab out)我的控件?(译)
    [翻译]Windows Phone 7 Application Controls
    转帖Windows Phone 7开发环境搭建
    利用SDF2.3获取Windows Mobile上的IP地址和MAC地址
    “2010 GCR MVP Open Day”之行
  • 原文地址:https://www.cnblogs.com/paulversion/p/8330482.html
Copyright © 2011-2022 走看看