BeautifulSoup是一个模块,该模块用于接收一个HTML或XML字符串,然后将其进行格式化,之后遍可以使用他提供的方法进行快速查找指定元素,从而使得在HTML或XML中查找指定元素变得简单。
官方文档:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/#id13
安装
pip install beautifulsoup4
使用示例
from bs4 import BeautifulSoup html_doc = """ <html><head><title>The Dormouse's story</title></head> <body> asdf <div class="title"> <b>The Dormouse's story总共</b> <h1>f</h1> </div> <div class="story">Once upon a time there were three little sisters; and their names were <a class="sister0" id="link1">Els<span>f</span>ie</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.</div> ad<br/>sf <p class="story">...</p> </body> </html> """ soup = BeautifulSoup(html_doc, features="lxml") # 第一个参数是网页内容的字符串形式,第二个参数是用来选择解析库 # 找到第一个a标签 tag1 = soup.find(name='a') # 找到所有的a标签 tag2 = soup.find_all(name='a') # 找到id=link2的标签 tag3 = soup.select('#link2')
使用
将一段文档传入BeautifulSoup的构造方法,就能得到一个文档的对象,可以传入一段字符串或一个文件句柄。
from bs4 import BeautifulSoup soup = BeautifulSoup(open('index.html')) # 或者 soup = BeautifulSoup('<html> data </html>')
然后文档被转换成Unicode的,并且HTML的实例都被转换成Unicode的编码
BeautifulSoup("Sacré bleu!") <html><head></head><body>Sacré bleu!</body></html>
然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器那么Beautiful Soup会选择指定的解析器来解析文档
BeautifulSoup默认支持Python的标准HTML解析库,但是它也支持一些第三方的解析库:
注:平时我们可以使用python内置的html.parser,但是用的多的还是lxml的两个解析库,速度快,容错也高
经过解析库的解析,就会将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag
, NavigableString
, BeautifulSoup
, Comment
.
(1)Tag
Tag相当于html中的一个标签。
这里有比较重要的两个属性:
- name:标签的名字
- attrs:每个标签中的全部属性
(2)NavigableString
字符串常被包含在tag内.Beautiful Soup用 NavigableString
类来包装tag中的字符串。
tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法。
如果想在Beautiful Soup之外使用 NavigableString
对象,需要调用 unicode()
方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存。
(3)BeautifulSoup
BeautifulSoup
对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag
对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup
对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name
属性是很方便的,所以 BeautifulSoup
对象包含了一个值为 “[document]” 的特殊属性 .name
(4)Comment
Tag
, NavigableString
, BeautifulSoup
几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:
Comment
对象就是一个特殊类型的 NavigableString
对象用来记录文档的。
比如:
markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>" soup = BeautifulSoup(markup) comment = soup.b.string type(comment) # <class 'bs4.element.Comment'>
但是当它出现在HTML文档中时, Comment
对象会使用特殊的格式输出:
print(soup.b.prettify()) # <b> # <!--Hey, buddy. Want to buy a used parser?--> # </b>
常用的属性和方法
1.name:获取标签名称

tag = soup.find('a') name = tag.name # 获取 print(name) tag.name = 'span' # 设置 print(soup)
2.attr:获取标签属性,值为集合形式

# tag = soup.find('a') # attrs = tag.attrs # 获取 # print(attrs) # tag.attrs = {'ik':123} # 设置 # tag.attrs['id'] = 'iiiii' # 设置 # print(soup #另一种方式,用get获取 #soup.select('a')[0].get('href')
3.children:获取所有子标签

# body = soup.find('body') # v = body.children
4.descendants:获取所有子子孙孙的标签

# body = soup.find('body') # v = body.descendants
5.clear:将标签的所有子标签全部清空(保留标签名)

# tag = soup.find('body') # tag.clear() # print(soup)
6.decompose:递归删除所有的标签

# body = soup.find('body') # body.decompose() # print(soup)
7.extract:递归删除所有的标签,并获取删除的标签

# body = soup.find('body') # v = body.extract() # print(soup)
8.decode:转换为字符串(含当前标签);decode_contents(不含当前标签)

# body = soup.find('body') # v = body.decode() # v = body.decode_contents() # print(v)
9.encode:转换为字节(含当前标签);encode_contents(不包含当前标签)

# body = soup.find('body') # v = body.encode() # v = body.encode_contents() # print(v)
10.find:获取匹配的第一个标签

# tag = soup.find('a') # print(tag) # tag = soup.find(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie') # tag = soup.find(name='a', class_='sister', recursive=True, text='Lacie') # print(tag)
11.find_all:获取匹配的所有标签

# tags = soup.find_all('a') # print(tags) # tags = soup.find_all('a',limit=1) # print(tags) # tags = soup.find_all(name='a', attrs={'class': 'sister'}, recursive=True, text='Lacie') # # tags = soup.find(name='a', class_='sister', recursive=True, text='Lacie') # print(tags) # ####### 列表 ####### # v = soup.find_all(name=['a','div']) # print(v) # v = soup.find_all(class_=['sister0', 'sister']) # print(v) # v = soup.find_all(text=['Tillie']) # print(v, type(v[0])) # v = soup.find_all(id=['link1','link2']) # print(v) # v = soup.find_all(href=['link1','link2']) # print(v) # ####### 正则 ####### import re # rep = re.compile('p') # rep = re.compile('^p') # v = soup.find_all(name=rep) # print(v) # rep = re.compile('sister.*') # v = soup.find_all(class_=rep) # print(v) # rep = re.compile('http://www.oldboy.com/static/.*') # v = soup.find_all(href=rep) # print(v) # ####### 方法筛选 ####### # def func(tag): # return tag.has_attr('class') and tag.has_attr('id') # v = soup.find_all(name=func) # print(v) # ## get,获取标签属性 # tag = soup.find('a') # v = tag.get('id') # print(v)
12.has_attr:检查标签是否具有该属性

# tag = soup.find('a') # v = tag.has_attr('id') # print(v)
13.get_text:获取标签内部文本内容

# tag = soup.find('a') # v = tag.get_text('id') # print(v)
14.index:检查标签在某标签中的索引位置

# tag = soup.find('body') # v = tag.index(tag.find('div')) # print(v) # tag = soup.find('body') # for i,v in enumerate(tag): # print(i,v)
15. is_empty_element,是否是空标签(是否可以是空)或者自闭合标签,
判断是否是如下标签:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame', 'base'

# tag = soup.find('br') # v = tag.is_empty_element # print(v)
16. 当前的关联标签

# soup.next # soup.next_element # soup.next_elements # soup.next_sibling # soup.next_siblings # # tag.previous # tag.previous_element # tag.previous_elements # tag.previous_sibling # tag.previous_siblings # # tag.parent # tag.parents
17. 查找某标签的关联标签

# tag.find_next(...) # tag.find_all_next(...) # tag.find_next_sibling(...) # tag.find_next_siblings(...) # tag.find_previous(...) # tag.find_all_previous(...) # tag.find_previous_sibling(...) # tag.find_previous_siblings(...) # tag.find_parent(...) # tag.find_parents(...) # 参数同find_all
18.select,select_one, CSS选择器

soup.select("title") soup.select("p nth-of-type(3)") soup.select("body a") soup.select("html head title") tag = soup.select("span,a") soup.select("head > title") soup.select("p > a") soup.select("p > a:nth-of-type(2)") soup.select("p > #link1") soup.select("body > a") soup.select("#link1 ~ .sister") soup.select("#link1 + .sister") soup.select(".sister") soup.select("[class~=sister]") soup.select("#link1") soup.select("a#link2") soup.select('a[href]') soup.select('a[href="http://example.com/elsie"]') soup.select('a[href^="http://example.com/"]') soup.select('a[href$="tillie"]') soup.select('a[href*=".com/el"]') from bs4.element import Tag def default_candidate_generator(tag): for child in tag.descendants: if not isinstance(child, Tag): continue if not child.has_attr('href'): continue yield child tags = soup.find('body').select("a", _candidate_generator=default_candidate_generator) print(type(tags), tags) from bs4.element import Tag def default_candidate_generator(tag): for child in tag.descendants: if not isinstance(child, Tag): continue if not child.has_attr('href'): continue yield child tags = soup.find('body').select("a", _candidate_generator=default_candidate_generator, limit=1) print(type(tags), tags)
19. 标签的内容

# tag = soup.find('span') # print(tag.string) # 获取 # tag.string = 'new content' # 设置 # print(soup) # tag = soup.find('body') # print(tag.string) # tag.string = 'xxx' # print(soup) # tag = soup.find('body') # v = tag.stripped_strings # 递归内部获取所有标签的文本 # print(v)
20.append在当前标签内部追加一个标签

# tag = soup.find('body') # tag.append(soup.find('a')) # print(soup) # # from bs4.element import Tag # obj = Tag(name='i',attrs={'id': 'it'}) # obj.string = '我是一个新来的' # tag = soup.find('body') # tag.append(obj) # print(soup)
21.insert在当前标签内部指定位置插入一个标签

# from bs4.element import Tag # obj = Tag(name='i', attrs={'id': 'it'}) # obj.string = '我是一个新来的' # tag = soup.find('body') # tag.insert(2, obj) # print(soup)
22. insert_after,insert_before 在当前标签后面或前面插入

# from bs4.element import Tag # obj = Tag(name='i', attrs={'id': 'it'}) # obj.string = '我是一个新来的' # tag = soup.find('body') # # tag.insert_before(obj) # tag.insert_after(obj) # print(soup)
23. replace_with 在当前标签替换为指定标签

# from bs4.element import Tag # obj = Tag(name='i', attrs={'id': 'it'}) # obj.string = '我是一个新来的' # tag = soup.find('div') # tag.replace_with(obj) # print(soup)
24. 创建标签之间的关系

# tag = soup.find('div') # a = soup.find('a') # tag.setup(previous_sibling=a) # print(tag.previous_sibling)
25. wrap,将指定标签把当前标签包裹起来

# from bs4.element import Tag # obj1 = Tag(name='div', attrs={'id': 'it'}) # obj1.string = '我是一个新来的' # # tag = soup.find('a') # v = tag.wrap(obj1) # print(soup) # tag = soup.find('a') # v = tag.wrap(soup.find('p')) # print(soup)
26. unwrap,去掉当前标签,将保留其包裹的标签

# tag = soup.find('a') # v = tag.unwrap() # print(soup)
小例子

from bs4.element import Tag tags = soup.find("body").children for tag in tags: if type(tag) == Tag: print(tag) else: print("文本。。")