解析库之beautifulsoup,pyquery

Posted bubu99

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解析库之beautifulsoup,pyquery相关的知识,希望对你有一定的参考价值。

Beautifulsoup模块

  • Beautiful Soup 是一个可以从html或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.
  • Beautiful Soup会帮你节省数小时甚至数天的工作时间.你可能在寻找 Beautiful Soup3 的文档,Beautiful Soup 3 目前已经停止开发.
  • 官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4
  • 官网推荐使用lxml作为解析器,因为效率更高.
  • 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
  • 中文文档点击
解析器使用方法优势劣势
Python标准库 BeautifulSoup(markup, "html.parser")
  • Python的内置标准库
  • 执行速度适中
  • 文档容错能力强
  • Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, "lxml")
  • 速度快
  • 文档容错能力强
  • 需要安装C语言库
lxml XML 解析器

BeautifulSoup(markup, ["lxml", "xml"])

BeautifulSoup(markup, "xml")

  • 速度快
  • 唯一支持XML的解析器
  • 需要安装C语言库
html5lib BeautifulSoup(markup, "html5lib")
  • 最好的容错性
  • 以浏览器的方式解析文档
  • 生成HTML5格式的文档
  • 速度慢
  • 不依赖外部扩展
技术图片
#安装 Beautiful Soup
pip install beautifulsoup4

#安装解析器
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml .根据操作系统不同,可以选择下列方法来安装lxml:

$ apt-get install Python-lxml

$ easy_install lxml

$ pip install lxml

另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:

$ apt-get install Python-html5lib

$ easy_install html5lib

$ pip install html5lib
安装lxml
技术图片
html_doc = """
<html><head><title>The Dormouse‘s story</title></head>
<body>
<p class="title"><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>
"""

#基本使用:容错处理,文档的容错能力指的是在html代码不完整的情况下,使用该模块可以识别该错误。使用BeautifulSoup解析上述代码,能够得到一个 BeautifulSoup 的对象,并能按照标准的缩进格式的结构输出
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,lxml) #具有容错功能
res=soup.prettify() #处理好缩进,结构化显示
print(res)
基本使用
技术图片
"""
#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
#1、用法
#2、获取标签的名称
#3、获取标签的属性
#4、获取标签的内容
#5、嵌套选择
#6、子节点、子孙节点
#7、父节点、祖先节点
#8、兄弟节点
"""
#遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
html_doc = """
<html><head><title>The Dormouse‘s story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">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>
"""

#1、用法
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,lxml)
# soup=BeautifulSoup(open(‘a.html‘),‘lxml‘)

print(soup.p) #存在多个相同的标签则只返回第一个
print(soup.a) #存在多个相同的标签则只返回第一个

#2、获取标签的名称
print(soup.p.name)

#3、获取标签的属性
print(soup.p.attrs)

#4、获取标签的内容
print(soup.p.string) # p下的文本只有一个时,取到,否则为None
print(soup.p.strings) #拿到一个生成器对象, 取到p下所有的文本内容
print(soup.p.text) #取到p下所有的文本内容
for line in soup.stripped_strings: #去掉空白
    print(line)


‘‘‘
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None,如果只有一个子节点那么就输出该子节点的文本,比如下面的这种结构,soup.p.string 返回为None,但soup.p.strings就可以找到所有文本
<p id=‘list-1‘>
    哈哈哈哈
    <a class=‘sss‘>
        <span>
            <h1>aaaa</h1>
        </span>
    </a>
    <b>bbbbb</b>
</p>
‘‘‘

#5、嵌套选择
print(soup.head.title.string)
print(soup.body.a.string)


#6、子节点、子孙节点
print(soup.p.contents) #p下所有子节点
print(soup.p.children) #得到一个迭代器,包含p下所有子节点

for i,child in enumerate(soup.p.children):
    print(i,child)

print(soup.p.descendants) #获取子孙节点,p下所有的标签都会选择出来
for i,child in enumerate(soup.p.descendants):
    print(i,child)

#7、父节点、祖先节点
print(soup.a.parent) #获取a标签的父节点
print(soup.a.parents) #找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...


#8、兄弟节点
print(=====>)
print(soup.a.next_sibling) #下一个兄弟
print(soup.a.previous_sibling) #上一个兄弟

print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
print(soup.a.previous_siblings) #上面的兄弟们=>生成器对象
遍历文档树

 

一、搜索文档树

1、五种过滤器

技术图片
#搜索文档树:BeautifulSoup定义了很多搜索方法,这里着重介绍2个: find() 和 find_all() .其它方法的参数和用法类似
html_doc = """
<html><head><title>The Dormouse‘s story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">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>
"""


from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,lxml)

#1、五种过滤器: 字符串、正则表达式、列表、True、方法
#1.1、字符串:即标签名
print(soup.find_all(b))

#1.2、正则表达式
import re
print(soup.find_all(re.compile(^b))) #找出b开头的标签,结果有body和b标签

#1.3、列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
print(soup.find_all([a,b]))

#1.4、True:可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print(soup.find_all(True))
for tag in soup.find_all(True):
    print(tag.name)

#1.5、方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag):
    return tag.has_attr(class) and not tag.has_attr(id)

print(soup.find_all(has_class_but_no_id))
View Code

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

技术图片
#2、find_all( name , attrs , recursive , text , **kwargs )
#2.1、name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
print(soup.find_all(name=re.compile(^t)))

#2.2、keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
print(soup.find_all(id=re.compile(my)))
print(soup.find_all(href=re.compile(lacie),id=re.compile(d))) #注意类要用class_
print(soup.find_all(id=True)) #查找有id属性的标签

# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup(<div data-foo="value">foo!</div>,lxml)
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can‘t be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
print(data_soup.find_all(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

#2.3、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all(a,class_=sister)) #查找类为sister的a标签
print(soup.find_all(a,class_=sister ssss)) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile(^sis))) #查找类为sister的所有标签

#2.4、attrs
print(soup.find_all(p,attrs={class:story}))

#2.5、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text=Elsie))
print(soup.find_all(a,text=Elsie))

#2.6、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
print(soup.find_all(a,limit=2))

#2.7、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False .
print(soup.html.find_all(a))
print(soup.html.find_all(a,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)
‘‘‘
View Code

3、find( name , attrs , recursive , text , **kwargs )

技术图片 View Code

4、其他方法

技术图片
#见官网:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find-parents-find-parent
View Code

5、CSS选择器

技术图片 View Code

二、修改文档树

三、总结

  • 推荐使用lxml解析库
  • 讲了三种选择器:标签选择器,find与find_all,css选择器
    1. 标签选择器筛选功能弱,但是速度快
    2. 建议使用find,find_all查询匹配单个结果或者多个结果
    3. 如果对css选择器非常熟悉建议使用select
  • 记住常用的获取属性attrs和文本值get_text()的方法

pyquery模块

技术图片
‘‘‘
强大而又灵活的网页解析库,如果你觉得正则写起来太麻烦,如果你觉得beutifulsoup
语法太难记,如果你熟悉jquery的语法,那么pyquery是最佳选择


安装pyquery
pip3 install pyquery
‘‘‘

html=‘‘‘
</div><div class="account-signin">
    <ul class="navigation menu" aria-label="Social Media Navigation">
        哈哈哈
        <li class="tier-1 last" aria-haspopup="true">

            <a href="/accounts/login/" title="Sign Up or Sign In to Python.org">Sign In</a>
            <ul class="subnav menu">
                <li class="tier-2 element-1" role="treeitem"><a href="/accounts/signup/">Sign Up / Register</a></li>
                <li class="tier-2 element-2" role="treeitem"><a href="/accounts/login/">Sign In</a></li>
            </ul>

        </li>
    </ul>
</div>
‘‘‘


#用法:

#1===========>初始化
#===>字符串初始化
# from pyquery import PyQuery as pq
# doc=pq(html)
# print(doc(‘.tier-2‘)) #默认就是css选择器

#===>url初始化
# from pyquery import PyQuery as pq
# doc=pq(url=‘http://www.baidu.com‘)
# print(doc(‘head‘))

#===>文件初始化
# from pyquery import PyQuery as pq
# doc=pq(filename=‘demo.html‘)
# print(doc(‘li‘))


#2===========>基本css选择器
from pyquery import PyQuery as pq
doc=pq(html)
# print(doc(‘.tier-2‘)) #默认就是css选择器

#查找元素

#子元素
# print(doc(‘li‘).find(‘li‘)) #这里的find是查找所有,但是不一定是直接子元素
# print(‘==>‘,doc(‘li‘).children(‘li‘)) #查找直接子元素


#父元素
# print(doc(‘.tier-2‘).parent())

#祖先元素:爹,爹的爹
# print(doc(‘.tier-2‘).parents())
# print(doc(‘.tier-2‘).parents(‘.account-signin‘)) #从祖先里筛选

#先补充:并列选择
# print(doc(‘.tier-1 .tier-2‘))
# print(doc(‘.tier-1 .tier-2.element-1‘))

#兄弟元素
# print(doc(‘.tier-2.element-1‘).siblings())
# print(doc(‘.tier-2.element-1‘).siblings(‘li a‘))







#3===========>遍历

# lis=doc(‘li‘).items()
# print(lis)
#
# for i,j in enumerate(lis):
#     print(i,j)

#4===========>获取属性
# print(doc(‘li‘).attr(‘class‘))
# print(doc(‘a‘).attr.href)


# 5===========>获取文本
# print(doc(‘a‘).text())

#6===========>获取html
# print(doc(‘.subnav.menu‘))
# print(doc(‘.subnav.menu‘).html())


#7===========>DOM
#addclass,removeclass
# tag=doc(‘.subnav.menu‘)
# print(tag)
#
# tag.addClass(‘active‘)
# print(tag)
#
# tag.removeClass(‘active‘)
# print(tag)


# tag=doc(‘.tier-2.element-1 a‘)
# tag.attr(‘name‘,‘link‘)
# tag.css(‘font-size‘,‘14px‘)
# print(tag)


tag=doc(.navigation.menu)
# print(tag.text()) #获取的是tag下所有的文本,

tag.find(li).remove()
print(tag.text()) #如果指向获取url下的那个"哈哈哈",则需要先删除li

#8===========>pyquery官网


# http://pyquery.readthedocs.io/en.latest/api.html


#9===========>伪类选择器

print(doc(li:first-child)) #选择li标签的第一个
print(doc(li:last-child)) #选择li标签的最后一个
print(doc(li:nth-child(2))) #选择li标签的第2个
print(doc(li:gt(2))) #选择li标签第2个以后的
print(doc(li:nth-child(2n))) #选择li标签的偶数标签
print(doc(li:nth-child(2n+1))) #选择li标签的奇数标签
print(doc(li:contains(second))) #选择li标签中包含second文本的标签

#更多css选择器可以查看
# http://www.w3school.com.cn/css/index.asp

#官网:http://pyquery.readthedocs.io/
View Code

以上是关于解析库之beautifulsoup,pyquery的主要内容,如果未能解决你的问题,请参考以下文章

爬虫知识总结

爬虫系列

Python3 BeautifulSoup和Pyquery解析库随笔

解析库之beautifulsoup模块

Python爬虫解析库之beautifulsoup

解析库之 beautifulsoup模块