给Python中通过urlopen/urlretrieve获取网页的过程中,添加gzip的解压缩支持

之前已经实现了用python获取网页的内容,相关已实现代码为:

try:
        html = urlopen(req)
    except HTTPError, e:
        print 'HTTPErrorerr'
        print e.code
        print e.read()
        return None
    except BadStatusLine:
        print "could not fetch %s" % url
    try:
        '''
        乱码的成因来说,编码问题只是一个方面,使用GB18030确实能够解决。
        另一个造成乱码的原因是压缩格式。
        很多规模较大的网站都是以gzip的压缩格式输出页面的,
        所以在用BS解析之前需要先判断该网页是否经过压缩,
        如果经过压缩则先进行解压操作。完整代码如下:
        '''
        data = html.read()
        soup = BeautifulSoup(data, "lxml")

实际使用中会发现soup的内容是乱码的,就像是代码注释里面说的,html以压缩的格式被传输过来,交给浏览器自动进行解压以后渲染进行展示。
现在我们要做的就是人工进行解压缩。

def gzdecode(data):  
    compressedStream = StringIO.StringIO(data)  
    gziper = gzip.GzipFile(fileobj=compressedStream)    
    data2 = gziper.read()   # 读取解压缩后数据   
    return data2 

但有些页面实际上可能并没有进行过压缩,我们需要判断这个页面是否被压缩过,才决定是否调用这个函数。

实际上,html = urlopen(req)返回的html中就包含了我们需要的信息,html.info()返回的就是服务器返回的信息头,

如果我们可以在里面找到Content-Encoding: gzip就以为着这个页面是被压缩过的。

所以此时我们只需要利用html.info()创建一个dict来查找即可:

keyMap = dict(html.info())
if 'content-encoding' in keyMap and keyMap['content-encoding'] == 'gzip':
    #do something

>
[1]. 传输数据编码:Transfer-Encoding
数据编码,即表示数据在网络传输当中,使用怎么样的保证方式来保证数据是安全成功地传输处理。
可以是分段传输,也可以是不分段,直接使用原数据进行传输。
有效的值为:trunked 和 identity
>
[2]. 传输内容编码:Content-Encoding
内容编码,即整个数据信息是在数据器端经过怎样的编码处理,然后客户端会以怎么的编码来反向处理,以得到原始的内容。
这里的内容编码主要是指压缩编码,即服务器端压缩,客户端解压缩。
可以参考的值为:gzip,compress,deflate和identity。
>
[3]. 传输内容格式:Content-Type
内容格式,即接收的数据最终是以何种的形式显示在浏览器中,可以是一个图片,还是一段文本,或者是一段html。
内容格式额外支持可选参数,charset,即实际内容的字符集。
通过字符集,客户端可以对数据进行解编码,以最终显示可以看得懂的文字(而不是一段byte[]或者是乱码)。

接下来,我还希望能够下载到一些指定的图片,我们直接利用urlretrieve应该就是可以的,但根据返回头的信息显示,即使是图片也是经过gzip压缩的

urllib.urlretrieve(url, filename, reporthook=None,data=None)

参数说明

url:外部或者本地url

filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);

reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。

相比于urlopen,需要创建一个表示远程url的类文件对象,然后像本地文件一样操作这个类文件对象来获取远程数据。
urlretrieve,会直接将远程数据下载到本地。

def autoDownLoad(url,add):#文件下载不完整,自动重新加载
    #print add
    try:
        '''
        我们使用urllib.urlretrieve(url,filename)
        时经常遇到下载到一半时,出现urllib.ContentTooShortError错误。
        这是因为文件下载不完全导致的错误。
        如果发现问题的是诸如图片和音乐文件这一类文件较小的问题,可以很容易使用以下方式解决。
        '''
        a, b = urllib.urlretrieve(url, add)#a表示地址, b表示返回头
        '''
        判断文件数据只返回了部分,导致文件偏小,
        我们进行重新的存储,10240即我们认为如果存储数据小于10K,判定下载接收不完全
        '''
        if os.path.getsize(add) < 10240:    
            autoDownLoad(url,add)
    except urllib.ContentTooShortError:
        print 'Network conditions is not good.Reloading.'
        autoDownLoad(url,add)
    except socket.timeout:
        print 'fetch ', url,' exceedTime '
        try:
            urllib.urlretrieve(url,add)
        except:
            print 'reload failed'

下载以后的图片全部无法进行显示,但根据上面的经验,我们只需要根据返回头判断是否需要解压缩即可,但因为urlretrieve会直接将远程数据下载到本地。所以我们需要对本地数据进行二次处理

a, b = urllib.urlretrieve(url, add)#a表示地址, b表示返回头
        keyMap = dict(b)
        #print keyMap
        #need decode
        if 'content-encoding' in keyMap and keyMap['content-encoding'] == 'gzip':
            print 'need2be decode'
            objectFile = open(add, 'r+')#以读写模式打开
            data = objectFile.read()
            data = gzdecode(data)
            objectFile.seek(0, 0)
            objectFile.write(data)
            objectFile.close()

文件 open 模式:

w:以写方式打开
a:以追加模式打开 (从 EOF 开始, 必要时创建新文件)
r+:以读写模式打开
w+:以读写模式打开 (参见 w )
a+:以读写模式打开 (参见 a )
rb:以二进制读模式打开
wb:以二进制写模式打开 (参见 w )
ab:以二进制追加模式打开 (参见 a )
rb+:以二进制读写模式打开 (参见 r+ )
wb+:以二进制读写模式打开 (参见 w+ )
ab+:以二进制读写模式打开 (参见 a+ )

考虑到效率问题,我们直接对文件做出修改-截断写:

seek(offset,where): where=0从起始位置移动,1从当前位置移动,2从结束位置移动。当有换行时,会被换行截断。seek()无返回值,故值为None。

truncate(n): 从文件的首行首字符开始截断,截断文件为n个字符;无n表示从当前位置起截断;截断之后n后面的所有字符被删除。其中win下的换行代表2个字符大小。

这里考虑的是原来是压缩文件,解压后势必会覆盖掉原来的全部数据,所以直接选择了seek的方式。

`urlopen`是Python标准库中用于打开URL的函数。在Python3中,它可以通过`urllib.request.urlopen()`来调用。 如果你遇到了`urlopen`报错的问题,以下是一些可能的解决方法: 1. 检查URL是否正确:首先确保你要打开的URL是正确的。如果URL不存在或无法访问,那么`urlopen`函数将会报错。 2. 检查网络连接:如果你的网络连接不稳定或中断,那么`urlopen`函数也会报错。确保你的网络连接正常。 3. 添加User-Agent:有些网站需要在请求头中添加User-Agent才能访问。你可以通过在`urllib.request.Request`中添加headers参数来添加User-Agent。示例代码如下: ```python import urllib.request url = "http://www.example.com" req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) response = urllib.request.urlopen(req) ``` 4. 添加超时时间:如果请求时间过长,那么`urlopen`函数也会报错。你可以通过在`urlopen`函数中添加timeout参数来设置超时时间。示例代码如下: ```python import urllib.request url = "http://www.example.com" response = urllib.request.urlopen(url, timeout=5) ``` 5. 检查代理设置:如果你使用了代理服务器来访问网站,那么请确保代理设置正确。你可以通过在`urllib.request.ProxyHandler`中指定代理服务器来设置代理。示例代码如下: ```python import urllib.request proxy_handler = urllib.request.ProxyHandler({'http': 'http://www.example.com:8080/'}) opener = urllib.request.build_opener(proxy_handler) response = opener.open('http://www.example.com') ``` 希望以上方法能帮助你解决`urlopen`报错的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值