python的文本读写和二进制读写
笔者在之前的文档里面提出过,scrapy框架本身存在大文件下载的问题,因此产生了改造scrapy的想法,这里在修改下载模块(FilesPipeline)的时候遇到了文本读写和二进制读写的问题。
(scrapy具体bug见笔者的另外一两博客:scrapy的MemoryError(续),scrapy改造过程中踩过的坑(暂未发表))
上上周结束的时候,实验室的师兄说组里的爬虫系统从应用市场里爬取下来的PE文件(exe和dll)无法再次上传和下载,每次重新下载之后的文件只有几个k大小。我看了他的源代码之后发现,其实也是因为字符串读导致上传的时候发生了截断。
字符串读写和二进制读写 :
python的文件读写模块十分的方便,加上with as表达式这样强大的工具,更是用几行代码就可以完成文件读写的功能。
with open('file.txt','rw') as f:
f.read()
#f.write('hello python')
其中open方法的第二个参数则是允许的文件操作,一般来说为了文件安全,遵循最小操作原则,即需要什么操作,就仅允许哪类操作(《windows via c++》)
而我们今天要关注的是’b’这个关键词,如果open的第二个参数不加’b’这个选项,则被判定为字符串读写,否则视为二进制读写。
这两者是存在区别的(敲黑板):
二进制读是把整个文件按照字节进行读取一直到文件结束(EOF),不考虑其具体内容。
字符串读则是把文件视为一个很长的字符串,需要处理其中的换行符’\n’,结束符’\0’,转义字符等问题。
问题代码复现:
最初的scrapy修改过程中的代码是这样的:
class _ResponseReader(protocol.Protocol):
def __init__(self, finished, txresponse, request, maxsize, warnsize,
fail_on_dataloss,giant_file):
self._finished = finished
self._txresponse = txresponse
self._request = request
self._maxsize = maxsize
self._warnsize = warnsize
self._fail_on_dataloss = fail_on_dataloss
self._fail_on_dataloss_warned = False
self._reached_warnsize = False
self._bytes_received = 0
self._giant_file = giant_file
self.giant_file_path = ""
if giant_file==1:
# self._bodybuf = BytesIO()
self.giant_file_path = self.getabspath(self.file_path(request.url),request.store_path)
dir_name = os.path.dirname(self.giant_file_path)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
f = open(self.giant_file_path,'w')
self._bodybuf = f
else:
self._bodybuf = BytesIO()
注意倒数第四行,这里写入文件的方式是’w’,不是’wb’,因此遇到了PE文件下载之后无法打开,打开之后报错的问题。而修改方法就是改成’wb’。
而上面师兄的问题也是这样的,在进行上传的时候采用了’r’的读入方式,而不是’rb’,这里导致了下载的PE文件只有几个k,无法读写。