Beautiful soup

对象类型

标签 tag

tag这个概念应该对应于XML的元素(Element), 包括起始与结束标签.

1
2
3
4
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b
type(tag)
# <class 'bs4.element.Tag'>

结构如下:

  • tag
    • name
    • attrs
    • string
标签名 name

每一个tag都有一个标签名, 可以被修改

1
2
tag.name
# u'b'
属性(attributes) attrs

访问方法:

  1. 直接使用dict方式: tag['class'] # u'boldest'
  2. 使用attrs: tag.attrs # {u'class': u'boldest'}

添加, 删除, 修改属性, 就跟操作dict类型一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tag['class'] = 'verybold' # 修改
tag['id'] = 1 # 添加
tag
# <blockquote class="verybold" id="1">Extremely bold</blockquote>

del tag['class'] # 删除
del tag['id']
tag
# <blockquote>Extremely bold</blockquote>

tag['class']
# KeyError: 'class'
print(tag.get('class'))
# None
多值属性(Multi-valued attributes)

使用HTML解析时, 以下属性的值会被自动分割成list:

  • rev
  • rel
  • accept-charset
  • headers
  • accesskey
1
2
3
css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.p['class']
# ["body", "strikeout"]

使用XML方式解析的话, 就不会有这种效果:

1
2
3
xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml')
xml_soup.p['class']
# u'body strikeout'
内容(NavigableString) string

tag 中间的内容可以通过string访问.

  • 除了和普通的Unicode String一样, 还可以使用查询和搜索.
  • 不能直接修改内容, 但是可以通过replace_with()
    1
    2
    3
    
    tag.string.replace_with("No longer bold")
    tag
    # <blockquote>No longer bold</blockquote>
    

查找

向下查找

  • 标签名直接查找: example tag.tag_name 将会查找到第一个标签名为tag_name的元素.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    soup.head
    # <head><title>The Dormouse's story</title></head>
    
    soup.title  # 跨级查询
    # <title>The Dormouse's story</title>
    
    
    soup.head.title  # 多级查询
    # <title>The Dormouse's story</title>
    
  • children:

    • .contents -- list
    • .children -- iterator
  • descendants: .descendants -- iterator
  • .string: 如果tag只有一个child, 而child至多只有一个标签, 那么.string 就可以代表内容
  • .strings: more than one thing inside a tag
  • .stripped_strings: 与.strings的区别是, 这个会删除掉多余的空格或换行.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    s1 = BeautifulSoup("<html><head><title>The Dormouse's story</title></head></html>")
    s2 = BeautifulSoup("<html><head><title>The Dormouse's <b>story</b></title></head></html>")
    
    s1.string
    # u"The Dormouse's story"
    
    s2.string # 夹杂了<b>story</b>标签
    # 
    
    for i in s2.strings:
        print repr(i)
    # u"The Dormouse's "
    # u'story'
    

向上查找

  • parent: .parent -- 一个元素的父节点只有一个
  • ansestor: .parents -- itertor, 所有的祖先

同级查找

  • .next_sibling & .previous_sibling
  • .next_siblings & .previous_siblings -- itertor version

向前与向后

  • next_element & previous_element
  • next_elements & previous_elements

搜索

支持的方法有:

1
2
3
4
5
6
┌─────────────────────────────────────────────────────────────────────────────────┐
│find_all               find_all_next          find_all_previous                  │
│find_next              find_next_sibling      find_next_siblings                 │
│find_parent            find_parents           find_previous                      │
│find_previous_sibling  find_previous_siblings                                    │
└─────────────────────────────────────────────────────────────────────────────────┘

它们接受的参数都是一样的, 只是搜索范围不一样:

1
find_all(name=None, attrs={}, recursive=True, ext=None, limit=None, **kwargs)
  • name: 查找tag的filter, 具体可见下面
  • attrs: 参数类型与name一样, 区别在于搜索的是attributes
  • recursive: 默认是采用递归搜索的方式, 也就是说会以及级往下找, 若只要限定搜索范围在直属child, 可设为False
  • limit: 限定搜索到的最大数目
  • **kwargs: 当参数非不是属于上述的, 就被转化为kwags
    • 指定属性过滤(和attrs一样), example soup.find_all(id='link2')
    • 通过class_搜索CSS class: soup.find_all("a", class_="sister")
    • test: 搜索string 而不是tag

filter

搜索的条件就是filter, 上述的name, attrs**kwargs都是接受filter. filter支持的类型有string, regular expression, list, function, or value True.

1
2
3
4
5
6
7
8
9
soup.find_all('b')
soup.find_all(re.compile("^b"))
soup.find_all(["a", "b"])

def has_class_but_no_id(tag):
    return tag.has_key('class') and not tag.has_key('id')
soup.find_all(has_class_but_no_id)

soup.find_all(True)

CSS selector

Beautiful Soup 支持CSS selector标准: .select()

1
2
3
4
5
6
7
soup.select("head > title")
# [<title>The Dormouse's story</title>]

soup.select("p > a")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie"  id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

修改xml树

添加

  • .append()
  • .new_string() & .new_tag()
  • .insert(), .insert_before() & .insert_after()

删除

  • .clear(): Tag.clear()只会删除tag的content
  • .decompose(): Tag.decompose() 会将Tag整个删除
  • .extract(): 提取一个tag或者string并返回. (原本的tag会被修改)

修改

  • 直接赋值修改(name, attrs等): Tag.name = "new name"
  • .replace_with(): 替换tag或者string
  • .wrap(): tag name 替换
  • .unwrap(): 只删除tag, 保留内容

Output

  • Pretty-printing: .prettify
  • Non-pretty: str(soup) or unicode(soup)
  • 只提取文本部分: .get_text()

指定Parser

BeautifulSoup()第二个参数可以指定parser类型.

1
BeautifulSoup("<a><b /></a>", "xml")

支持的类型有:

  • xml
  • lxml
  • html5lib
  • html.parser -- 默认

其中最大的区别在于HTML和XML解析的方式不一样. HTML可以将类似半开<b />转化成<b></b>, 但是XML parser不会.

1
2
3
4
5
6
BeautifulSoup("<a><b /></a>")
# <html><head></head><body><a><b></b></a></body></html>

BeautifulSoup("<a><b /></a>", "xml")
# <?xml version="1.0" encoding="utf-8"?>
# <a><b /></a>

留言