Write the Code. Change t|

Kang_kin

园龄:4年5个月粉丝:2关注:9

正则表达式和XPath

正则表达式

常用匹配规则

模式 描述
\w 匹配字母、数字以及下划线
\W 匹配不是字母、数字及下划线的字符
\s 匹配任意空白字符串,等价于[\t\n\r\f]
\S 匹配任意非空字符串
\d 匹配任意数字,等价与[0-9]
\D 匹配任意非数字的字符
\A 匹配字符串的开头
\Z 匹配字符串结尾。如果存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结尾。如果存在换行,同时还会匹配换行符
\G 匹配最后匹配完成的位置
\n 匹配一个换行符
\t 匹配一个制表符
^ 匹配一行字符串的开头
$ 匹配一行字符串的结尾
. 匹配任意字符,除了换行符
[...] 用来表示一组字符,单独列出,例如[amk]用来匹配a、m或k
[^...] 匹配不在[]中的字符,例如匹配除a、b、c之外的字符
* 匹配0个或多个表达式
+ 匹配1个或多个表达式
? 匹配0个或1个前面的正则表达式片段,非贪婪模式
精确匹配n个前面的表达式
匹配n到m次由前面正则表达式定义的片段,贪婪模式
a|b 匹配a或b
() 匹配括号内的表达式,也表示一个组

match

向match传入匹配的字符串以及正则表达式,就可以检测正则表达式是否和字符串相匹配。

match方法会从字符串的起始位置开始匹配正则表达式,如果匹配返回匹配成功结果;不匹配返回None。

import re

content='Hello 123 4567 World_This is a Regex Demo'
print(len(content))   # 41
result=re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}',content)

print(result) # <_sre.SRE_Match object; span=(0,25),match='Hello 123 4567 World_This'
print(result.group()) # Hello 123 4567 World_This
print(resutl.span()) # (0,25)

^代表开头用Hello匹配;\s匹配空白符,用来匹配字符串中Hello后的空格;\d表示匹配数字,3个\d用来匹配123;\s匹配后面的空格;\d{4}代表匹配4次数字;空格;\w{10}表示匹配10个字母、数字及下划线

group输出匹配到的内容;span方法输出匹配的范围,结果字符串在原字符串的位置范围

匹配目标

将()把想提取的字字符串括起来,就可以提取字符串中的一部分内容。()实际标记一个子表达式的开始和结束位置,被标记的每个子表达式依次对应分组。

import re

content='Hello 1234567 World_This is a Regex Demo'
result=re.match('^Hello\s(\d+)\sWorld',content)
print(result)    # <_sre.SRE_Match object; span=(0,19),match='Hello 1234567 World'
print(result.group())  # Hello 1234567 World
print(result.group(1)) # 1234567
print(result.span())	 # (0,19)

通用匹配

.可以万能匹配,.可以匹配任意字符(除换行符*),*代表前面字符的无限次,

import re

content='Hello 123 4567 World_This is a Regex Demo'
result=re.match('^Hello.*Demo$',content)
print(result)    # <_sre.SRE_Match object; span=(0,41),match='Hello 123 4567 World_This is a Regex Demo'>
print(result.group())  # Hello 123 4567 World_This is a Regex Demo
print(result.span())	 # (0,41)

贪婪与非贪欲

import re

content='Hello 1234567 World_This is a Regex Demo'
result=re.match('^He.*(\d+).*Demo&',content)
print(result)   # <_sre.SRE_Match object; span=(0,40),match='Hello 1234567 World_This is a Regex Demo'>
print(result.group(1))  # 7

在贪欲模式下,.会匹配尽可能多的字符。正则表达式中,.后面是\d+,也就是至少一个数字,而且没有指定具体几个数字,因此,.***会匹配尽可能多的字符,所以就把123456全匹配了,只留下了一个可满足条件的数字了

当在^He.*?(\d+).*Demo$加上一个?号就会变为非贪婪模式。当.*?匹配到Hello后面的空白字符时,再往后就是数字了,而\d+恰好可以匹配,与.就不在匹配,而是交给.*?*去匹配。

如果.*?在字符串结尾,就可能匹配不到任何内容了,因为他会匹配尽可能少的字符

import re

content='http://weibo.com/comment/kEraCN'
result1=re.match('http.*?comment/(.*?)',content)
result2=re.match('http.*?comment/(.*)',content)
print('result1',result1.group(1))					# result1
print('result2',result2.group(1))					# result2 kEraCN

修饰符

# 在正则表达式中可以用一些可选标志修饰符来控制匹配模式。
import re

content="""Hello 1234567 World_This
is a Regex Demo
"""
result=re.match=('^He.*?(\d+).*?Demo$',content)
print(result.group(1))  # 运行报错

因为匹配的内容是除换行符以外的任意字符,当遇到.*?就不能匹配了,只需加上修饰符re.S既可修正错误

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 实现本地化识别(locale-aware)匹配
re.M 多行匹配,影响(^和$)
re.S 是匹配内容包括换行符在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志会影响\w、\W、\b、\B
re.X 该标志能够给予更灵活的格式
import re

content='Extra stings Hello 1234567 World_This is a Regex Demo Extra    stings'
result=re.match('Hello.*?(\d+).*>?Demo',content)
print(result)  # None

因为match方法会从字符串头开始匹配,因此在使用时需要考虑目标字符串的开头内容。

search会扫描整个字符串,然后返回第一个匹配成功的结果。

findall

如果需要获取与正则表达式相匹配的所有字符串,则可以使用findall方法。

sub

使用正则表达式除了可以提取信息,还可以用来修改信息。

import re

content='3453hdfjgl3453jjk'
content=re.sub('\d+','',content)
print(content) # hdfjgjjk

compile

可以将字符串编译成正则表达式对象,重复使用

import re

content1='2019-12-15 12:00'
content2='2019-12-17 12:55'
content3='2019-12-22 13:21'
patten=re.compile('\d{2}:\d{2}')
result1=re.sub(patten,'',content1)
result2=re.sub(patten,'',content2)
result3=re.sub(patten,'',content3)
print(result1,result2,result3) # 12:00 12:55 13:21
# 使用compile方法将正则表达式编译成一个正则表达式对象,完成复用,在借助sub方法去掉日期

XPath

当使用正则表达式在网页比较复杂,内容较多时不方便调试。所以可以先获取标签的节点然后获取标签下相应的值

常用表达式

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性

基本使用

渲染所有

from lxml import etree

text = '''
<div> 
<ul>
<li class="item-0"><a href="link.1html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''

def default():
    html = etree.HTML(text)
    result = etree.tostring(html)
    print(result.decode('utf-8'))
# 即使最后的li标签没有闭合,使用tostring方法即可修正html代码,但是结果是bytes类型,使用decode进行转换    

获取节点

获取所有节点
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())
# 以//开头的Xpath规则匹配,//*代表匹配所有的节点
html.xpath('//*')

# 匹配节点下所有的li节点,用索引获取具体
result=html.xpath('//li')
result[index]
子节点
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())

# 获取li节点下的所有子节点a
html.xpath('//li/a')

# 获取ul节点下所有的子孙节点a
html.xpath('//ul//a')
父节点
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())
# 获取link4的父节点的class属性
result=html.xpath('//a[@href="link4.html"]/../@class')  # ['item-1']
# 也可以使用parent::获取父节点
html.xpath('//a[@href="link4.html"]/parent::*/@class')
属性匹配
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())
# 根据符号@进行属性过滤
html.xpath('//li[@class="item-0"]')
文本获取
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())
# 获取li下面的文本内容
rest=html.xpath('//li[@class="item-0"]/text()')
print(rest)  #['\n  ']
# 因为li下还有一个a标签所以还需加上一个a标签  //li[@class="item-0"]/a/text()   ['first item','first item']

#直接使用子孙获取
rest=html.xpath('//li[@class="item-0"]//text()')
print(rest)
# ['first item','first item','\n     ']
属性获取
from lxml import etree

html = etree.parse('test.html', etree.HTMLParser())
# 获取属性的值,而非属性的类型进行匹配
rest=html.xpath('//li/a/@href')
print(rest)
#['link1.html','link2.html','link3.html','link4.html','link5.html']
属性多值匹配
from lxml import etree

text='''
<li class="li li-first"><a href="link.html"></a>first html</li>
'''

html = etree.HTML(text)

# 当遇到class为多属性时使用[@class="li"]将不起作用,需要使用contains
rest=html.xpath('//li[contains(@class,"li")]/a/text()')
print(rest)
# ['first item']
多属性匹配

Xpath运算符

运算符 描述
or
and
mod 计算除法的余数
| 计算两个节点集
+ 加法
- 减法
* 乘法
div 除法
= 等于
!= 不等于
< 小于
<= 小于或等于
> 大于
>= 大于或等于
from lxml import etree

text='''
<li class="li li-first" name="item"><a href="link.html"></a>first html</li>
'''

html = etree.HTML(text)

# 使用运算法进行连接
html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
按序选择
from lxml import etree

text="""
<div>
<ul>
<li class='item-0'><a herf="link1.html">first item</a></li>
<li class='item-1'><a herf="link2.html">second item</a></li>
<li class='item-inactive'><a herf="link3.html">third item</a></li>
<li class='item-0'><a herf="link4.html">fourth item</a></li>
<li class='item-1'><a herf="link5.html">fifth item</a></li>
</ul>
</div>
"""

html=etree.HTML(text)
# 下标为1的li的a标签文本内容
result=html.xpath('//li[1]/a/text()')
print(result)		#'[first item]'

# 下标为最后一个li的a标签文本内容
result=html.xpath('//li[last()]/a/text()')
print(result) 	# ['fifth item']

# 下标为li位置小于3的a标签文本内容
result=html.xpath('//li[position()<3]/a/text()')
print(result)		# ['first item','second item']

# 下标为最后li减去2个位置的a标签文本内容
result=html.xpath('//li[last()-2]/a/text()')
print(result)		# ['third item']

本文作者:Kang_kin

本文链接:https://www.cnblogs.com/kangkin/p/17343136.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Kang_kin  阅读(599)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起