Python:将大型网页保存到文件

5
让我先说一下,我不是新手程序员,但对Python非常陌生。我使用urllib2编写了一个程序,请求一个网页并将其保存到文件中。这个网页大约有300KB,虽然我觉得并不算特别大,但似乎足以给我带来麻烦,所以我称之为“大型”网页。我使用简单的调用从urlopen返回的对象直接复制到文件中: file.write(webpage.read()) 但它会花费数分钟的时间,试图将内容写入文件,最终我会收到以下错误信息:
Traceback (most recent call last):
  File "program.py", line 51, in <module>
    main()
  File "program.py", line 43, in main
    f.write(webpage.read())
  File "/usr/lib/python2.7/socket.py", line 351, in read
    data = self._sock.recv(rbufsize)
  File "/usr/lib/python2.7/httplib.py", line 541, in read
    return self._read_chunked(amt)
  File "/usr/lib/python2.7/httplib.py", line 592, in _read_chunked
    value.append(self._safe_read(amt))
  File "/usr/lib/python2.7/httplib.py", line 649, in _safe_read
    raise IncompleteRead(''.join(s), amt)
httplib.IncompleteRead: IncompleteRead(6384 bytes read, 1808 more expected)

我不知道为什么这会让程序如此困扰?

编辑 |

这是我获取页面的方法

jar = cookielib.CookieJar()

cookie_processor = urllib2.HTTPCookieProcessor(jar);

opener = urllib2.build_opener(cookie_processor)
urllib2.install_opener(opener)

requ_login = urllib2.Request(LOGIN_PAGE,
                             data = urllib.urlencode( { 'destination' : "", 'username' : USERNAME, 'password' :  PASSWORD } ))

requ_page = urllib2.Request(WEBPAGE)    
try:
    #login
    urllib2.urlopen(requ_login)

    #get desired page
    portfolio = urllib2.urlopen(requ_page)
except urllib2.URLError as e:
    print e.code, ": ", e.reason

可能与http.client.IncompleteRead有关。 - Terry Li
有几件事情需要隔离一下...如果你只是读入一个数组,我假设你在不进行任何文件写入的情况下也有同样的问题。另外,如果你指定了一个最大的读取大小,比如read(500000),会怎么样? - TJD
也许这可以帮助你:http://bobrochel.blogspot.com/2010/11/bad-servers-chunked-encoding-and.html - César
1
我按照下面的建议使用了shutil.copyfileobj,现在似乎可以工作了。你有什么想法为什么会这样? - Justin Smith
我按照下面的建议使用了shutil.copyfileobj,现在似乎可以工作了。有什么想法吗?我也很好奇 :) - Piotr Dobrogost
显示剩余2条评论
1个回答

5
我会使用由shutil模块提供的方便的文件对象复制功能。它在我的机器上运行良好 :)
>>> import urllib2
>>> import shutil
>>> remote_fo = urllib2.urlopen('http://docs.python.org/library/shutil.html')
>>> with open('bigfile', 'wb') as local_fo:
...     shutil.copyfileobj(remote_fo, local_fo)
... 
>>> 

更新:您可能想将第三个参数传递给copyfileobj,以控制用于传输字节的内部缓冲区的大小。

更新2:shutil.copyfileobj没有任何花哨的东西。它只是从源文件对象中读取一块字节并重复写入目标文件对象,直到没有更多内容可读。以下是我从Python标准库内部获取的实际源代码:

def copyfileobj(fsrc, fdst, length=16*1024):
    """copy data from file-like object fsrc to file-like object fdst"""
    while 1:
        buf = fsrc.read(length)
        if not buf:
            break
        fdst.write(buf)

谢谢,我用了这个方法,现在似乎可以工作了。你有什么想法,copyfileobj是如何不同地工作的,使它更有效吗? - Justin Smith
嗨@JustinSmith,再看一下我的答案,我已经更新了更多的信息。copyfileobj并没有做什么特别深奥的事情。只是将字节一块一块地复制到目标文件中。 - Pavel Repin
这仍然没有解释原始代码有什么问题。 - Piotr Dobrogost
1
实际上,真正的问题是由于文件大小过大,请求超时了。我增加了超时时间,之后一切都正常了。 - Justin Smith
我使用了这种方法来复制远程二进制文件。(例如,图像文件。) - DavidRR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接