目录
3.1re(正则)
3.1.1符号
- '.':1个任意字符,除换行符
- '?':[0,1]任意字符
- '+':[1:] 任意字符
- '*':[0,:] 任意字符
- \s:空字符;
- \S:非空字符
- \w:字母数字下划线
- \W:非字母数字下划线
- \d:任意数字
- \D:任意非数字
- ^:匹配字符串的开头(加上这个就和match一样)
- $:匹配字符串的结尾
- '[]':匹配方括号中的任意字符.[abc],[a-zA-Z]
- '{n}'匹配大括号前面的字符n次,{n,}大于n次;{,n}小于n次;{n,m}n-m次之间
- '*? +? ?? {m,n}?':这些都是懒惰模式
- \b:边界字符
- 注意:在写正则的时候要加上字符r,为的是不让其和转义字符冲突.]
3.1.3函数
3.1.3.1匹配函数
re.match()函数
从头进行匹配,一旦匹配成功一个就返回结果对象,我们可以通过group()函数进行提取,如果没有匹配到结果我们返回None.
- 参数1:正则表达式
- 参数2:目标文本
- 参数3:可以忽略
import re
match_obj = re.match('abc', 'abc123', re.I)
print(match_obj.group())
fullmatch()函数
从头开始匹配,要求文本与字符串完全匹配,没有匹配到结果则返回None,匹配到结果可以使用group()函数对内容进行提取.
- 参数1:正则表达式
- 参数2:目标文本
- 参数3:可以忽略
try:
match_obj = re.fullmatch(r'abc\w+', 'abc123', re.I)
print(match_obj.group())
except Exception as ex:
print('没有匹配成功')
search()函数
扫描整个字符串,查找匹配整个正则格式的内容,找到就返回Match对象,通过group()对内容进行提取
- 参数1:正则表达式
- 参数2:目标文本
- 参数3:可以忽略
match_obj = re.search('abc', 'xyzABCdef', re.I) # re.I表示忽略大小写
print(match_obj)
print(match_obj.group()) # 匹配的内容
print(match_obj.span()) # 匹配内容的位置
print(match_obj.start()) # 从开始匹配
findall()函数
查找匹配到的所有内容,,返回值是一个列表,如果没有找到,返回的是一个空列表.
- 参数1:正则表达式
- 参数2:目标文本
- 参数3:可以忽略
try:
list1= re.findall('\d+','123abc456cdb34jfn456')
print(list1)
except Exception as ex:
print('没有匹配成功')
3.1.3.2其他函数
compile()函数
通过该函数包裹的内容会生成一个正则对象,我们可以直接通过该对象调用正则匹配相关函数
import re
pattern = re.compile('abc') # 正则对象
match_obj = pattern.match('abcdef')
# 匹配对象调用group获取匹配的内容
print(match_obj.group())
group()函数
从对象内容中提取数据
sub()函数
通过该函数可以将字符串中的空白字符替换掉.
- 参数1:正则
- 参数2:将匹配到的字符串填充成该字符串
- 参数3:要匹配的文本
- 参数4:要匹配的最大个数
将文本数据替换成自己的想要的内容
import re
text=re.sub(r'\\n','',r'1q2\nw3\n45r6\nf7f\n#89#f0',5)
print(text)
split()函数
通过pattern字符集切割string,如果有多个字符要进行切割,可以放在[]中进行选择,如果想要保留分隔符,可以放置在括号中,如果不想保留分隔符,可以放置在括号中,使用这种格式"?:[]"
import re
result1=re.split('(\d+)','123sjfh2345kfj89d') # ['', '123', 'sjfh', '2345', 'kfj', '89', 'd']
result2=re.split('\d+','123sjfh2345kfj89d') # ['', 'sjfh', 'kfj', 'd']
print(result1,result2)
3.1.2函数参数
- re.I:大小写忽略
- re.M:多行匹配,对^和$有影响
- re.S:使点匹配包括换行在内的所有字符
3.1.3分组
\number 引用number组内容
import re
s = '<div aaaaa>lalala</div><div hahahaha>lelelelele</div>'
m = re.match(r'<(.+?) .+?>.+?</\1>', s)
print(m.group())
print(m.group(1))
?P<name>pattern) 使用名: (?P=name)
s = '<div aaaaa>lalala</div><div hahahaha>lelelelele</div>'
m = re.match(r'<(?P<name>.+?) .+?>.+?</(?P=name)>', s)
print(m.group())
print(m.group(1))
这种起别名的方式一定要注意,不能将其他的字符放置在?P的前面,我们在使用?P的时候要将其用括号包裹起来.前者和后者是同一个东西,只不过用别名将其代替了而已
() 表示分组关系
s = '<div class="breadcrumb">布鲁斯</div>'
m = re.fullmatch(r'<(.+) class="breadcrumb">(.+)</\1>', s) #
print(m)
print(m.group(2))
| 表示或者关系
s = 'hi linda'
m_obj = re.search('(hi|hello) (Jack|Tom|Lucy|Linda)', s, re.I) #
print(m_obj)
print(m_obj.group())
print(m_obj.group(1))
print(m_obj.group(2))
使用圆括号将其包裹,然后字符串之间用|分开表示整个字符串之间是或者关系,[]表示从中选取一个字符进行匹配,但是使用()就表示匹配的是字符串.
3.2xpath
安装lxml库:
pip3.6 install lxml
文本内容
books = '''
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>50</price>
</book>
<book category="web1" cover="paperback">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
<book category="web2" cover="paperback">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>40</price>
</book>
<book category="web3" cover="paperback">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
'''
html=etree.HTML(books) # 封装成html对象
etree.tostring(html,encoding='utf-8').decode('utf-8') # 对对象进行编码
html.xpath('//book') # 获取标签对象
html.xpath('//book/@category') # 获取指定属性的值
html.xpath('//book') # 获取标签对象
books[0].xpath('./year/text()') # 当前标签下某个标签的文本内容
books[0].xpath('..//year/text()') # 上一级标签下某个标签的文本内容
html.xpath('//book[position() <= 3]/title/text()') # 限制标签范围
html.xpath('//book[last()]') #匹配最后一个book对象
html.xpath('//book[first()]') # 匹配第一个book对象
html.xpath('//book[@cover="paperback"]/title/text()') # 指定属性的book对象的文本内容
html.xpath('//book[@cover="paperback"][@category="web2"]/title/text()') # 指定一个对象的多个属性
html.xpath('//book[contains(@category,"web")][@cover="paperback"]/@category') # book对象属性category的值模糊匹配web,并且属性cover匹配paperback
html.xpath('//book|//title') # book对象或者title对象
html.xpath('//@*') # 匹配所有函数属性的属性值
html.xpath('//book[price mod 2 = 0]/title/text()') # book标签下属性price的文本值为偶数的tiitle标签的文本值
3.3beautifulSoup
3.3.1基础入门
安装:pip3.6 install bs4
效率低于xpath和正则,但是比前两个都要简单许多,Python标准中的html解析器,支持lxml的lxml解析器.BeautifulSoup将html转换成一个树形结构,每个结点都是一个Python对象,所有对象可以归纳为4类。Tag对象、文本内容对象、Comment注释对象、文档内容对象。
soup = BeautifulSoup(data,'lxml')
- 参数1:要进行解析的数据
- 参数2:解析器lxml
3.3.2Soup的四个对象(解析方式一)
文档对象
soup = BeautifulSoup(data,'lxml')
print(soup)
print(type(soup))
<html>
...
</html>
<class 'bs4.BeautifulSoup'>
标签对象
t = soup.title
print(t)
print(type(t))
# 结果
<title>soup测试</title>
<class 'bs4.element.Tag'>
文本对象
s = soup.title.string
print(s)
print(type(s))
# 结果
soup测试
<class 'bs4.element.NavigableString'>
注释对象
# 原文
<li class="hello" id="world"><a id="world" href="http://www.baidu.com" title="出塞"><!--秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山--></a></li>
程序
s = soup.li.string
print(s)
print(type(s))
结果
秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山
<class 'bs4.element.Comment'>
3.3.3子节点
直接子节点
cs = soup.body.div.ul.children
print(cs) # 可迭代对象
for item in cs:
print(item) # ul中的html内容
所有子孙结点
d = soup.body.div.ul.descendants # 一个可迭代对象
for item in d:
print(item)
异同点:
- children只是获取ul下的html内容,但是descendants会将当前对象下每一层分开。举个例子ul*2>li*3>a*2,只获取第一个ul下的内容,children获取的是li*3>a*2这部分内容,但是descendants会获取li*3>a*2和a*2这些内容.
3.3.4搜索文档树(解析方式二)
获取单个元素:
- soup.find(name='标签',id='id值')
- soup.find(name='标签',attrs={'class':'hello','id':'world'})
获取元素列表:
- soup.find_all(name='标签',class_='hello')将其放在列表中
实际举例:
print(soup.find(name = 'li',id = 'wo'))
print(soup.find(name = 'li',attrs={'class':'hello','id':'world'}))
lis = soup.find_all('li',class_ = 'hello')
print(lis)
3.3.5CSS选择器(解析方式三)
和find_all()有相同的功能,写css的时候标签名不加任何修饰,类名外直接加点,id名前直接加#,在这里我们可以用类似的方法筛选元素,用到的方法有soup.select()返回的是list
标签查找和属性查找
- 空格表示下一个元素
print(len(soup.select('li'))) # 通过标签查找
print(len(soup.select('.hello'))) # 通过类名查找
print(len(soup.select('#h2'))) # 通过id进行查找
print(soup.select('div[class="cao"][id="h2"] > ul > li')[0]) # 不支持下标,但是我们可以将下标写在外层
组合查找
ret = soup.select('li#wo') # id为wo的li标签的html包括li标签
ret = soup.select('lists.hello a') # 类名为hello的lists标签下的a标签
print(soup.select('li#world')) # 包括li标签的html内容
print(soup.select('li #world')) # 不包括li标签,为li标签下的html内容
soup.select('div>ol>pl,zl') # 获取div下的ol下的pl和div下的ol下的zl,这里的pl和zl是同一级的
获取内容
tag = soup.find_all(name='title')[1] # 通过find_all获取到的是一个列表
print(tag) # 获取标签的html
print(tag.name) # 获取某个标签的name值
print(tag.attrs) # 获取标签的所有属性和属性值,并且是存放在字典中的
print(tag.attrs['href']) # 获取标签对象具体属性的值
print(tag.string) # 获取标签的内容
print(tag['href']) # 直接获取标签的某个属性
print(tag.get_text()) # 获取标签的内容
get_text()和string()的区别
tag = soup.find_all(name='title')[1]
tag.get_text() 递归获取该标签下的所有文本内容
tag.string() # 如果该标签的内容是文本内容则获取,如果是html内容,则返回None
3.4jsonpath库
简单介绍
- 上面提到的都是处理html或者xml格式的数据的,如果遇到json格式的数据,就需要我们使用jsonpath进行解析,尽管我们使用序列化和反序列化也可以解决这种问题,但是过于麻烦,因为我们可以使用jsonpath更加快捷方便。
jsonpath和xpath的对比
实际应用
注意点:
- book[?(@.isbn)]该属性存不存在
- book[(@.length -1)]该对象的个数
- book[?(@.price>12)]该对象的属性满不满足某个条件