JSON是一种轻量级的数据格式,web数据交换的主流数据结构,相应的XML用的较少
初识正则表达式
是一个特殊的字符序列,用于检测一个字符串是否与我们所设定的字符序列相匹配
快速检索文本、实现一些替换文本的操作,比如如下的需求:
- 检查一串数字是否是电话号码
- 检测一个字符串是否为邮箱地址
- 把一个文本的指定单词替换为另外一个单词
Python自带函数处理字符串:判断字符串a中是否含有Python
a = 'C|C++|Python|Java' #判断字符串a中是否含有Python
print(a.index('Python') > -1)
print('Python' in a)
#True
#True
使用正则判断:
import re #引入正则的包
a = 'C|C++|Python|Java'
r = re.findall('Python', a) #findall函数返回一个列表,r = ['Python']
if len(r) > 0:
print('字符串中包含Python')
#字符串中包含Python
这个例子中需要匹配的是 常量Python,可以说使用正则是没有意义的。下一节我们讨论有意义的正则。
元字符与普通字符
取出给定字符串中的数字:
import re
a = 'C1C++2Python3Java4Javascript'
r = re.findall('\d', a) #/d 表示数字0-9
print(r)
#['1', '2', '3', '4']
'Python' 普通字符,‘\d’ 元字符,正则表达式就是由一系列普通字符和元字符组成的。
取出给定字符串中的非数字:
import re
a = 'C1C++2Python3Java4Javascript'
r = re.findall('\D', a) #/D 表示非数字
print(r)
#['C', 'C', '+', '+', 'P', 'y', 't', 'h', 'o', 'n', 'J', 'a', 'v', 'a', 'J', 'a', 'v', 'a', 's', 'c', 'r', 'i', 'p', 't']
字符集
a和c作为普通字符,可以在长字符串中确定一个小段,定界
#字符集
import re
s = 'abc, adc, aec, ahc, afc, acc'
r = re.findall('a[cf]c', s) #中间是c或者f
print(r)
r1 = re.findall('a[^cf]c', s) #中间不是c和f
print(r1)
r2 = re.findall('a[b-d]c', s) #中间按照顺序是b,c,d
print(r2)
#['afc', 'acc']
#['abc', 'adc', 'aec', 'ahc']
#['abc', 'adc', 'acc']
中括号[ ] :表示 c 或者 f
^ :取反操作
减号 - : 按照顺序 b-d 表示 b,c,d,省略中间操作
概括字符集
例如之前用的 \d 表示 [0-9],两者可以替换
\D 可用 [^0-9] 替换
\w :匹配单词字符(包括下划线在内),可用 [A-Za-z0-9_]替换:
#概括字符集
import re
a = '#&*python1111java678php__'
r = re.findall('\w', a) #包括下划线
print(r)
#['p', 'y', 't', 'h', 'o', 'n', '1', '1', '1', '1', 'j', 'a', 'v', 'a', '6', '7', '8', 'p', 'h', 'p', '_', '_']
\W :匹配非单词字符
\s:匹配空白字符,空格、\n、\r等:
#概括字符集
import re
a = '#&*python1111\tjava\r 678php__\n'
r = re.findall('\s', a)
print(r)
#['\t', '\r', ' ', '\n']
\S :匹配处空格符之外的字符
. (点):匹配除换行符 \n 之外的其他所有字符
数量词
大括号{ } 匹配连续三个字母的单词:
#数量词
import re
a = 'python1111java678php'
r = re.findall('[a-z]{3}', a)
print(r)
#['pyt', 'hon', 'jav', 'php'] #这里注意java后面的a不会匹配
匹配完整的单词:
import re
a = 'python1111java678php'
r = re.findall('[a-z]{3,6}', a) #表示3-6个字符
print(r)
#['python', 'java', 'php']
为什么这里在匹配到3个字符时已经算是匹配完成了,却还是匹配出了python?答案请看下一节
贪婪与非贪婪
默认情况下,Python倾向于使用一种贪婪的匹配方式(3-6的区间,匹配完成3个时,会继续向下匹配,直到取到符合情况的最大值)
非贪婪模式:
import re
a = 'python1111java678php'
r = re.findall('[a-z]{3,6}?', a)
print(r)
#['pyt', 'hon', 'jav', 'php']
{3,6}? 后面加问号;与 {3} 的匹配结果相同
匹配0次1次或者无限多次
* 对前一个字符匹配0次或者无限次
import re
a = 'pytho0python1pythonn2'
r = re.findall('python*', a)
print(r)
#['pytho', 'python', 'pythonn']
+ 对前一个字符匹配一次或者无限多次
import re
a = 'pytho0python1pythonn2'
r = re.findall('python+', a)
print(r)
#['python', 'pythonn']
?对前一个字符匹配0次或者1次
import re
a = 'pytho0python1pythonn2'
r = re.findall('python?', a)
print(r)
#['pytho', 'python', 'python']
{3,6}?和 python? 时的问号用法不一样。
边界匹配符
判断给定qq号是否是 4-8 位
import re
qq = '100000001' #给定9位数字
r1 = re.findall('\d{4,8}', qq) #返回8位字符,判断错误
print(r1)
r = re.findall('^\d{4,8}$', qq) #从字符串的第一个开始匹配,直到最后一个结束匹配,返回空,判断正确
print(r)
#['10000000']
#[]
^ $ 组成边界匹配符
^ 从字符串开头开始匹配
$ 从字符串末尾开始匹配
使用例子如下:
import re
qq = '100000001'
r1 = re.findall('000', qq)
print(r1)
r2 = re.findall('^000', qq) #匹配以000开头的字符
print(r2)
r3 = re.findall('000$', qq) #匹配以000结尾的字符
print(r3)
#['000', '000']
#[]
#[]
组
判断字符串中是否出现三次Python:
import re
a = 'PythonPythonPythonPython'
r = re.findall('(Python){3}', a)
print(r)
#['Python']
这里一个括号对应一组,(Python){3} 是把Python 重复三次,Python{3} 是将前一个字符重复三次
中括号 [ ] :或关系
小括号 ():且关系
匹配模式参数
re.findall() 函数的第三个参数为:匹配模式参数
多个模式可以用 | 进行连接,表示且关系
忽略大小写匹配:
import re
a = 'PythonC#JavaPHP'
r = re.findall('c#', a, re.I)
print(r)
#['C#']
这里如果不加参数 re.I, 输出为空, re.I 是忽略大小写匹配
已知概括字符集 . (点):匹配除换行符 \n 之外的其他所有字符
re.S 改变点的行为,使得点可以匹配换行符 \n :
import re
a = 'PythonC#\nJavaPHP'
r = re.findall('c#.{1}', a, re.I) #匹配c#和任意一个字符(换行符除外),输出空
print(r)
r2 = re.findall('c#.{1}', a, re.I | re.S) #匹配c#和任意一个字符(包括换行符),可以匹配到
print(r2)
#[]
#['C#\n']
re.sub正则替换
re.sub 替换字符串中的 C# 为 GO:
import re
a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', 'GO', a) #默认第四个参数为0,表示将C#全部替换为GO
print(r)
r2 = re.sub('C#', 'GO', a, 3) #第四个参数 count=3,表示字符串中最多替换3次
print(r2)
#PythonGOJavaPHPGOGOGO
#PythonGOJavaPHPGOGOC#
replace() 函数替换:
import re
a = 'PythonC#JavaPHPC#C#C#'
a.replace('C#', 'Go') #没有替换是因为string不可变
print(a)
a = a.replace('C#', 'Go') #重新赋值,可以实现替换
print(a)
#PythonC#JavaPHPC#C#C#
#PythonGoJavaPHPGoGoGo
replace函数可以说是 re.sub 的简单代替,第三个参数是 count
re.sub() 一个强大的功能:第二个参数传入一个函数:
import re
def convert(value):
pass
a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', convert, a)
print(r)
#PythonJavaPHP
这里处理过后的 C# 都消失了,原因是:re.sub()的第二个参数传入一个函数时,它的流程是:当匹配到第一个 C# 时,会把匹配的结果传入这个函数中,也就是 C# 会作为 convert函数的value传入,convert 函数的返回结果回用于替换原字符串中的 C#
因此如上例子中,convert函数并未返回,所以C# 全部消失
但是要注意,此处传入的 C# ,并不是简单的以字符串的方式传入,而是作为一个对象,打印如下:
import re
def convert(value):
print(value) #匹配到4次,调用4次
a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', convert, a)
print(r)
# <_sre.SRE_Match object; span=(6, 8), match='C#'> #span表示当前匹配的字符在原字符串中的位置
# <_sre.SRE_Match object; span=(15, 17), match='C#'>
# <_sre.SRE_Match object; span=(17, 19), match='C#'>
# <_sre.SRE_Match object; span=(19, 21), match='C#'>
# PythonJavaPHP
使用对象 value 的 group 方法可以可以取出对应字符串,给匹配到的 C# 前后加上感叹号 :
import re
def convert(value):
matched = value.group()
return '!!' + matched + '!!'
a = 'PythonC#JavaPHPC#C#C#'
r = re.sub('C#', convert, a)
print(r)
#Python!!C#!!JavaPHP!!C#!!!!C#!!!!C#!!
re.sub() 传入函数非常实用,特别是在对匹配到的字符做进一步操作时
把函数作为参数传递
找出字符串中的所有数字,大于等于 6 时替换为 9,小于 6 替换为 0:
import re
a = 'A8C3467290'
def convert(value):
matched = value.group()
if int(matched) >= 6: #注意字符串转为 int
return '9' #返回值必须是string
else:
return '0'
r = re.sub('\d', convert, a) #每找到一个数字,就调用convert一次
print(r)
#A9C0099090
这种函数调用函数的方式非常灵活
练习:传入的字符串字母和数字夹杂,数字分为一位和两位,要求剔除所有一位的数字,剩下的大于等于50 替换为99,小于50 替换为0:
import re
a = 'A83C12D1D8E67'
def convert(value):
matched = value.group()
if int(matched) >= 50:
return '99'
elif int(matched) < 10:
return ''
else:
return '0'
r = re.sub('\d{1,2}', convert, a) #匹配一到两位数字
print(r)
#A99C0DDE99
search与match函数
区别与联系:
- match 从字符串的开始就要匹配
- 搜索需要匹配的字符串,直到找到第一个满足需要的字符串,把这个对象返回回来
- 都会在匹配到第一个时,就停止搜索,不会继续往下匹配
- match 和 search 两者返回的均是对象,但是 findall 更为简便,返回的是匹配到的 列表,推荐使用 findall
import re
a = 'A83C12D1D8E67'
r = re.match('\d', a) #第一个字符不是数字,返回None
print(r)
r2 = re.search('\d', a)
print(r2)
b = '1234' #第一个字符改为数字,可返回一个对象
r3 = re.match('\d', b)
print(r3)
#None
#<_sre.SRE_Match object; span=(1, 2), match='8'>
#<_sre.SRE_Match object; span=(0, 1), match='1'>
可以看出,match 和 search 两者返回的均是对象,但是 findall 更为简便,返回的是匹配到的 列表
group() 函数 和 span() 函数:
import re
a = 'A83C12D1D8E67'
r2 = re.search('\d', a)
print(r2.group()) #返回对象中的字符
print(r2.span()) #返回字符的位置
b = '1234'
r3 = re.match('\d', b)
print(r3.group())
print(r3.span())
# 8
# (1, 2)
# 1
# (0, 1)
group分组
提取 life 和 python 之间的字符:
import re
a = 'life is short, i use python'
r = re.search('life(.*)python', a)
#.* 表示除换行符以外的字符(包括空格)重复0到无限次
print(r.group(1))
# is short, i use
对于 'life.*python' 如果不加括号,默认为 '(life.*python)' ,即默认是一个组
group函数可以传入参数,表示要获取的组号,当传入 group(0) 时就是默认情况,即获取完整匹配结果
但是要访问完整匹配结果内部的分组的话,必须从 1 开始访问;因此这里使用 'life(.*)python',对 .* 加括号,group(1) 就可以获取到中间的字符
用search 完成有些难懂,使用 findall 更为简便:
import re
a = 'life is short, i use python'
r = re.findall('life(.*)python', a)
print(r)
#[' is short, i use ']
这在Python爬虫书写中,读取HTML标签中间数据中很常用
更加复杂的字符串处理,返回 life 和 python 之间、python 和 python 之间的字符:
import re
a = 'life is short, i use python, i love python'
r = re.search('life(.*)python(.*)python', a) #两个组
print(r.group(0))
print(r.group(1))
print(r.group(2))
print(r.group(0,1,2)) #以元祖形式返回三个结果
print(r.groups()) #返回两个组
# life is short, i use python, i love python
# is short, i use
# , i love
# ('life is short, i use python, i love python', ' is short, i use ', ', i love ')
# (' is short, i use ', ', i love ')
一些关于学习正则的建议
python绝大多数用在爬虫上,需要用到正则表达式
搜索'常用正则表达式'并加以分析
理解JSON
JavaScript Object Notation 的简写:JavaScript 对象标记
本质上,JSON 是一种轻量级的数据 交换格式
- 与 XML 相比较,JSON 是轻量级的
- 注意 JSON 是一种数据格式
字符串 是JSON的表现形式
符合JSON 格式的字符串叫做JSON字符串,例如 {"name" : "julia"} 就是标准的 JSON 格式,和python中的字典对应
JSON 优势:
- 易于阅读
- 易于解析
- 网络传输效率高
- 适合于跨语言交换数据
JSON 的应用场景:
简单的网站web场景下:
较为复杂的移动端场景:
我们要理解,JSON 本身只是一种语言格式,它是一种格式、规范,并不是实实在在的东西;JSON 的载体是JSON字符串;并且 JSON 并不是某一个语言所特有的,可以作为各个语言之间的数据交互。
反序列化
利用python 内部的 json 解析JSON 数据:
import json
json_str = '{"name":"julia", "age":18}' #JSON字符串
student = json.loads(json_str)
print(type(student))
print(student)
print(student['name']) #访问数据项
print(student['age'])
#<class 'dict'>
#{'name': 'julia', 'age': 18}
#julia
#18
JSON 在python中对应 字典 的格式,要注意:
- JSON规定内部的key必须用双引号 "julia"
- value为字符串时,使用双引号,int型不加双引号
- 这样一来,需要解析的python 字符串外部加 单引号
- json.load 函数解析 python字符串,解析之后变为 字典 格式
在python,对于 json 字符串会解析为字典;其他语言中不一定,对应于 json 中的数据结构对应的是 object
另外json 还有 array 类型:
import json
json_str = '[{"name":"julia", "age":18}, {"name":"julia", "age":18}]'
student = json.loads(json_str) #load函数把json数据类型转化为python的数据类型
print(type(student))
print(student)
#<class 'list'>
#[{'name': 'julia', 'age': 18}, {'name': 'julia', 'age': 18}]
json中的数据结构 array 对应python中的列表,每一项是object(对应python中的字典)
反序列化:由字符串到某一种语言对象的解析过程
json到Python对应的数据转换类型:
序列化
序列化:python数据类型 转化为 json字符串,如下所示:
import json
student = [
{'name':'julia', 'age':18, 'flag':False},
{'name':'julia', 'age':20}
]
json_str = json.dumps(student)
print(json_str)
print(type(json_str))
#[{"name": "julia", "age": 18, "flag": false}, {"name": "julia", "age": 20}]
#<class 'str'>
序列化不仅仅局限于json与python之间的数据转化,还有XML与python之间
小谈JSON、JSON对象与JSON字符串
JSON 与 JavaScript区别(没有直接关系):
- JSON 并不是特定于 JavaScript的,并不是JavaScript的附属品
- JavaScript只是 标准ECMASCRIPT的实现方式之一,另外还有实现方式 ActionScript,我们可以理解 JSON 也是ECMASCRIPT 的实现版本之一,某种程度上JavaScript 和 JSON 可以看成平齐的语言
- JSON 大量应用于 JavaScript 的交互
- json有自己的数据类型,虽然他和JavaScript的数据类型有些相似
JSON 对象:只是放在 JavaScript当中时,JSON 对象是成立的;
JSON字符串:只放在 JavaScript当中
JSON:
REST 服务的标准形式:JSON