问题描述:
笔者在初学Python爬虫时,用到 urllib.request.urlopen 获取百度搜索页面 (http://www.baidu.com) 上的信息。
首先,访问百度并获取网页信息,将信息保存在 response 中。代码如下:
from urllib.request import urlopen
url = r'http://www.baidu.com'
response = urlopen(url)
然后,调用 .read() 看一下 response 的内容,发现 charset=utf-8,于是用 utf-8 解码。代码如下:
print(response.read())
'''
结果:
b'<!DOCTYPE html><!--STATUS OK-->\n\n\n
<html><head><meta http-equiv="Content-Type" content="text/html;
charset=utf-8">
......'
'''
最后,用 utf-8 解码 response 的内容,并把解码后的内容保存到 百度.html 中。代码如下:
with open(r'百度.html','w') as f:
f.write(response.read().decode('utf-8'))
然而,打开 百度.html 后却发现其一片空白???
现在打印一下 response.read().decode(‘utf-8’),居然也是空的???
print(response.read().decode('utf-8'))
''' 结果:b'' '''
探索原因:
首先,检查一下 response.read() 的内容是从哪一步开始变为空的。代码如下:
from urllib.request import urlopen
url = r'http://www.baidu.com'
response = urlopen(url)
print('1','-'*100,'\n',response.read(),sep='')
print('2','-'*100,'\n',response.read(),sep='')
with open(r'百度.html','w') as f:
print('3','-'*100,'\n',response.read(),sep='')
f.write(response.read().decode('utf-8'))
print('4','-'*100,'\n',response.read(),sep='')
'''
结果:
1----------------------------------------------------------------------------------------------------
b'<!DOCTYPE html><!--STATUS OK-->\n\n\n <html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8">......
2----------------------------------------------------------------------------------------------------
b''
3----------------------------------------------------------------------------------------------------
b''
4----------------------------------------------------------------------------------------------------
b''
'''
发现,在 response.read() 写入 百度.html 前, response.read() 就已经变为空的了,即 response 的第一次 .read() 一切顺利,但是第二次 .read() 就变为空的了。
于是,我们探索一下 response.read()。
通过查看帮助文档,发现response.read 只有一个参数 amt:当 amt=None/负数 时,会读取所有字节;当 amt=正数k 时,会读取 k 个字节。
from urllib.request import urlopen
url = r'http://www.baidu.com'
response = urlopen(url)
print('1','-'*100,'\n',response.read(amt=20),sep='')
print('2','-'*100,'\n',response.read(amt=40),sep='')
response = urlopen(url)
print('3','-'*100,'\n',response.read(amt=60),sep='')
'''
结果:
1----------------------------------------------------------------------------------------------------
b'<!DOCTYPE html><!--S'
2----------------------------------------------------------------------------------------------------
b'TATUS OK-->\n\n\n <html><head><meta http'
3----------------------------------------------------------------------------------------------------
b'<!DOCTYPE html><!--STATUS OK-->\n\n\n <html><head><meta http'
'''
可以看到,在第一步解读 response 的前 20 个字节后,第二步解读的 40 个字节,是指 response 的第 21-60 个字节,而非前 40 个。第三步重新获取 response ,并解读其前 60 个字节,刚好是 第一步 与 第二步 的拼接。
因此,response.read() 解读了 response 的所有字节,再次使用 response.read() 时才会得到空的结果。
解决方案:
确定解码类型时,可以先打开网页源码查看 charset=utf-8,然后直接将 response.read().decode(‘utf-8’) 写入 百度.html。代码如下:
from urllib.request import urlopen
url = r'http://www.baidu.com'
response = urlopen(url)
with open(r'百度.html','w') as f:
f.write(response.read().decode('utf-8'))
如有不当之处,烦请指出!