1.Beautiful Soup 是一个HTML/XML 的解析器,主要用于解析和提取 HTML/XML 数据。
2.它基于HTML DOM 的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。
3. BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
4.虽然说BeautifulSoup4 简单容易比较上手,但是匹配效率还是远远不如正则以及xpath的,一般不推荐使用,推荐正则的使用。
Beautiful Soup库是解析、遍历、维护“标签树”的功能库,也叫beautifulsoup4 或 bs4。约定引用方式如下,即主要是用BeautifulSoup类。BeautifulSoup对应一个HTML/XML文档的全部内容。
from bs4 import BeautifulSoup # 导入
import bs4
soup = BeautifulSoup('data', 'html.parser' ) # 创建Beautiful Soup对象
bs4库将任何读入的HTML文件或字符串都转换成为’utf-8’编码(国际通用的标准编码格式,能够很好的支持中文等第三国语言),由于python3系列默认支持编码是’utf-8’,因此在做相关解析时使用bs库没有任何障碍。若用的python2解析器,则要无穷无尽的做相应的转换。
# bs4库编码的小例子
soup = BeautifulSoup("中文
","html.parser")
soup.p.string
print(soup.p.prettify())
soup = BeautifulSoup(‘data’, ‘html.parser’)
解析器 | 使用方法 | 条件 | 特点 |
---|---|---|---|
bs4 HTML解析器 | BeautifulSoup(mk,‘html.parser’) | 安装bs4库 | Python的内置标准库,执行速度适中,文档容错能力强 |
lxml HTML解析器 | BeautifulSoup(mk,‘lxml’) | pip install lxml | 速度快,文档容错能力强 |
lxml XML解析器 | BeautifulSoup(mk,‘xml’) | pip install lxml | 速度快,唯一支持XML的解析器 |
html5lib解析器 | BeautifulSoup(mk,‘html5lib’) | pip install html5lib | 最好的容错性,以浏览器的方式解析文档,生成HTML5格式的文档,速度慢 |
Beautiful Soup将复杂HTNL文档转换成一个复杂的树形结构,每个节点都是python对象,所有对象可以分为以下5种:
…
的名字是’p’,格式:
# 导入bs4库
from bs4 import BeautifulSoup
import requests
r = requests.get('https://python123.io/ws/demo.html') # 抓取页面(Demo网址)
demo = r.text # 抓取的数据(网页源码)
demo
# 解析HTML页面
soup = BeautifulSoup(demo, 'html.parser') # bs4的解析器,解析页面数据(网页源码)
# 有层次感的输出解析后的HTML页面
print(soup.prettify()) # prettify()为HTML文本<>及其内容增加更加'\n',有层次感的输出
# prettify()可用于标签,方法:.prettify()
soup.a # 访问标签a
>> <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a>
soup.title # 访问标签title
>> <title>This is a python demo page</title>
2)标签的name(名字):
soup.a.name
>> 'a'
soup.a.parent.name
>> 'p'
soup.p.parent.name
>> 'body'
tag = soup.a
print(tag.attrs)
print(tag.attrs['class'])
print(type(tag.attrs))
>> {'href': 'http://www.icourse163.org/course/BIT-268001', 'class': ['py1'], 'id': 'link1'}
>> ['py1']
>> <class 'dict'>
print(soup.a.string)
print(type(soup.a.string))
>> Basic Python
>> <class 'bs4.element.NavigableString'>
5)标签的Comment:
newsoup = BeautifulSoup("This is not a comment
","html.parser")
newsoup.b.string
>> 'This is a comment'
type(newsoup.b.string)
>> bs4.element.Comment
newsoup.p.string
>> 'This is not a comment'
type(newsoup.p.string)
>> bs4.element.NavigableString
HTML基本格式:<>…>构成了所属关系,形成了标签的树形结构
1)标签树的下行遍历
属性 | 说明 |
---|---|
.contents | 子节点的列表,将所有儿子节点存 |
.children | 子节点的迭代类型,与.contents类似,用于循环遍历儿子节点 |
.descendants | 子孙节点的迭代类型,包含所有子孙节点,用于循环遍历 |
import requests
from bs4 import BeautifulSoup
r=requests.get('http://python123.io/ws/demo.html')
demo=r.text
soup=BeautifulSoup(demo,'html.parser')
print(soup.prettify())
print(soup.contents)# 获取整个标签树的儿子节点
print(soup.body.content)#返回标签树的body标签下的节点
print(soup.head)#返回head标签
print(soup.head.contents)#返回head标签的儿子节点
for child in soup.body.children:#遍历儿子节点
print(child)
for child in soup.body.descendants:#遍历子孙节点
print(child)
属性 | 说明 |
---|---|
.parent | 节点的父亲标签 |
.parents | 节点先辈标签的迭代类型,用于循环遍历先辈节点 |
soup.title.parent
soup.html.parent
for parent in soup.a.parents: # 遍历所有先辈节点,包括soup本身,所以要区别判断
if parent is None:
print(parent)
else:
print(parent.name)
属性 | 说明 |
---|---|
.next_sibling | 返回按照HTML文本顺序的下一个平行节点标签 |
.previous_sibling | 返回按照HTML文本顺序的上一个平行节点标签 |
.next_siblings | 迭代类型,返回按照HTML文本顺序的后续所有平行节点标签 |
.previous_siblings | 迭代类型,返回按照HTML文本顺序的前续所有平行节点标签 |
print(soup.a.next_sibling)#a标签的下一个标签
print(soup.a.next_sibling.next_sibling)#a标签的下一个标签的下一个标签
print(soup.a.previous_sibling)#a标签的前一个标签
print(soup.a.previous_sibling.previous_sibling)#a标签的前一个标签的前一个标签
for sibling in soup.a.next_siblings:#遍历后续节点
print(sibling)
# name : 对标签名称的检索字符串
soup.find_all('a')
soup.find_all(['a', 'p'])
# attrs: 对标签属性值的检索字符串,可标注属性检索
soup.find_all("p","course")
soup.find_all(id="link") # 完全匹配才能匹配到
>> []
# recursive: 是否对子孙全部检索,默认True
soup.find_all('p',recursive=False)
>> []
# string: <>…>中字符串区域的检索字符串
soup.find_all(string = "Basic Python") # 完全匹配才能匹配到
>> ['Basic Python']
采用requests-bs4路线实现了中国大学排名定向爬虫
对中英文混排输出问题进行了优化
爬取url:http://www.zuihaodaxue.cn/zuihaodaxuepaiming2019.html
爬取思路:
1.从网络上获取大学排名网页内容
2.提取网页内容中信息到合适的数据结构(二维数组)-排名,学校名称,总分
3.利用数据结构展示并输出结果
if isinstance(tr,bs4.element.Tag):
1.isinstance:判断一个对象是否是一个已知的类型,类似于type()
type()不考虑子类是父类的一种类型,不考虑继承关系
isinatance()认为子类是父类的一种类型,考虑继承关系
2.bs4.element.Tag
bs4是包,element是模块,Tag是类名
tag是‘bs4.element.Tag’的实例对象,或者说tag的数据类型是bs4.element.Tag
# 导入库
import requests
from bs4 import BeautifulSoup
import bs4
def getHTMLText(url):
try:
r = requests.get(url,timeout=30) # 获取url信息,并且设置时间
r.raise_for_status() # 产生异常信息
r.encoding=r.apparent_encoding # 修改编码
return r.text # 将网页的信息内容返回给程序的其他部分
except:
return ""
def fillUnivList(ulist,html):# 提取HTML中关键的数据并且添加一个列表中
soup = BeautifulSoup(html,"html.parser")
for tr in soup.find("tbody").children: # 解析HTML代码中的tbody标签所在的位置,在tbody标签中找到每一所大学对应的tr标签
if isinstance(tr,bs4.element.Tag): #检测tr标签的类型,如果类型不是bs4库定义的Tag类型则过滤掉
tds=tr('td') # 对tr标签中的td标签做查询,将所有的td标签存为一个列表类型tds
ulist.append([tds[0].string,tds[1].string,tds[3].string]) # 在ulist中增加我们需要的对应字段(大学排名,大学名称,大学得分)
def printUnivList(ulist,num):
tplt = "{0:^10}\t{1:{3}^10}\t{2:^10}" # 生成一个输出模板的变量,主要增加中间的宽度设定,将变量的输出按照顺序来表示。
# 把字符串宽度都定义为10,但是中文本身的宽度都不到10所以会填充西文空格(字符),就会导致字符的实际宽度长短不一,所以需要用chr(12288)增加中文空格的变量位置
# 在1号位填充{3}的原因是:中英文全半角造成不对齐的原因产生在1号位;
# {3}指的是我们需要对format函数的三个变量(排名,学校名称,总分)进行填充(也就是中文字符的空格填充)
print(tplt.format("排名","学校名称","总分",chr(12288))) # 实现对表头的打印,用chr(12288)增加中文空格的变量位置
for i in range(num):
u=ulist[i] # 将第i个学校信息用一个简短的变量u来代替
print(tplt.format(u[0],u[1],u[2],chr(12288))) # 将每一所学校信息打印出来。(注:为了保证输出效果,需要用跟表头相一致的字符串表示)
def main():
uinfo=[]
url='http://www.zuihaodaxue.com/zuihaodaxuepaiming2019.html'
html=getHTMLText(url)
fillUnivList(uinfo,html)
printUnivList(uinfo,30) # 30 univs
main()
若输出再添加一个“省市”变量,则fillUnivList函数和printUnivList函数里面的内容应做相应的改变,如下所示:
def fillUnivList(ulist,html):
soup = BeautifulSoup(html,"html.parser")
for tr in soup.find("tbody").children:
if isinstance(tr,bs4.element.Tag):
tds=tr('td')
ulist.append([tds[0].string,tds[1].string,tds[3].string,tds[2].string])
# 在ulist中增加我们需要的对应字段(大学排名,大学名称,总分,省份)
def printUnivList(ulist,num):
tplt = "{0:^10}\t{1:{4}^10}\t{2:^10}\t{3:^10}"
# 添加了地区(省市),相应地作为填充不足10个字符长度的chr(12288)已经不是3了,而是4。所以,在1号位上应填充{4}
print(tplt.format("排名","学校名称","总分","省市",chr(12288)))
for i in range(num):
u=ulist[i]
print(tplt.format(u[0],u[1],u[2],u[3],chr(12288)))
采用.format打印输出时,可以定义输出字符串的输出宽度,在 ‘:’ 后传入一个整数, 可以保证该域至少有这么多的宽度。 用于美化表格时很有用。
但是在打印多组中文的时候,不是每组中文的字符串宽度都一样,当中文字符宽度不够的时候,程序默认采用西文空格(字符)填充,中西文空格宽度不一样,就会导致输出文本不整齐。
所以,中文对齐问题的解决方法是:采用中文字符的空格填充chr(12288)