XPath的使用
全称为XML Path Language,用于在XML文档中查找信息,同样适用于HTML文档的搜索
XPath的常用规则
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
. . | 选取当前节点的父节点 |
@ | 选取属性 |
例:
//title[@lang='eng']
表示选择所有名称为title,同时 属性lang的值为eng的节点
基本使用
from lxml import etree
text='''
<div>
<ul>
<li class="item-0"><a href="link1.heml">first item</a></li>
<li class="item-1"><a href="link2.heml">second item</a></li>
<li class="item-inactive"><a href="link3.heml">third item</a></li>
<li class="item-1"><a href="link4.heml">fourth item</a></li>
<li class="item-0"><a href="link5.heml">fifth item</a>
</ul>
</div>
'''
html=etree,HTML(text)
result=etree.totring(html)
print(result.decode('utf-8'))
读取文件解读
from lxml import etree
html=etree.prase('./test.html',etree.HTMLParser())
result=etree.tostring(html)
print(result.decode('utf-8'))
所有文件
from lxml import etree
html= etree.parse('./test.heml',etree.HTMLParse())
result=html.xpath('//*')
print(result)
指定节点名称
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParse())
result=html.xpath('//li')
print(result)
子节点
/用于获取直接子节点
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParse())
result=html.xpath('//li/a')
print(result)
//用于获取子孙节点
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParse())
result=htmlx.path('//li//a')
print(result)
父节点
. .用于查找父节点
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//a[@href="link4.html"]/../@class')
print(result)
也可以通过parent::来获取父节点
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//a[@href="link4.html"]/parent::*/@class')
属性匹配
在选取节点的时候,还可以使用@符合实现属性过滤
from lxml import etree
html=etree.parse('.test.html',etree.HTMLParser())
result=html.xpath('//li[@class="item-0"]')
文本获取
用XPath中的text方法可以获取节点中的文本
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li[@class"item=0"]//text()')
print(result)
属性获取
from lxml import etree
html=etree.parse('./test.html',etree.HTMLParser())
result=html.xpath('//li//a/@href')
print(result)
属性多值匹配
当节点属性拥有多个值时需要用到contains方法
from lxml impot etree
text='''
<li class="li li-first"><a href="link.html">first item</a></li>
'''
html=etree.HTML(text)
result=html.xpath('//li[contains(@class,"li")]/a/text()')
print(result)
多属性匹配
from lxml import etree
text='''
<li class="li li-first" name="item"><a href="link.html">first item</a></li>
'''
html=etree.HTML(text)
result=html.xpath('//li[contains(@class , "li") and @name="item"]/a/text()')
print(result)
其中的and其实是XPath中的运算符
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
or | 或 | age=19 or age=20 | 如果age是19,则返回true。如果age是21,则返回false |
and | 与 | age>19 and age<21 | 如果age是20,则返回true。如果age是18,则返回false |
mod | 计算除法的余数 | 5 mod 2 | 1 |
I | 计算两个节点集 | //bookI//cd | 返回所有拥有book和cd元素的节点集 |
+ | 加法 | 6+4 | 10 |
- | 减法 | 6-4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | age=19 | 如果age是19,则返回true。如果age是20,则返回false |
!= | 不等于 | age!=19 | 如果age是18,则返回true。如果age是119,则返回false |
< | 小于 | age<19 | 如果age是18,则返回true。如果age是19,则返回false |
<= | 小于等于 | age<=19 | 如果age是19,则返回true。如果age是20,则返回false |
> | 大于 | age>19 | 如果age是20,则返回true。如果age是19,则返回false |
>= | 大于等于 | age>=19 | 如果age是19,则返回true。如果age是18,则返回false |
按序选择
在选择节点时,某些属性可能同时匹配了多个节点,但我们只需要其中的某一个。这时可以使用往中括号中传入索引的方法获取特定次序的节点
from lxml import etree
text='''
<div>
<ul>
<li class="item-0"><a href="link1.heml">first item</a></li>
<li class="item-1"><a href="link2.heml">second item</a></li>
<li class="item-inactive"><a href="link3.heml">third item</a></li>
<li class="item-1"><a href="link4.heml">fourth item</a></li>
<li class="item-0"><a href="link5.heml">fifth item</a>
</ul>
</div>
'''
html=etree.HTML(text)
result=html.xpath('//li[1]/a/text()')
print(result)
result=html.xpath('//li[last()]/a/text()')
print(result)
result=html.xpath('//li[position()<3]/a/text()')
print(result)
result=html.xpath('//li[last()-2]/a/text()')
节点轴选择
from lxml import etree
text='''
<div>
<ul>
<li class="item-0"><a href="link1.heml">first item</a></li>
<li class="item-1"><a href="link2.heml">second item</a></li>
<li class="item-inactive"><a href="link3.heml">third item</a></li>
<li class="item-1"><a href="link4.heml">fourth item</a></li>
<li class="item-0"><a href="link5.heml">fifth item</a>
</ul>
</div>
'''
html=etree.HTML(text)
result=html.xpath('//li[1]/ancestor::*') #调用了ancestor轴,可以获取所有祖先节点
print(result)
result=html.xpath('//li[1]/ancestor::div')
print(result)
result=html.xpath('//li[1]/attribute::*') #调用了attribute轴,可以获取所有属性值
print(result)
result=html.xpath('//li[1]//child::a[@href="link1.html"]') #调用了child轴,可以获取所有直接子节点
print(result)
result=html.xpath('//li[1]//descendant::span') #调用了descendant轴,可以获取所有子孙节点
print(result)
result=html.xpath('//li[1]//following::*2') #调用了following轴,可以获取当前节点之后的所有节点
print(result)
result=html.xpath('//li[1]//following::sibling*') #调用了following-sibling轴,可以获取当前节点之后的所有同级节点
print(result)
Beautiful Soup
其在解析时是依赖解析器的,推荐使用LXML解析器
from bs4 import BeautifulSoup
soup=BeautifulSoup('<p>Hello</p>','lxml')
print(soup.p.string)
pip3 install beautifulsoup4
基本使用
html="""
<html><head><title>The Dormouse's story </title></head>
<body>
<p class="title" name="dormouse"><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>
"""
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.prettify())
print(soup.title.string)
其中调用的prettify方法可以把要解析的字符串以标准的缩进格式输出
而调用soup.titlt.string则是输出HTML中title节点的文本内容
节点选择器
html="""
<html><head><title>The Dormouse's story </title></head>
<body>
<p class="title" name="dormouse"><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>
"""
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)
提取信息
获取名称
print(soup.title.name)
获取属性
print(soup.p.attrs)
print(soup.p.attrs['name']) #调用attrs属性
print(soup.p['name'])
获取内容
print(soup.p.string)
嵌套选择
print(soup.head.title)
print(type(soup.head.title))
print(soup.head.title.string)
关联选择
子节点和子孙节点
选取节点之后,如果想要获取它的直接子节点,可以调用contents属性
html="""
<html>
<head>
<title>The Dormous's story</title>
</head>
<body>
<p class="story">
Once upon a time there were three little sisters;and their names were
<a herf="http://example.com/elsie" class="sister" id="link1">
<span>Elsie</span>
</a>
<a herf="http://example.com/lacie" class="sister" id="link2">
<span>Lacie</span>
</a>
and
<a href="http://example.com/tillie" class="sister" id="link3">
<span>Tillie</span>
and they lived at the bottom of a well
print(soup.p.contents)
同样,我们可以调用child属性
print(soup.p.children)
for i,child in enumerate(soup.p.children):
print(i,child)
要得到所有的子孙节点,则可以调用descendants属性
print(soup.p.descendants)
for i,child in enumerate(soup.p.descendants):
print(i,child)
父节点和祖先节点
获取某个节点元素的父节点,可以调用parent属性
print(soup.p.parent)
获取某个节点的所有祖先节点,可以调用parents属性:
print(type(soup,a.parents))
print(list(enumerate(soup.a.parents)))
兄弟节点
next_sibling和previous_sibling分别用于获取节点的下一个和上一个兄弟节点。而next_siblings和previous_siblings分别返回后面和前面的所有兄弟节点
提取信息
print(type(soup.a.next_sibling))
print(soup.a.next_sibling)
print(soup.a.next_sibling.string)
print(type(soup.a.parent))
print(soup.a.parent)
print(list(soup.a.parent)[0])
print(list(soup.a.parent)[0].attrs['class'])
方法选择器
find_all
find_all查询所有符合条件的元素
API:
find_all(name,attrs,recursive,text,**kwargs)
name参数:
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1",name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jar</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')))
返回的列表元素都是tag类型的,所以可以进行嵌套查询
for ul in soup,find_all(name='ul'):
print(ul.find_all(name='li'))
attrs参数:
print(soup.find_all(attrs={'id'='list-1'}))
print(soup.find_all(attrs={'name'='elements'}))
对于一些常用的属性,例如id和class,我们可以不用attrs传递
print(soup.find_all('id'='list-1'))
print(soup.find_all('class_'='elements'))
python中class是一个关键字,所以需要在其后加一个_
text参数:
可以用来匹配节点的文本,其传入形式可以是字符串,也可以是正则表达式对象
import re
html='''
<div class="panel">
<div class="panel-body">
<a>Hello,this is a link</a>
<a>Hello,this is a link,too</a>
</div>
</div>
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.find_all(text=re.compile('link')))
find
find方法返回单个元素,而fand_all返回元素列表
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1",name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jar</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'xlml')
print(soup.find('name'='ul'))
print(type(soup.find('name'='ul')))
print(soup.find('class_'='list'))
其他查询方法:
- find_parents和find_parent:前者返回所有祖先节点,后者返回直接父节点
- find_next_siblings和find_next_sibling:前者返回后面的所有兄弟节点,后者返回后面第一个兄弟节点
- find_previous_siblings和find_previous_sibling:前者返回前面的所有兄弟节点,后者返回前面第一个兄弟节点
- find_all_next和find_next:前者返回节点后所有符合条件的节点,后者返回后面第一个符合条件的节点
- find_all_previous和find_previous:前者返回节点前所有符合条件的节点,后者返回前面第一个符合条件的节点
CSS选择器
使用CSS选择器只需要调用select方法,传入相应的CSS选择器即可
html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1",name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jar</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))
嵌套选择
for ul in soup.select('ul'):
print(ul.select('li'))
获取属性
for ul in soup.select('ul'):
print(ul['id'])
print(ul.atrrs['id'])
获取文本
for li in soup.select('li')
print(li.get_text())
print(li.string)
pyquery
准备工作
pip3 install pyquery
初始化
用pyquery库解析HTML文本的时候需要将其初始化为一个PyQuery对象
字符串初始化
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'>fouth item</a></li>
<li class='item-0'><a href='link5.heml'>fifth item</a></li>
</ul>
</div>
'''
from pyquery import PyQuery as pq
doc=pq(html)
print(pq('li'))
URL初始化
from pyquery as pq
doc=pq(url='www.baidu.com')
print(doc('title'))
文件初始化
from pyquery as pq
doc=pq(filename='demo.html')
print(doc('li'))
基本CSS选择器
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'>fouth item</a></li>
<li class='item-0'><a href='link5.heml'>fifth item</a></li>
</ul>
</div>
'''
print(doc('#container .list li'))
print(type(doc('#container .list li')))
#表示ID,.表示类
查找节点
子节点
查找节点时,需要用到find方法
from pyquery import PyQuery as pq
doc=pq(html)
items=doc('.list')
print(type(items))
print(items)
lis=items.find('li')
print(type(lis))
print(lis)
find方法的查找范围是节点的所有子孙节点。如果只想查找子节点,那么可以用children方法
lis=items.children()
print(type(lis))
print(lis)
还可以进行筛选
lis=items.children('.active')
print(lis)
父节点
我们可以用parent方法获取某个节点的父节点
items=doc('.list')
container=items.parent()
print(type(contaier))
print(container)
还可以使用parents方法查找祖先节点
items=doc('.list')
parents=items.parents()
print(type(parents))
print(parents)
也可以筛选
parent=items.parents('.warp')
print(parent)
兄弟节点
li=doc('.list .item-0 .active')
print(li.siblings())
也可以进行筛选
li=doc('.list .item-0 .active')
print(li.siblings('.active'))
遍历节点
遍历节点需要调用items方法
lis=doc('li').items()
print(type(lis))
for li in lis:
print(li,type(li))
获取属性
a=doc('.item-0.active a')
print(a,type(a))
print(a.attr('href'))
也可以调用attrs属性
print(a,.attr.href)
其返回结果只有一个,故当结果包含多个节点时,需要使用遍历
a=doc('a')
for item in a.items():
print(item.attr('href'))
获取文本
a=doc('.itemm-0 active a')
print(a)
print(a.text())
要获取节点内部的HTML文本,需要用html方法:
li=doc('.iten-0 active')
print(li)
print(li.html())
而其中text方法不需要遍历 ,会对所有节点取文本之后合并成一个字符串
节点操作
可以使用pyquery库对节点进行动态修改
addClass和removeClass
li=doc('.item-0 active')
print(li)
li.removeClass('active')
print(li)
li.addClass('actibe')
print(li)
attr、text和html
li=doc('.item-0.active')
print(li)
li.attr('name','link')
print(li)
li.text('change item')
print(li)
li.html('<span>changed item</span>')
print(li)
remove
wrap=doc('.wrap')
print(wrap.text())
wrap.find('p').remove()
print(warp.text())
伪类选择器
li=doc('li:first-child') #选择文本中第一个li节点
print(li)
li=doc('li:last-child') #最后一个li节点
print(li)
li=doc('li:nth-child(2)') #第二个li节点
print(li)
li=doc('li:gt(2)') #第三个li之后的li节点
print(li)
li=doc('li:nth-child(2n)') #偶数位置的li节点
print(li)
li=doc('li:contains(second)') #包含second文本的li节点
print(li)
parsel
准备工作
pip3 install parsel
初始化
我们一般会用parsel库里的Selector类声明一个Selector对象
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'>fouth item</a></li>
<li class='item-0'><a href='link5.heml'>fifth item</a></li>
</ul>
</div>
'''
from parsel import Selector
selector=Selector(text=html)
之后我们可以使用css和xpath方法分别传入CSS选择器和XPath进行内容提取
items=selector.css('.item-0')
print(len(items),type(items),items)
items2=selector.xpath('//li[contains(@class,"item-0")]')
print(len(items2),type(item2),items2)
提取文本
需要进行遍历
items=selector.csss('.item-0')
for item in items:
text=item.xpath('.//text()').get()
print(text)
提取属性
result=selector.css('.item-0.active a::attr(href)').get()
print(result)
result=selector.xpath('//li[contains(@class,"item-0")and contains(@class,"active")]/a/@href').get()
正则提取
result=selector.css('.item-0').re('link.*')
print(result)
result=selector.css('.item-0').re_first('<span class="bold">(.*?)</span>') #调用了re_first方法选择第一个符合条件的内容
print(result)