正则表达式
常用匹配规则
模式 | 描述 |
---|---|
\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 | 该标志能够给予更灵活的格式 |
search
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进行转换
获取节点
标签:XPath,匹配,item,正则表达式,re,html,result,etree From: https://www.cnblogs.com/kangkin/p/17343136.html获取所有节点
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']