数据编码与处理
一、读写CSV数据
(1)使用csv库处理CSV数据
import csv with open('./stock.csv') as f: f_csv = csv.reader(f) headers = next(f_csv) for row in f_csv: # process row
由于每一行的row是个列表,访问需要用row[0]、row[1],
(2)可以考虑转换成命名元组访问。
import csv from collections import namedtuple with open('./stock.csv') as f: f_csv = csv.reader(f) headers = next(f_csv) Row = namedtuple('Row',headers) for r in f_csv: row = Row(*r) # process row
(3)转换为字典
import csv with open('./stock.csv') as f: f_csv = csv.DictReader(f) for row in f_csv: # process row
写入CSV数据:
import csv headers = ['Symbol', 'Price', 'Date', 'Time', 'Change', 'Volume'] rows = [ ('AA', '39.48', '6/11/2007', '9:34am', '-0.18', '428900'), ('BB', '48.54', '8/25/2001', '19:57am', '-0.44', '142800'), ('CC', '92.13', '3/18/1886', '3:11am', '-0.67', '126700'), ('DD', '79.25', '2/05/1999', '8:22am', '-0.27', '110000'), ] with open('stock2.csv','w') as f: f_csv = csv.writer(f) f_csv.writerow(headers) f_csv.writerows(rows)
如果数据是字典序列,那么可以这样处理:
import csv headers = ['Symbol', 'Price', 'Date', 'Time', 'Change', 'Volume'] rows = [ {'Symbol':'AA','Price':39.48,'Date':'6/11/2007', 'Time':'9:34am', 'Change':-0.18, 'Volume':428900} ] with open('stock2.csv','w') as f: f_csv = csv.DictWriter(f, headers) f_csv.writeheader() f_csv.writerows(rows)
标题行出现非法字符,需要进行转换。
import re with open('./stock.csv') as f: f_csv = csv.reader(f) headers = [ re.sub('[^a-zA-Z_]', '_', h) for h in next(f_csv)]
读取数据时,将部分数据转换成除字符串之外的类型。
import csv,re col_type = [str,float,str,str,float,str] with open('./stock.csv') as f: f_csv = csv.reader(f) headers = [ re.sub('[^a-zA-Z_]', '_', h) for h in next(f_csv)]
for row in f_csv: row = tuple( convert(value)for convert, value in zip(col_type, row) )
字段转化成字典:
field_type = [ ('Price',float), ('Change',float), ('Volume',int), ] with open('./stock.csv') as f: for row in csv.DictReader(f): row.update( (key,convert(row[key])) for key, convert in field_type) print(row)
二、读写JSON数据
(1)字符串形式:json.dumps()、json.loads()
(2)文件形式:json.dump()、json.load()
(3)使用pprint()函数,合理格式输出 或者 在json.dumps()函数中使用indext参数
>>> from urllib.request import urlopen
>>> pprint(json_resp)
>>> print(json.dumps(data, indent=4))
(4)load时解码为OrderDict有序字典
>>> from collections import OrderedDict
>>> data = json.loads(s, object_pairs_hook=OrderedDict)
(5)JSON字典转变为Python对象
class JSONObject: def __init__(self,d): self.__dict__ = d >>> data = json.loads(s, object_hook=JSONObject) >>> data.name
(6)序列化类实例,提供一个函数作为输入并返回一个可以被序列化处理的字典。
def serialize_instance(obj): obj = { '__classname__' : type(obj).__name__ } obj.update(vars(obj)) return obj class Point: def __init__(self,x,y): self.x = x self.y = y classes = { 'Point':Point } def unserialize_instance(d): clsname = d.pop('__classname__',None) if clsname: cls = classes[clsname] obj = cls.__new__(cls) for key,value in d.items(): setattr(obj,key,value) return obj return d >>> p = Point(2, 3) >>> s = json.dumps(p, default = serialize_instance) >>> a = json.loads(s, object_hook = unserialize_instance )
三、解析简单的XML文档
xml.etree.ElementTree.parse()函数将整个XML文档解析为一个文档对象。
之后,就可以利用find()、iterfind()、findtext()方法查询特定的XML元素。
(1)指定标签时,需要整体考虑文档的结构。每一个查找操作都是相对于一个起始元素来展开的。
(2)doc.iterfind('channel/item')的调用会查找所有在channel元素之下的item元素。doc代表着文档的顶层。
(3)之后对item.findtext()的调用就相对于已找到的item元素来展开。
(4)每个由ElementTree模块所表示的单个元素都有重要的属性和方法,tag属性包含标签的名称,text属性包含附着的文本,get()方法可以用来提取出属性。
四、以增量的方式解析大型XML文件
从大型XML文档中提取出数据时,使用迭起器和生成器。
<?xml version="1.0" encoding="ISO-8859-1"?> <!-- Edited with XML Spy v2007 (http://www.altova.com) --> <breakfast_menu> <food> <food> <name>Belgian Waffles</name> <price>$5.95</price> <description>two of our famous Belgian Waffles with plenty of real maple syrup</description> <calories>650</calories> </food> <food> <name>Strawberry Belgian Waffles</name> <price>$7.95</price> <description>light Belgian waffles covered with strawberries and whipped cream</description> <calories>900</calories> </food> <food> <name>Berry-Berry Belgian Waffles</name> <price>$8.95</price> <description>light Belgian waffles covered with an assortment of fresh berries and whipped cream</description> <calories>900</calories> </food> <food> <name>French Toast</name> <price>$4.50</price> <description>thick slices made from our homemade sourdough bread</description> <calories>600</calories> </food> <food> <name>Homestyle Breakfast</name> <price>$6.95</price> <description>two eggs, bacon or sausage, toast, and our ever-popular hash browns</description> <calories>950</calories> </food> </food> </breakfast_menu>
iterparse()方法允许我们对XML文档做增量式处理;
迭代器产生出形式为(event, elem)的元组,event是列出的事件,而elem是对应的XML元素。
from xml.etree.ElementTree import iterparse def parse_and_remove(filename, path): tag_stack = [] elem_stack = [] path_parts = path.split('/') doc = iterparse(filename, ('start', 'end')) next(doc) for event, elem in doc: if event == 'start': tag_stack.append(elem.tag) elem_stack.append(elem) elif event == 'end': if tag_stack == path_parts: yield elem elem_stack[-2].remove(elem) try: tag_stack.pop() elem_stack.pop() except IndexError as e: pass data = parse_and_remove('./simple.xml','food/food') for i in data: print('>>>>>>>>>>>>>>>>',i,'<<<<<<<<<<<<<<<')
elem_stack[-2].remove(elme),这一行代码使得之前通过yield产生的元素从它们的父节点中移除。
因此可假设其再也没有任何其他的引用存在,因此该元素被销毁进而可以回收它所占用的内存。
五、将字典转换为XML
xml.etree.ElementTree库同样可以用来创建XML文档。
from xml.etree.ElementTree import Element def dict_to_xml(tag, d): elem = Element(tag) for key, val in d.items(): child = Element(key) child.text = str(val) elem.append(child) return elem >>> s = { 'name':'GGGG','share':'100','price':'23.44'} >>> e = dict_to_xml('stock', s) <Element 'stock' at 0x000001CAFEE80F98>
转换的结果是得到一个Element实例。可以利用xml.etree.ElementTree库中的tostring()函数将其转换为字节串。
>>> from xml.etree.ElementTree import tostring >>> tostring(e) b'<stock><name>GGGG</name><price>23.44</price><share>100</share></stock>'
为元素附加属性,使用set()方式实现。
如果要考虑元素间的顺序,创建OrderedDict有序字典来取代普通的字典。
当创建XML时,倾向于只使用字符串来完成:
def str_to_xml(tag, d): parts = ['<{}>'.format(tag)] for key, val in d.items(): parts.append('<{0}>{1}</{0}>'.format(key, val)) parts.append('</{}>'.format(tag)) return ''.join(parts) >>> e = str_to_xml('stock', s) <stock><name>GGGG</name><price>23.44</price><share>100</share></stock>
当字典中包含有特殊字符时:{ 'name' : '<spam>' }
>>> d = {'name':'<spam>'} >>> tostring(dict_to_xml('stock',d)) b'<stock><name><spam></name></stock>' >>> str_to_xml('stock',d) <stock><name><spam></name></stock>
需要手工对字符做转义处理,可以使用xml.sax.saxutils中的escape()和unescape()函数。
>>> from xml.sax.saxutils import escape,unescape >>> escape('<spam>') '<spam>' >>> unescape(_) '<spam>'
六、解析、修改和重写XML
修改XML文档的结构主要是对父元素进行操作,如果需要移除某个元素,那么就利用它的直接父节点的remove()方法完成。
如果插入或添加新的元素,使用父节点的insert()和append()方法来完成。这些元素也可以使用索引和切片操作来进行操控,比如element[i]或element[i:j]。
>>> from xml.etree.ElementTree impor parse,Element
>>> doc = parse('simple.xml')
>>> root = doc.getroot()
>>> root.remove(root.find('sri'))
>>> root.getchildren().index(root.find('nm')) # 1
>>> e = Element('spam')
>>> e.text = 'This is a test'
>>> root.insert(2, e) # 插入在<nm>...</nm> 后面 ..
>>> doc.write('new_simple.xml', xml_declaration=True)
七、用命名空间来解析XML文档
对包含有命名空间的XML文档进行解析会非常繁琐。XMLNamespaces类的功能只是用来稍微简化一下这个过程,
能够在后续的操作中使用缩短的命名空间名称,而不必去使用完全限定的URI。
class XMLNamespace: def __init__(self, **kwargs): self.namespace = {} for name, uri in kwargs.items(): self.register(name, uri) def register(self,name, uri): self.namespace[name] = '{'+uri+'}' def __call__(self, path): return path.format_map(self.namespace) >>> ns = XMLNamespace(html='http://www.w3.org/1999/xhtml') >>> doc.find(ns('content/{html}/html/{html}/head/{html}/title'))
'Hello World'
正在解析的文本除了命名空间之外的其他高级XML特性,那么最好还是使用lxml库。
九、编码和解码十六进制数字
(1)字节流 =》十六进制数组成的字符串
>>> import binascii
>>> s = b'hello'
>>> h = binascii.b2a_hex(s) b'68656c6c6f'
(2)十六进制数组成的字符串 =》字节流
>>> binascii.a2b_hex(h) b'hello'
(3)base64模块下,字节流 =》十六进制数组成的字符串
>>> import base64
>>> h = base64.bl6encode(s) b'68656C6C6F'
(4)base64模块下,十六进制数组成的字符串 =》字节流
>>> s = base64.bl6decode(h) b'hello'
base64.bl64encode和base64.bl64decode只能对大写形式的十六进制数进行操作,而binascii模块能够处理任意一种情况。
十、Base64编码和解码
采用Base64编码对二进制数据做编码解码操作。
(1)字节串编码
>>> import base64
>>> s = b'hello'
>>> a = base64.b64encode(s) b'aGVsbG8'
(2)解码
>>> base64.n64decode(a) b'hello'