#=========================结构化文件存储====================
# 结构数据: 先有的结构,在谈数据
# JSON文件
# JSON Path
# 转换成Python类型进行操作(json类)
# XML文件
# 转换成python类型(xmltodict)
# XPath
# CSS选择器
# 正则
# 非结构化数据:先有数据,再谈结构
# 文本
# 电话号码
# 邮箱地址
# 通常处理此类数据,使用正则表达式
# Html文件
# 正则
# XPath
# CSS选择器
#===========================XML文件===========================
# 参考资料
# https://docs.python.org/3/library/xml.etree.elementtree.html
# http://www.runoob.com/python/python#xml.html
# https://blog.youkuaiyun.com/seetheworld518/article/details/49535285
# XML(eXtensibleMarkupLanguage), 可扩展标记语言
# 标记语言: 语言中使用尖括号括起来的文本字符串标记
# 可扩展:用户可以自己定义需要的标记
# 是w3c组织制定的一个标准
# XML描述的是数据本身,即数据的结构和语义
# HTML侧重于如何显示web页面中的数据
# XML文档的构成
#例:
# 处理指令(可以认为一个文件内只有一个处理指令)
# 最多只有一行
# 且必须在第一行
# 内容是与xml本身处理起相关的一些声明或者指令
# 以xml关键字开头
# 一般用于声明XML的版本和采用的编码
# version属性是必须的
# encoding属性用来支出xml解释器使用的编码
<?xml version="1.0" encoding="utf-8" ?>
# 根元素(一个文件内只有一个根元素)
# 在整个xml文件中,可以把他看作一个树形结构
# 根元素有且只能有一个
<root_element attribute="a"> # 属性
# 子元素
<element attribute="b">
# 内容
# 表明标签所存储的信息
<name>content</name>
</element>
<element>
# 注释,起说明作用的信息
# 注释不能嵌套在标签里
<name> <!-- content --> </name> #可以
<name <!-- content -->> </name> #不可以,注释在标签内
# 只有在注释的开始和结尾使用双短横线
<!--c-o-te-nt--> #可以,注释内容可以有一个短横线
<!--c--o--te--nt-->#不可以,双短横线只能出现在开头或结尾
# 三短横线只能出现在注释的开头而不能用在结尾
<!---con-tent--> #可以, 三短横线只能出现在开头
<!---con-tent---> #不可以, 三短横线只能出现在开头
</element>
</root_element>
# 保留字符的处理
# XML中使用的符号可能跟实际符号相冲突,典型的就是左右尖括号
# 使用实体引用(EntityReference)来表示保留字符
<score> score>80 </score> #有错误,xml中不能出现">"
<score> score>80</score> #使用实体引用
# 把含有保留字符的部分放在CDATA块内部,CDATA块把内部信息视为不需要转义
<![CDATA[
select name,age
from Student
where score>80
]]>
# 常用的需要转移的保留字符和对应实体引用,以&开头并且以分号结尾:
# & : &
# < : <
# > : >
# ' : '
# " : "
# XML标签的命名规则
# Pascal命名法
# 用单词表示,第一个字母大写
# 大小写严格区分
# 配对的标签必须一致
# 命名空间,为了防止命名冲突
<Student>
<Name>LiuYing</Name>
<Age>23</Age>
</Student>
<Room>
<Name>2014</Name>
<Location>1#23#1</Location>
</Room>
# 如果归并上述两个内容信息,会产生冲突
<Schooler>
<Name>LiuYing</Name>
<Age>23</Age>
<Name>2014</Name>
<Location>1#23#1</Location>
</Schooler>
# 为了避免冲突,需要给可能冲突元素添加命名空间
# xmlns: xml name space 的缩写
<Schooler xmlns:student="http://my_student" xmlns:room="http://my_room">
<student:Name>LiuYing</student:Name>
<Age>23</Age>
<room:Name>2014</room:Name>
<Location>1#23#1</Location>
</Schooler>
# XML访问## 读取
# XML读取分两个主要技术,SAX, DOM
# SAX(Simple API for XML):
# 基于事件驱动的API
# 利用SAX解析文档设计到解析器和事件处理两部分
# 特点:
# 快
# 流式读取
# DOM
# 是W3C规定的XML编程接口
# 一个XML文件再缓存中以树形结构保存,读取
# 用途
# 定位浏览XML任何一个节点信息
# 添加删除相应内容
# minidom
# minidom.parse(filename):加载读取的xml文件, filename也可以是xml代码
# doc.documentElement:获取xml文档对象,一个xml文件只有一个对于的文档对象
# node.getAttribute(attr_name):获取xml节点的属性值
# node.getElementByTagName(tage_name):得到一个节点对象集合
# node.childNodes:得到所有孩子节点
# node.childNodes[index].nodeValue:获取单个节点值
# node.firstNode:得到第一个节点,等价于node.childNodes[0]
# node.attributes[tage_name]
# 实例:
import xml.dom.minidom
# 负责解析xml文件
from xml.dom.minidom import parse
# 使用minidom打开xml文件
DOMTree = xml.dom.minidom.parse("student.xml")
#得到文档对象
doc = DOMTree.documentElement
# 显示子元素
for ele in doc.childNodes:
if ele.nodeName == "Teacher":
print("-------Node:{0}-----".format(ele.nodeName))
childs = ele.childNodes
for child in childs:
if child.nodeName == "Name":
# data是文本节点的一个属性,表示他的值
print("Name: {0}".format(child.childNodes[0].data))
if child.nodeName == "Mobile":
# data是文本节点的一个属性,表示他的值
print("Mobile: {0}".format(child.childNodes[0].data))
if child.nodeName == "Age":
# data是文本节点的一个属性,表示他的值
print("Age: {0}".format(child.childNodes[0].data))
if child.hasAttribute("detail"):
print("Age-detail: {0}".format(child.getAttribute("detail")))
# etree
# 以树形结构来表示xml
# root.getiterator:得到相应的可迭代的node集合
# root.iter
# find(node_name):查找指定node_name的节点,返回一个node
# root.findall(node_name):返回多个node_name的节点
# node.tag: node对应的tagename
# node.text:node的文本值
# node.attrib: 是node的属性的字典类型的内容
#案例:
import xml.etree.ElementTree
root = xml.etree.ElementTree.parse("student.xml")
print("利用getiterator访问:")
nodes = root.getiterator()
for node in nodes:
print("{0}--{1}".format(node.tag, node.text))
print("利用find和findall方法:")
ele_teacher = root.find("Teacher")
print("{0}--{1}".format(ele_teacher.tag, ele_teacher.text))
ele_stus = root.findall("Student")
for ele in ele_stus:
print("{0}--{1}".format(ele.tag, ele.text))
for sub in ele.getiterator():
if sub.tag =="Name":
if "Other" in sub.attrib.keys():
print(sub.attrib['Other'])
# xml文件写入
# 更改
# ele.set:修改属性
# ele.append: 添加子元素
# ele.remove:删除元素
# 实例:
import xml.etree.ElementTree as et
tree = et.parse(r'to_edit.xml')
root = tree.getroot()
for e in root.iter('Name'):
print(e.text)
for stu in root.iter('Student'):
name = stu.find('Name')
if name != None:
name.set( 'test', name.text * 2)
stu = root.find('Student')
#生成一个新的 元素
e = et.Element('ADDer')
e.attrib = {'a':'b'}
e.text = '我加的'
stu.append(e)
# 一定要把修改后的内容写回文件,否则修改无效
tree.write('to_edit.xml')
# 生成创建
# 方法1、SubElement
import xml.etree.ElementTree as et
stu = et.Element("Student1")
name = et.SubElement(stu, 'Name')
name.attrib = {'lang','en'}
name.text = 'maozedong'
age = et.SubElement(stu, 'Age')
age.text = 18
et.dump(stu)
# 方法2、minidom 写入
import xml.dom.minidom
#在内存中创建一个空的文档
doc = xml.dom.minidom.Document()
#创建一个根节点Managers对象
root = doc.createElement('Managers')
#设置根节点的属性
root.setAttribute('company', 'xx科技')
root.setAttribute('address', '科技软件园')
#将根节点添加到文档对象中
doc.appendChild(root)
managerList = [{'name' : 'joy', 'age' : 27, 'sex' : '女'},
{'name' : 'tom', 'age' : 30, 'sex' : '男'},
{'name' : 'ruby', 'age' : 29, 'sex' : '女'}
]
for i in managerList :
nodeManager = doc.createElement('Manager')
nodeName = doc.createElement('name')
#给叶子节点name设置一个文本节点,用于显示文本内容
nodeName.appendChild(doc.createTextNode(str(i['name'])))
nodeAge = doc.createElement("age")
nodeAge.appendChild(doc.createTextNode(str(i["age"])))
nodeSex = doc.createElement("sex")
nodeSex.appendChild(doc.createTextNode(str(i["sex"])))
#将各叶子节点添加到父节点Manager中,
#最后将Manager添加到根节点Managers中
nodeManager.appendChild(nodeName)
nodeManager.appendChild(nodeAge)
nodeManager.appendChild(nodeSex)
root.appendChild(nodeManager)
#开始写xml文档
fp = open('Manager.xml', 'w')
doc.writexml(fp, indent='\t', addindent='\t', newl='\n', encoding="utf-8")
# 方法3、etree创建
import xml.etree.ElementTree as et
#在内存中创建一个空的文档
etree = et.ElementTree()
e = et.Element('Student')
etree._setroot(e)
e_name = et.SubElement(e, 'Name')
e_name.text = "hahahah"
etree.write('v06.xml')
#===========================JSON===========================
# 在线工具
# https://www.sojson.com/
# http://www.w3school.com.cn/json/
# http://www.runoob.com/json/json#tutorial.html
# JSON(JavaScriptObjectNotation)
# 轻量级的数据交换格式,基于ECMAScript
# json格式是一个键值对形式的数据集
# key: 字符串
# value:字符串,数字,列表,json
# json使用大括号包裹
# 键值对直接用都好隔开 student={
"name": "wangdapeng",
"age": 18,
"mobile":"13260446055"
}
# json和python格式的对应
# 字符串:字符串
# 数字:数字
# 队列:list
# 对象:dict
# 布尔值:布尔值
# python for json
#实例
# 导入json包
import json
# 此时student是一个dict格式内容,不是json
student={
"name": "luidana",
"age": 18,
"mobile":"15578875040"
}
# json和python对象的转换
stu_json = json.dumps(student) #对数据编码,把python格式表示成json格式
stu_dict = json.loads(stu_json) #对数据解码,把json格式转换成python格式
# python读取json文件
with open("t.json", 'w') as f:
json.dump(data, f) #把内容写入文件
with open("t.json", 'r') as f:
d = json.load(f) #把json文件内容读入python
print(d)
#===================正则表达式(RegularExpression, re)=====================
# 是一个计算机科学的概念
# 用于使用单个字符串来描述,匹配符合某个规则的字符串
# 常常用来检索,替换某些模式的文本
# 正则的写法
# .(点号):表示任意一个字符,除了\n, 比如查找所有的一个字符 \.
# []: 匹配中括号中列举的任意字符,比如[L,Y,0] , LLY, Y0, LIU
# \d: 任意一个数字
# \D:除了数字都可以
# \s:表示空格,tab键
# \S:除了空白符号
# \w: 单词字符, 就是a-z, A-Z, 0-9, _
# \W: 除了上面单词字符外
# {m,n}:允许前面内容出现最少m次,最多n次
# *: 表示前面内容重复零次或者多次, \w*,等价\w{0,}
# +: 表示前面内容至少出现一次,等价{1,}
# ?: 前面出现的内容零次或者一次,等价{0,1}
# ^:匹配字符串的开始
# $:匹配字符串的结尾
# \b:匹配单词的边界
# ():对正则表达式内容进行分组, 从第一个括号开始,编号逐渐增大
# 例:
# 验证一个数字: ^\d$
# 必须有一个数字,最少一位:^\d+$
# 只能出现数字,且位数为5-10位: ^\d{5,10}$
# 注册者输入年龄,要求16岁以上,99岁以下: ^[16-99]$
# 只能输入英文字符和数字: ^[A-Za-z0-9]$
# 验证qq号码: [0-9]{5,12}
# \A: 只匹配字符串开头, \Aabcd, 则abcd
# \Z: 仅匹配字符串末尾, abcd\Z, abcd
# |: 左右任意一个
# (?P<name>...): 分组,除了原来的编号再制定一个别名, (?P<id>12345){2}, 1234512345
# (?P=name): 引用分组,
#========================RE使用===================
#1、使用compile将表示正则的字符串编译为一个pattern对象
#2、通过pattern对象提供一系列方法对文本进行查找匹配,获得匹配结果,一个Match对象
#3、最后使用Match对象提供的属性和方法获得信息,根据需要进行操作# Re常用函数
#实例:
import re
# r表示字符不转义
# re.l表示忽略大小写
p = re.compile(r'([a-z]+) ([a-z]+)', re.l)
# 在字符串中查找,按照规则进行查找
# 返回结果是None,表示没有找到,否则返回Match对象
# 参数3,6表示字符串中查找的范围
# 查找到的结果只包含一个,表示第一次
m = p.match("ljfa lkjf324 a2j43d afass gt4df", 3, 26)
m.group() #表示匹配的结果
m.start(0) #表示匹配的结果从哪个位置出发的
m.end(0) #表示匹配的结果从哪个位置结束的
m.span(0) # 返回匹配成功的整个子串的跨度
# 正则常用方法:
# match(str): 从开始位置开始查找,一次匹配
# search(str, [pos, [endpos]]):从任何位置查找,一次匹配,pos和endpos表示起止位置
# findall: 全部匹配,返回列表, 案例v26
# finditer: 全部匹配,返回迭代器, 案例v26
# split: 分割字符串,返回列表
# sub:替换
f = p.findall("ljfa lkjf324 a2j43d afass gt4df")
f.group(0) #表示整个匹配的结果
f.group(1) #表示第一组的结果
f.start(1)
f.end(1))
f.span(1) # 返回匹配成功的第一个子串的跨度
#表示所有组的结果
f.groups() #返回所有子串,tuple类型,等价于m.gourp(1), m.group(2)..
# sub替换找到的文本
import re
p = re.compile(r'(\w+) (\w+)')
s = "aad dfa ggg dfd"
rst = p.sub(r'替换的文本', s)
# 匹配中文
#大部分中文内容表示范围是[u4e00-u9fa5],不包含全角标点
re.compile(r'[\u4e00-\u9fa5]+')
# 贪婪和非贪婪
# 贪婪:尽可能多的匹配,用"*"表示
# 非贪婪:找到符合条件的最小内容即可,用"?"表示
# 正则默认使用贪婪匹配
# 例如:
# 查找文本abbbbbbccc
# re是 ab*
# 贪婪模式: 结果是abbbbbb
# 非贪婪: 结果是a
# 默认贪婪
re.compile(r".*")
# 非贪婪
re.compile(r".*?")
#========================XPath=====================
# 在XML文件中查找信息的一套规则/语言,根据XML的元素或者属性进行遍历
# 参见:http://www.w3school.com.cn/xpath/index.asp
# XPath 开发工具
# 开源的XPath表达式编辑工具:XMLQuire
# Chrome插件:XPath Helper
# Firefox插件: XPath Checker
# 选取节点
Nodename # 选取此节点的所有子节点
/ # 从根节点开始选取
例:/Student # 没有结果
例:/School # 选取School节点
// # 选取节点,不考虑位置
例://age # 选取出三个节点,一般组成列表返回
. # 选取当前节点
.. # 选取当前节点的父节点
@ # 选取属性
# xpath中查找一般按照路径方法查找,一下是路径表示方法
School/Teacher # 返回Teacher节点
School/Student # 返回两个Student节点
//Student # 选取所有Studetn的节点,不考虑位置
School//Age # 选取School后代中所有Age节点
//@Other # 选取Other属性
//Age[@Detail] # 选取带有属性Detail的Age元素
# 谓语-Predicates
/School/Student[1] # 选取School下面的第一个Student节点
/School/Student[last()] # 选取School下面的最后一个Student节点
/School/Student[last()-1] # 选取School下面的倒数第二个Student节点
/School/Student[position()<3] # 选取School下面的前二个Student节点
//Student[@score] # 选取带有属性score的Student节点
//Student[@score="99"] # 选取带有属性score并且属性值是99的Student节点
//Student[@score]/Age # 选取带有属性score的Student节点的子节点Age
# 通配符
# `*` : 任何元素节点
# @*: 匹配任何属性节点
# node(): 陪陪任何类型的节点
# 选取多个路径
# //book/tile | //book/author : 选取book元素中的title和author元素
# //tile | //price: 选取文档中所有的title和price元素
# 其余不常见XPath运算符号包括+, - , *, div, >, < # lxml库,etree
# python的HTML/XML的解析器
# 官方文档: http://lxml.de/index.html
# 功能:
# 解析HTML,修正HTML代码:
# 利用etree.HTML把字符串解析成HTML文档
text = 'xxxxxxxxx'
html = etree.HTML(text)
s = etree.tostring(html)
# 文件读取如下案例
# etree和XPath的配合使用:
from lxml import etree
# 只能读取xml格式内容,html报错
html = etree.parse("./x.xml")
# etree.tostring(html, pretty_print=True)
rst = html.xpath('//book')
# xpath的意识是,查找带有category属性值为sport的book元素
rst = html.xpath('//book[@category="sport"]')
# xpath的意识是,查找带有category属性值为sport的book元素下的year元素
rst = html.xpath('//book[@category="sport"]/year')
rst = rst[0]
print(type(rst))
print(rst.tag) #属性名
print(rst.text) #属性值
#====================CSS选择器 BeautifulSoup4===================
# http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/
# 几个常用提取信息工具的比较:
# 正则: 很快,不好用,无需安装
# beautifulsoup:慢,使用简单,安装简单
# lxml: 比较快,使用简单,安装一般
# 四大对象
# Tag
# NavigableString
# BeautifulSoup
# Comment
# Tag
# 对应Html中的标签
# 可以通过soup.tag_name
# tag两个重要属性
# name
# attrs
# NavigableString
# 对应内容值
# BeautifulSoup
# 表示的是一个文档的内容,大部分可以把他当做tag对象
# 一般我们可以用soup来表示
# Comment
# 特殊类型的NavagableString对象,
# 对其输出,则内容不包括注释符号
# 四大对象案例
from urllib import request
from bs4 import BeautifulSoup
url = "http://xxxxxxx"
rsp = request.urlopen(url)
content = rsp.read()
soup = BeautifulSoup(content, 'lxml')
print(soup.prettify()) # bs自动转码,得到所有html
print(soup.tag_name) #html里的tag_name如head/link/...第一个标签
print(soup.tag_name.name) #tag_name标签里的标签名称
print(soup.tag_name.attrs) #tag_name标签里的所有属性,返回字典类型
print(soup.tag_name.attrs['type']) #tag_name标签里的type属性值
print(soup.tag_name.string]) #tag_name标签里的内容
print(soup.name) #返回document
print(soup.attrs)
# 遍历文档对象
# contents: tag的子节点以列表的方式给出
# children: 子节点以迭代器形式返回
# descendants: 所子孙节点
# string
# 搜索文档对象
# find_all(name, attrs, recursive, text, ** kwargs)
# name:按照那个字符串搜索,可以传入的内容为
# 字符串
# 正则表达式
# 列表
# kewwortd参数,可以用来表示属性
# text: 对应tag的文本值
tags = soup.find_all(re.compile('^me'), content="always")
for tag in tags:
print(tag)
# css选择器
# 使用soup.select, 返回一个列表
# 通过标签名称: soup.select("title")
# 通过类名: soup.select(".content")
# id查找: soup.select("#name_id")
# 组合查找: soup.select("div #input_content")
# 属性查找: soup.select("img[class='photo'])
# 获取tag内容: tag.get_text
titles = soup.select("title")
print(titles[0])
metas = soup.select("meta[content='always']")
print(metas[])