待解决问题描述:
多个XML文件,具有相同的格式,如下:
<?xml version="1.0" encoding="UTF-8"?>
<test name="F000 ">
…
</test>
<test name="F001 ">
…
</test>
<test name="F002 ">
…
</test>
…
不同xml文件中的<test>标签个数不同。有的50个,有的80个。
代码目标:
提取test 标签的 name 属性值,将这些值保存到excel文件中,并为每个值加上日期。生成表格如下:
F000 | 1月6日 |
F001 | 1月6日 |
F002 | 1月6日 |
… | 1月6日 |
说明:每个xml文件会生成一个如上的表格。将所有这些表格保存到同一个excel文件中。
解决步骤:
1. 解析xml
方法一:使用python API SAX .
http://www.runoob.com/python/python-xml.html
方法二:使用xml.dom
Dom 方法将xml文件一次性读取,保存在一个树形结构中,读取文档内容很方便。
2. csv module写excel文件
很多码友分享可以用csv模块处理,将提取的文件写入csv文件,excel会将csv格式转换成表格。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
testname = [‘F000’,’F001’,’F002’]
import csv
#write to file
with open('report.csv', 'w') as csvfile:
spamwriter = csv.writer(csvfile,dialect='excel')
for item in testname:
spamwriter.writerow(item)
csvfile.close()
list 变量testname 保存目标字符串,将其存入到python 的list中。
然后import csv模块写入文件report.csv。
遇到的问题一:中文编码
参考的文章:
http://blog.youkuaiyun.com/andoring/article/details/6575183
http://www.crifan.com/summary_python_2_x_common_string_encode_decode_error_reason_and_solution/
当目标字符串中含有中文时,xml模块读入此字符串后,存为unicode。
用下面方法打印看看:
print isinstance(testname[1], unicode)
打印为True。
尽管在python文件的开头声明了编码方式:# -*- coding: UTF-8 -*- , 中文字符串依然以unicode编码保存。
有意思的是,testname变量是一个list, 它里面的元素被存为unicode, 但如果查看testname 这个list的编码方式,发现打印为False, 即它不是用unicode编码。
查阅python的帮助文档,摘下描述:
The csv module doesn’tdirectly support reading and writing Unicode, but it is 8-bit-clean save forsome problems with ASCII NUL characters. So you can write functions or classesthat handle the encoding and decoding for you as long as you avoid encodingslike UTF-16 that use NULs. UTF-8 is recommended.
说明csv module 不直接处理unicode格式。
这就是为什么调用spamwriter.writerow(item) 方法时报错UnicodeEncodeError的原因。
UnicodeEncodeError: 'ascii' codec can't encode characters in position 5-8: ordinal not in range(128)
使用str 的encode方法将unicode转换为UTF-8后运行成功。
spamwriter.writerow(item .encode('UTF-8'))
成功生成report.csv文件后,用MS office打开,新问题出现——中文乱码。
暂时没找到办法用代码解决,找到一条百度经验,试验有效:
http://jingyan.baidu.com/article/3c48dd3464b46ce10be3581f.html
遗留的问题:notepad中可以正常显示日期:2016/1/30
但如百度经验中操作后,用excel打开却显示: ######## 。在此单元格上双击则正常显示出日期。再换个单元格双击,整个列的日期都正常显示出来。奇怪~~
遇到的问题二:生成的excel文件中多一行空白。
参考的文章:
http://blog.youkuaiyun.com/pfm685757/article/details/47806469
我用的版本是python27, 写成如下就没有问题了。用‘wb’方式打开csv 文件。
with open('report.csv', 'wb') as csvfile:
补充,由于是需要对多个xml文件处理,希望把不同文件中提取出的目标字符串保存到同一个文件中,因使用增量方式写文件,将'wb'改为'ab'
with open('report.csv', 'ab') as csvfile:
3. 用xlrd等excel处理工具。待学习。。。
4. 遍历文件 http://www.jb51.net/article/54640.htm
遇到问题三:对路径的处理。
由于路径中存在’\’, 直接把一个路径赋值给一个变量的时候,需要转义,即为c:\\mypath。要处理该路径下某个文件时,又需要使用其原始的,非转义的路径,即c:\mypath。 处理不当导致错误:
IOError: [Errno 2] No such file or directory: 'D:\\Python27\\works\\RFlogxml\\xmlst.xml'
os.path.join 处理路径,参考下面的文章:
http://blog.youkuaiyun.com/murphykwu/article/details/39785353
例如:在C:\TDDOWNLOAD目录下有a.txt、b.txt两个文件,另有\sub1子文件夹,C:\TDDOWNLOAD\sub1下又有c.txt、d.txt两个文件。
1. os.walk
os.walk()返回一个三元素的tuple:当前路径、子文件夹名称、文件列表。
>>> import os
>>> def fun( path ):
... for root, dirs, files in os.walk( path ):
... for fn in files:
... print root, fn
...
>>> fun( r'C:\TDDOWNLOAD' )
C:\TDDOWNLOAD a.txt
C:\TDDOWNLOAD b.txt
C:\TDDOWNLOAD\sub1 c.txt
C:\TDDOWNLOAD\sub1 d.txt
>>>
2. glob.glob
glob.glob()只接受一个参数,这个参数既代有路径,又代有匹配模式,返回值为一个列表。注意,glob.glob()无法直接穿透子文件夹,需要自己处理:
>>> def fun( path ):
... for fn in glob.glob( path + os.sep + '*' ): # '*'代表匹配所有文件
... if os.path.isdir( fn ): # 如果结果为文件夹
... fun( fn ) # 递归
... else:
... print fn
...
>>> fun( r'C:\TDDOWNLOAD' )
C:\TDDOWNLOAD\a.txt
C:\TDDOWNLOAD\b.txt
C:\TDDOWNLOAD\sub1\c.txt
C:\TDDOWNLOAD\sub1\d.txt
>>>
'*'为匹配模式,代表匹配所有文件,只有这样才能将子文件夹查出来,以便递归深入,探查下一层的文件。
参考如下文章的处理方式:
http://blog.youkuaiyun.com/murphykwu/article/details/39785353
**folderTravel.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import xmlparserDOM as parser
import re
#http://blog.youkuaiyun.com/murphykwu/article/details/39785353
rootdir = os.getcwd()
print('rootdir = ' + rootdir)
for (dirpath, dirnames, filenames) in os.walk(rootdir):
#print('dirpath = ' + dirpath)
for dirname in dirnames:
print('dirname = ' + dirname)
for filename in filenames:
if re.search('.xml', filename):
#print(os.path.join(dirpath, filename))
#xml files and py files are in the same directory
parser.func(filename)
os.getcwd 获取当前文件所在路径,避免指定路径字符串。
用os.walk遍历当前文件夹,获取xml文件名。
因为得到的结果中会列出所有文件包括python脚本文件及xml名件,需要过滤掉py文件,因引入remodule匹配(.xml)文件。
parser.func 为步骤1,2中的处理过程。意思是找到一个xml文件,就按照步骤1,2处理一遍。