Bs4

2016/06/08 Spider 阅读次数:

摘要 : BeautifulSoup 从基本的使用,到搜索文档树,搜索字符串,CSS选择器使用代码示例

# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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> and
<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>
"""

# 创建 Beautiful Soup 对象
soup = BeautifulSoup(html)

# 打印出title标签
print(soup.title)

# 打印出第一个p标签
print(soup.p)

# 打印出第一个a标签
print(soup.a)

# 打印head标签的内容
print(soup.head)

# 打印出来的结果是整个boby里面的所有标签
print(soup.body)

# <class 'bs4.element.Tag'> 是这个类型,tag有两个属性 一个name,一个attrs
print(type(soup.p))

print("*" * 30)

# 这个输出的是[document]
print(soup.name)

# 输出标签名字title
print(soup.title.name)
# 输出标签head名字
print(soup.head.name)

# 输入一个空字典,是因为title没写属性
print(soup.title.attrs)

# 是把标签里的所有属性打印出来,会得到一个字典型数据
print(soup.p.attrs)

# 获取标签属性值,以下三种方法获取的结果一样
print(soup.p.attrs['class'])
print(soup.p['class'])
print(soup.p.get('class'))

# 修改属性值
soup.p['class']='hei'
print(soup.p) # 查看修改后的结果

# 删除属性值
del soup.p['class']
print(soup.p) # 查看删除后的属性值

# 获取标签的内容 .string
print(soup.title.string)
# 获取p标签的内容
print(soup.p.string)
# 获取a标签的内容
print(soup.a.string)

# 直接子节点 :.contents .children 属性
# contents是获取该标签下所有子标签,返回的结果是一个列表
print(soup.head.contents)
# 取出boby标签的儿子标签
print(soup.body.contents)

' .children 看一下,可以发现它是一个 list 生成器对象' \
'它返回的不是一个 list,不过我们可以通过遍历获取所有子节点'

# .children 返回的是一个生成器
print(soup.head.children)

# .children结果是一个生成器,可以遍历取出每个元素结果
for child in soup.body.children:
print(child)

print('-' * 30)
# 所有子孙节点: .descendants 属性
print(soup.descendants)

for descdan in soup.descendants:
print(descdan)

搜索文档树

"""
find_all(name, attrs, recursive, text, **kwargs)
括号里面的参数:name是指标签名,查找所有的该标签,字符串会被自动忽略
"""
# 查找文档中所有b标签,返回结果是一个列表
print(soup.find_all('b'))

# 查找文档中所有a标签,返回结果是一个列表
print(soup.find_all('a'))

# 传入正则表达试,查找包含以b开头的标签
for tag in soup.find_all(re.compile('b')):
# 输出标签名字
print(tag.name)

# 如果传入列表,满足列表中任意一个的标签
for tag in soup.find_all(['a','b']):
print(tag.name)

# 如果keyword 参数,是查找属性class等于sister的标签
print(soup.find_all(class_='sister'))

print(soup.find_all(id='link1'))

搜索文档中的字符串

# 搜索文档中的字符串内容
print(soup.find_all(text=["Tillie", "Elsie", "Lacie"]))

# 搜索文本中包含Dormouse的字符串
print(soup.find_all(text=re.compile("Dormouse")))


注意的是:

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

data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
data_soup.find_all(data-foo="value")
# SyntaxError: keyword can't be an expression
但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:

data_soup.find_all(attrs={"data-foo": "value"})
# [<div data-foo="value">foo!</div>]    
表达式可以是字符串、布尔值、正则表达式
2.class属性要用class_=""

 

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

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件.这里有几个例子:

soup.find_all("title")
# [<title>The Dormouse's story</title>]

soup.find_all("p", "title")
# [<p class="title"><b>The Dormouse's story</b></p>]

soup.find_all("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>]

soup.find_all(id="link2")
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

import re
soup.find(text=re.compile("sisters"))
# u'Once upon a time there were three little sisters; and their names were\n'
有几个方法很相似,还有几个方法是新的,参数中的 text 和 id 是什么含义? 为什么 find_all("p", "title") 返回的是CSS Class为”title”的<p>标签? 我们来仔细看一下 find_all() 的参数

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

简单的用法如下:

soup.find_all("title")
# [<title>The Dormouse's story</title>]
重申: 搜索 name 参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .

keyword 参数
如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性.

soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:

soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]
搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .

下面的例子在文档树中查找所有包含 id 属性的tag,无论 id 的值是什么:

soup.find_all(id=True)
# [<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>]
使用多个指定名字的参数可以同时过滤tag的多个属性:

soup.find_all(href=re.compile("elsie"), id='link1')
# [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:

data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
data_soup.find_all(data-foo="value")
# SyntaxError: keyword can't be an expression
但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:

data_soup.find_all(attrs={"data-foo": "value"})
# [<div data-foo="value">foo!</div>]
按CSS搜索
按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag:

soup.find_all("a", class_="sister")
# [<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>]
class_ 参数同样接受不同类型的 过滤器 ,字符串,正则表达式,方法或 True :

soup.find_all(class_=re.compile("itl"))
# [<p class="title"><b>The Dormouse's story</b></p>]

def has_six_characters(css_class):
    return css_class is not None and len(css_class) == 6

soup.find_all(class_=has_six_characters)
# [<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>]
tag的 class 属性是 多值属性 .按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名:

css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.find_all("p", class_="strikeout")
# [<p class="body strikeout"></p>]

css_soup.find_all("p", class_="body")
# [<p class="body strikeout"></p>]
搜索 class 属性时也可以通过CSS值完全匹配:

css_soup.find_all("p", class_="body strikeout")
# [<p class="body strikeout"></p>]
完全匹配 class 的值时,如果CSS类名的顺序与实际不符,将搜索不到结果:

soup.find_all("a", attrs={"class": "sister"})
# [<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>]
text 参数
通过 text 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, text 参数接受 字符串 , 正则表达式 , 列表, True . 看例子:

soup.find_all(text="Elsie")
# [u'Elsie']

soup.find_all(text=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

soup.find_all(text=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]

def is_the_only_string_within_a_tag(s):
    ""Return True if this string is the only child of its parent tag.""
    return (s == s.parent.string)

soup.find_all(text=is_the_only_string_within_a_tag)
# [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']
虽然 text 参数用于搜索字符串,还可以与其它参数混合使用来过滤tag.Beautiful Soup会找到 .string 方法与 text 参数值相符的tag.下面代码用来搜索内容里面包含“Elsie”的<a>标签:

soup.find_all("a", text="Elsie")
# [<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>]
limit 参数
find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果.

文档树中有3个tag符合搜索条件,但结果只返回了2个,因为我们限制了返回数量:

soup.find_all("a", limit=2)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]
recursive 参数
调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .

一段简单的文档:

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>
...
是否使用 recursive 参数的搜索结果:

soup.html.find_all("title")
# [<title>The Dormouse's story</title>]

soup.html.find_all("title", recursive=False)
# []
像调用 find_all() 一样调用tag
find_all() 几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:

soup.find_all("a")
soup("a")
这两行代码也是等价的:

soup.title.find_all(text=True)
soup.title(text=True)

CSS选择器:用到的方法是 soup.select(),返回类型是 list

# css选择器
'用到的方法是 soup.select(),返回类型是 list'

print(soup.select('.sister'))
print(soup.select('p'))
print(soup.select('#link1'))
# 组合查找 意思查找p标签中id属性等于link2的标签
print(soup.select('p #link2'))
# 直接子标签查找,则使用 > 分隔
print(soup.select('head > title'))

# 属性查找 标签必须和属性在同一个节点,不能用空格
print(soup.select('a[class="sister"]'))

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

# 在使用select情况下,使用get_text()获取内容
soup = BeautifulSoup(html,'lxml')

print(soup.select('title')[0].get_text())

for tag in soup.select('a'):
print(tag.get_text())

Search

    Table of Contents