Install BS4
/usr/bin/pip3 install BeautifulSoup4
/usr/bin/pip3 install lxml
Parse XML
#!/usr/bin/python3
/usr/bin/python3
# 将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("<html>data</html>")
#我这次要解析的是XML文档
soup = BeautifulSoup(open("test.xml"))
#Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment
Basic knowledge
tag.name # 每个tag都有自己的名字,通过 .name 来获取
tag.name = "blockquote" # 如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档
tag['class'] # 一个tag可能有很多个属性,tag的属性的操作方法与字典相同
tag.attrs # 也可以直接”点”取属性, 比如: .attrs,将会返回键值对
#tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样
#字符串常被包含在tag内.Beautiful Soup用 NavigableString 类来包装tag中的字符串
tag.string
#一个 NavigableString 字符串与Python中的Unicode字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性.
# 通过 unicode() 方法可以直接将 NavigableString 对象转换成Unicode字符串
# tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法
tag.string.replace_with("No longer bold")
#其实还有append 、 insert 、clear等方法,这里没有提及
# 如果想在Beautiful Soup之外使用 NavigableString 对象,需要调用 unicode() 方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存
# 一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.
# 注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点
soup.head #操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取 <head> 标签
soup.body.b # ,可以在文档树的tag中多次调用这个方法.下面的代码可以获取<body>标签中的第一个<b>标签
# 通过点取属性的方式只能获得当前名字的第一个tag
soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
soup.find_all('a') #获取所有a tag,返回一个list
# tag的 .contents 属性可以将tag的子节点以列表的方式输出
# 通过tag的 .children 生成器,可以对tag的子节点进行循环
for child in title_tag.children:
print(child)
# The Dormouse's story
# .contents 和 .children 属性仅包含tag的直接子节点 **************
# .descendants 属性可以对所有tag的子孙节点进行递归循环
for child in head_tag.descendants:
print(child)
# 如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点
tag.string
# 如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同
# 如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None
# 如果tag中包含多个字符串,可以使用 .strings 来循环获取
for string in soup.strings:
print(repr(string))
#输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容
for string in soup.stripped_strings:
print(repr(string))
# 通过 .parent 属性来获取某个元素的父节点.在例子“爱丽丝”的文档中,<head>标签是<title>标签的父节点
title_tag.parent
# 通过元素的 .parents 属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents 方法遍历了<a>标签到根节点的所有节点
link = soup.a
link
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
# 在文档树中,使用 .next_sibling 和 .previous_sibling 属性来查询兄弟节点
sibling_soup.b.next_sibling
sibling_soup.c.previous_sibling
# 通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出
# .next_element 属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 .next_sibling 相同,但通常是不一样的
# .previous_element 属性刚好与 .next_element 相反,它指向当前被解析的对象的前一个解析对象
# 通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样
搜索文档树
过滤器
#最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的<b>标签
soup.find_all('b')
#如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容
#下面例子中找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
for tag in soup.find_all(re.compile("t")):
print(tag.name)
#如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签
soup.find_all(["a", "b"])
#True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
...
find_all()
#find_all( name , attrs , recursive , string , **kwargs )
#find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件
soup.find_all("title") #tag.name == title
soup.find_all("p", "title") #tag.name == p and tag.attrs == title
soup.find_all(id="link2") # 如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索
data_soup.find_all(attrs={"data-foo": "value"}) #通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag
#name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉
#搜索 name 参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True
#如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索
#搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True
#按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字 class 在Python中是保留字,使用 class 做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag
soup.find_all("a", class_="sister")
#class_ 参数同样接受不同类型的 过滤器 ,字符串,正则表达式,方法或 True :
soup.find_all(class_=re.compile("itl"))
#通过 string 参数可以搜搜文档中的字符串内容
#string 参数接受 字符串 , 正则表达式 , 列表, True
soup.find_all(string="Elsie")
soup.find_all(string=re.compile("Dormouse"))
soup.find_all(string=["Tillie", "Elsie", "Lacie"])
soup.find_all("a", string="Elsie")
#find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量
soup.find_all("a", limit=2)
#调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False
soup.html.find_all("title", recursive=False)
#记住: find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等
# find_parents() 和 find_parent()
# find_next_siblings() 和 find_next_sibling()
# find_previous_siblings() 和 find_previous_sibling()
# find_all_next() 和 find_next()
# find_all_previous() 和 find_previous()
输出
soup.prettify()
#prettify() 方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行
#如果只想得到结果字符串,不重视格式,那么可以对一个 BeautifulSoup 对象或 Tag 对象使用Python的 unicode() 或 str() 方法
#如果想在Beautiful Soup之外使用 NavigableString 对象,需要调用 unicode() 方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存
get_text()
#如果只想得到tag中包含的文本内容,那么可以调用 get_text() 方法,这个方法获取到tag中包含的所有文版内容包括子孙tag中的内容,并将结果作为Unicode字符串返回
soup.get_text()
soup.get_text("|") # 可以通过参数指定tag的文本内容的分隔符
soup.get_text("|", strip=True) #还可以去除获得文本内容的前后空白及特殊字符
#或者使用 .stripped_strings 生成器,获得文本列表后手动处理列表
[text for text in soup.stripped_strings]
# [u'I linked to', u'example.com']