python3解析库(Beautiful Soup、pyquery、parsel)

Beautiful Soup

基本使用

<html>
<head>
    <title>
        The Document's story
    </title>
</head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three 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>
</body>
</html>
from bs4 import BeautifulSoup
# 使用lxml解析器
soup=BeautifulSoup(html,'lxml')
# 通过prettify修正html
print(soup.prettify())
//输出title的内容
print(soup.title.string)

节点选择器

from bs4 import BeautifulSoup
# 使用lxml解析器
soup=BeautifulSoup(html,'lxml')
print(soup.title)
# <title>The Document's story</title>

print(soup.title.string)
#The Document's story 

print(soup.head)
#<head><title>The Document's story</title></head>

print(soup.p)
#当有多个节点时,此方法只会选择匹配到的第一个,后面的忽略
#<p class="title" name="dromouse"><b>The Dormouse's story</b></p>

提取信息

获取名称

利用name属性可以获取节点的名称。

print(soup.title.name)

获取属性

一个节点可能有多个属性,例如id和class等,选择这个节点元素后可以调用attrs获取其所有属性:

print(soup.p.attrs) print(soup.p.attrs['name'])

{'class':['title'],'name':'dromouse'} dromouse

调用attrs属性返回结果是字典形式,包括所选择节点的所有属性和属性值。其中attrs可以省略,直接使用[],有一些属性会返回列表,有一些返回字符串,因为class可能会有多个

print(soup.p['name']) # dromouse
print(soup.p['class']) # ['title']
获取内容

print('soup.p.string')这里只会选取p节点的第一个节点

关联选择

from bs4 import BeautifulSoup

html = """
<html>
<head>
    <title>
        The Document's story
    </title>
</head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three 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>
</body>
</html>
"""

soup = BeautifulSoup(html, 'lxml')
#获取直接子元素
print("这是子节点所以内容", soup.p.contents)
# 对子节点进行遍历
for i, child in enumerate(soup.p.children):
    print("这是子节点", i, child)
print('\n')
# 获取子孙元素
for i, child in enumerate(soup.p.descendants):
    print("这是子孙节点", i, child)
print('--------------------------------------------------------')
# 获取父元素
print('这是父元素', soup.a.parent)
# 获取兄弟元素
print('这是前面的兄弟元素' , list(enumerate(soup.a.previous_siblings)))
print('这是后面的兄弟元素' , list(enumerate(soup.a.next_siblings)))

方法选择器

Beautiful Soup提供一些查询的方法

find_all

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

name

from bs4 import BeautifulSoup

html='''
<div class="panel">
	<div class="panel-heading">
			<h4>Hello</h4>
	</div>
	<div class="panel-body">
			<ul class="list" id="list-1">
					<li class="element">Foo</li>
					<li class="element">Bar</li>
					<li class="element">Jay</li>
			</ul>
			<ul class="list list-small" id="list-2">
					<li class="element">Foo</li>
					<li class="element">Bar</li>
			</ul>
	<div>
</div>
'''

soup=BeautifulSoup(html,'lxml')
print(soup.find_all(name='ul'))
''' [<ul class="list" id="list-1">
					<li class="element">Foo</li>
					<li class="element">Bar</li>
					<li class="element">Jay</li>
			</ul>
			<ul class="list list-small" id="list-2">
					<li class="element">Foo</li>
					<li class="element">Bar</li>
			</ul>]
'''
print(type(soup.find_all(name='ul')[0]))
# <class 'bs4.element.Tag>'
for ul in soup.find_all(name='ul'):
  print(ul.soup.find_all(name='li'))
  for li in ul.soup.find_all(name='li'):
    print(li.string)
"""
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
Foo
Bar
Jay
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
Foo
Bar
"""

attrs

通过属性进行查询

# id可以省略attrs属性直接使用id='list-1'
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name':'elements'}))
# class为python关键字,所以需要再class后加一个_
print(soup.find_all(class_='element'))

text

可以使用text参数来匹配文本,输入类型可以是字符串也可以是正则表达式

print(soup.find_all(text=re.compile('Foo'))) 使用text会警告 DeprecationWarning: The 'text' argument to find()-type methods is deprecated. Use 'string' instead。换成string=即可

find

除了fand_all方法,fand方法也可以查询符合条件的元素,不过find方法返回的元素是单个,是匹配的第一个元素。而fand_all会返回一个元素组成的列表。

其他方法

  1. find_parents和find_parent:前者返回所有祖先节点。后者返回父节点。
  2. find_next_siblings和find_next_sibling:前者返回后面的所有兄弟节点,后者返回后面第一个兄弟节点。
  3. find_previous_siblings和find_previous_sibling:前者返回前面的所有兄弟节点,后者返回前面第一个兄弟节点。
  4. find_all_next和find_next:前者返回节点后面所有符合条件的节点,后者返回后面第一个符合条件的节点。
  5. find_all_previous和find_previous:前者返回节点前面所有符合条件的节点,后者返回前面第一个符合条件的节点。

CSS选择器

from bs4 import BeautifulSoup


soup=BeautifulSoup(html,'lxml')
print(soup.select('ul li'))
# [<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]

print(soup.select('.panel .panel-heading'))
# [<div class="panel-heading"><h4>Hello</h4></div>]

print(soup.select('#list-2 .element'))
# [<li class="element">Foo</li>, <li class="element">Bar</li>]

# 嵌套选择
for ul in soup.select('ul'):
		print(ul.select('li'))	# 和ul ul显示结果一致

# 获取属性
for ul in soup.select('ul'):
		# 获取每个节点的id属性
  	print(ul['id'])
    print(ul.attrs["id"])
    
# 获取文本
for li in soup.select('li'):
# 除了通过string属性获取文本,还可以通过方法get_text()
  	print(li.string)
    print(li.get_text())

pyquery

基本使用

from pyquery import PyQuery as pq

html = """
<div>
    <ul>
        <li class="item-0">first item</li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
        <li class="item-1 active"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
    </ul>
</div>
"""

doc = pq(html)
print(doc('li'))

'''
<li class="item-0">first item</li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
        <li class="item-1 active"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
'''

URL初始化

访问url地址解析html

from pyquery import PyQuery as pq
import requests

doc=pq(url='hppts://www.baidu.com')
print(doc('title'))
# 显示乱码
# 等同于以下

# 使用ruquests选择编码格式
resp=requests.get('hppts://www.baidu.com')
resp.encoding="utf-8"
doc=pq(resp.text)
print(doc('title'))
# <title>百度一下,你就知道</title>

文件初始化

读取本地文件html进行解析

doc=pq(filename='demo.html')

CSS选择器

from pyquery import PyQuery as pq

html = """
<div id="container">
    <ul class="list">
        <li class="item-0">first item</li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
        <li class="item-1 active"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
    </ul>
</div>
"""

doc=pq(html)
# 打印id为container下的class为list的所有li
print(doc('#container .list li'))
"""
<li class="item-0">first item</li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
        <li class="item-1 active"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
"""

print(type(doc('#container .list li')))
# <class 'pyquery.pyquery.PyQuery'>
# PyQuery类型可以进行遍历

for item in doc('#container .list li'):
		print(item.text())
    # first item
    # second item
    # third item
    # fourth item
    # fifth item

查找节点

子节点

from pyquery import PyQuery as pq

doc=pq(html)
items=doc('.list')
print(type(items))
# <class 'pyquery.pyquery.PyQuery'>

print(items)
"""
<ul class=list>
<li class="item-0">first item</li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
        <li class="item-1 active"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>        
"""

lis=items.find('li')
print(type(lis))
# <class 'pyquery.pyquery.PyQuery'>

print(lis)
"""
<li class="item-0">first item</li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
        <li class="item-1 active"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a></li>
"""
# 查找子元素
lis=items.chlidren()
# 筛选子元素中符合条件的节点
print(items.children('.active'))
#       <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
#         <li class="item-1 active"><a href="link4.html">fourth item</a></li>

find()的范围是所有子孙元素,children()是子元素

父元素

parent()寻找某个节点的直接父元素,不会寻找父元素的父元素。parents()寻找祖先元素,如果存在多个相同的元素调用,会返回所有的祖先元素,如果要筛选某个特定的祖先元素可以传入parents()方法需要筛选的节点

兄弟元素

使用siblings()方法可以筛选出兄弟节点,如果需要对多个兄弟节点进行进一步筛选可以在siblings()方法中传入符合条件的节点

遍历节点

from pyquery import PyQuery as pq

doc=pq(html)
li=doc('.item-0.active')
print(li)
#         <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
print(str(li))
#         <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>

# 如果是多个节点需要遍历使用方法items()
lis=doc('li').items()
print(type(lis))
for li in lis:
  	print(li,type(li))

PyQuery返回单个节点或者多个节点不想beautifulsoup会返回一个列表,所以如果是多个节点需要使用items()方法进行遍历

获取属性

from pyquery import PyQuery as pq

doc=pq(html)
a=doc('.item-0.active a')
print(a.attr('href'))
# link3.html
# 也可以使用
print(a.attr.href)

# 当获取多个a时,使用a.attr只会返回第一个所以需要使用items()方法
a=doc('a')
for item in a.items():
  	print(item.arrt('href'))

#link2.html link3.html link4.html link5.html    

获取文本

使用text()方法可以把标签内部文字输出。如果要获取标签内部的html文本,可以使用html()

如果标签有多个使用text()会返回所有文字,使用逗号隔开。html()只会返回第一个

节点操作

addClass和removeClass

对节点中的class进行新增或者删除

from pyquery import PyQuery as pq

doc = pq(html)
li = doc('.item-0.active')
print("删除一次", li.removeClass('active'))
print("新增一次", li.add_class('active'))

# 删除一次 <li class="item-0"><a href="link3.html"><span class="bold">third item</span></a></li>
        
# 新增一次 <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>

attr、text、和html

除了使用addClassremoveClass对节点进行操作,还可以通过attr方法对属性进行操作,texthtml对内容进行操作

from pyquery import PyQuery as pq

html="""
<ul class="list">
		<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
</ul>
"""

lis = """
   <ul class="list">
		<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
	</ul>
   """
doc = pq(lis)
li = doc(".item-0.active")
print("原始数据", li)
li.attr('name', 'link')
print("添加属性", li)
li.text('changed item')
print("修改内容", li)
li.html('<span>changed item</span>')
print("修改内部属性", li)

"""
原始数据 <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
	
添加属性 <li class="item-0 active" name="link"><a href="link3.html"><span class="bold">third item</span></a></li>
	
修改内容 <li class="item-0 active" name="link">changed item</li>
	
修改内部属性 <li class="item-0 active" name="link"><span>changed item</span></li>
"""

remove

当只想要显示信息里的具体内容时可以使用remove删除节点

from pyquery import PyQuery as pq

lis = """
<ul class="list">
   hh
		<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
	</ul>
"""
text = doc('.list')
text.find('li').remove()
print(text.text())
# hh

伪类选择器

from pyquery import PyQuery as pq

doc=pq('html')
# 第一个li节点
li=doc('li:first-child')

# 最后一个li节点
li=doc('li:last-child')

# 第二个li节点
li=doc('li:nth-child(2)')

# 第三个li后的节点
li=doc('li:gt(2)')

# 偶数位置li节点
li=doc('li:nth-child(2n)')

# 包含文本second的li节点
li=doc('li:contains(second)')

官方文档

parsel

可以将Xpath、PyQuery库联合使用。Scrapy选择器基于做二次封装

pip3 install parsel

from parsel import Selector

html="""
<div>
	<ul>
		<li class="item-0">first item</li>
		<li class="item-1"><a href="link2.html">second item</a></li>
		<li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li>
		<li class="item-1 active"><a href="link4.html">fourth item</a></li>
		<li class="item-0"><a href="link5.html">fifth item</a></li>
	</ul>
</div>
"""

selector=Selector(text=html)
# 返回一个SelectorList可迭代对象
items=selector.css('.item-0')

for	item in items:
  	# 提前当前所有节点内容,输入文本
  	text=item.xpath('.//text()').get()
    
# 获取class属性为item-0的所以li节点文本,只会显示一个
# 需要使用getall()才会显示所有文本
items2=selector.xpath('//li[contains(@class,"item-0")]').get()

# 也可以替换为
# *提取所有子节点。::text提取文本
selector.css('.item-0::text').getall()

# 提取属性
selector.css('.item-0.active a::attr(href)').get()
selector.xpath('//li[contains(@class,"item-0") and contains(@class,"active")]/a/@href').get()

# 正则表达式提取
selector.css('.item-0').re('link.*')

官方文档

posted @ 2023-04-27 21:54  Kang_kin  阅读(274)  评论(0编辑  收藏  举报