Beautiful Soup功能介绍
简介
Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间.
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。
支持的解析器
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是lxml .根据操作系统不同,可以选择下列方法来安装lxml
解析器
使用方法 | 优势 | 劣势 | |
---|---|---|---|
Python标准库 | BeautifulSoup(markup,"html.parser") |
|
|
lxml HTML 解析器 | BeautifulSoup(markup,"lxml") |
|
|
lxml XML 解析器 |
|
|
|
html5lib | BeautifulSoup(markup,"html5lib") |
|
|
from bs4 import BeautifulSoup soup = BeautifulSoup(open("index.html")) soup = BeautifulSoup("data") print BeautifulSoup("Hello World!") 执行结果:Hello World!
解析的对象种类
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:Tag
,NavigableString
, BeautifulSoup
,Comment
<1>Tag
Tag
对象与XML或HTML原生文档中的tag相同,
soup = BeautifulSoup('Extremely bold') tag = soup.b type(tag) #(1)tag中最重要的属性: name和attributes
每个tag都有自己的名字,通过.name
来获取,一个tag可能有很多个属性. tagclass="boldest">
有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同,如下
getsoup=BeautifulSoup("Hello World!
")
tag=getsoup.p
print tag
print tag.name
print tag['class']
----结果----
Hello World!
body
['test']
(2) 可以遍历的字符串
字符串常被包含在tag内.Beautiful Soup用NavigableString
类来包装tag中的字符串:
print tag.string-----------Hello World!
(3)注释及特殊字符串
Tag
,NavigableString
,BeautifulSoup
几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分:
markup = ""
soup = BeautifulSoup(markup)
comment = soup.b.string
print type(comment)
print comment
Hey, buddy. Want to buy a used parser?
(4)遍历文档树
子节点循环遍历:
如果想要得到所有的标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到Searching the tree 中描述的方法,比如: find_all()
print soup.find_all('a')
1).contents 和 .children
tag的.contents
属性可以将tag的子节点以列表的方式输出
通过tag的 .children 生成器,可以对tag的子节点进行循环
2).descendants 属性可以对所有tag的子孙节点进行递归循环 [5]
3).strings 和 stripped_strings
如果tag中包含多个字符串 [2] ,可以使用 .strings 来循环获取:
输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容
父节点
1).parent
通过 .parent 属性来获取某个元素的父节点.在例子“爱丽丝”的文档中,标签是
2).parents
通过元素的 .parents 属性可以递归得到元素的所有父辈节点,下面的例子使用了 .parents 方法遍历了标签到根节点的所有节点.
兄弟节点
1).next_sibling 和 .previous_sibling
在文档树中,使用 .next_sibling 和 .previous_sibling 属性来查询兄弟节点
2).next_siblings 和 .previous_siblings
通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出
回退或前进
1).next_element 和 .previous_element
.next_element 属性指向解析过程中下一个被解析的对象(字符串或tag),结果可能与 .next_sibling 相同,但通常是不一样的.
2).next_elements 和 .previous_elements
通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样:
(5)搜索文档树
过滤器----find_all()
1)字符串
最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签:
soup.find_all('b')
2)正则表达式
如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示和标签都应该被找到
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
3)列表
如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签:
soup.find_all(["a", "b"])
4)True
True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
for tag in soup.find_all(True):
print(tag.name)
5)方法 标签: The Dormouse's story Once upon a time there were... ...
如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 [4] ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True:
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
将这个方法作为参数传入 find_all() 方法,将得到所有
soup.find_all(has_class_but_no_id)
# [
#
#
6)name 参数
name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉.
简单的用法如下:
soup.find_all("title")
# [
重申: 搜索 name 参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
keyword 参数
如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性.
soup.find_all(id='link2')
# [Lacie]
如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:
soup.find_all(href=re.compile("elsie"))
# [Elsie]
搜索指定名字的属性时可以使用的参数值包括 字符串 , 正则表达式 , 列表, True .
其它详见:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/
Beautiful Soup抓取‘谣言百科’的分类内容
源代码:
# -*- coding:UTF-8 -*-
# 2017年7月15日
# 爬虫目标网站:http://www.yaoyanbaike.com/
# 获取信息BeautifulSoup+request
import urllib2
from bs4 import BeautifulSoup
import re
import codecs
if __name__ == "__main__":
text_file_number = 0 # 同一类新闻下的索引数
number = 1 # 同类别新闻不同页面下的索引数
while (number <= 2):
if number==1: # 第一个新闻下地址是baby不是baby_数字所以要区分判断一下
get_url = 'http://www.yaoyanbaike.com/category/baby.html'
else:
get_url = 'http://www.yaoyanbaike.com/category/baby_'+str(number)+'.html' #这个是baby_数字,number就是目录索引数
head = {} #设置头
head['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0'
# 模拟浏览器模式,定制请求头
download_req_get = urllib2.Request(url = get_url, headers = head)
# 设置Request
download_response_get = urllib2.urlopen(download_req_get)
# 设置urlopen获取页面所有内容
download_html_get = download_response_get.read().decode('UTF-8','ignore')
# UTF-8模式读取获取的页面信息标签和内容
soup_texts = BeautifulSoup(download_html_get, 'lxml')
# BeautifulSoup读取页面html标签和内容的信息
for link in soup_texts.find_all(["a"]):
print(str(text_file_number)+" "+str(number)+" "+str(link.get('title')))
# 打印文件地址用于测试
s=link.get('href')
if s.find("/a/") == -1:
print("错误网址") # 只有包含"/a/"字符的才是有新闻的有效地址
else:
download_url = link.get('href')
head = {}
head['User-Agent'] = 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19'
download_req = urllib2.Request(url = "http://www.yaoyanbaike.com"+download_url, headers = head)
download_response = urllib2.urlopen(download_req)
download_html = download_response.read().decode('UTF-8','ignore')
soup_texts = BeautifulSoup(download_html, 'lxml')
texts = soup_texts.find_all('article')
soup_text = BeautifulSoup(str(texts), 'lxml')
p = re.compile("<[^>]+>")
text=p.sub("", str(soup_text))
# 去除页面标签
f1 = codecs.open('E:\pic\\'+str(text_file_number)+'--'+str(link.get('title')).decode('utf-8')+'.txt','w','UTF-8')
# f1 = codecs.open('E:\pic\\'+str(text_file_number)+'.txt','w','UTF-8')
# 排序id+按标题名称将信息存储在本地
f1.write(text)
f1.close()
text_file_number = text_file_number + 1
number = number + 1
执行结果: