2010-09-06 10 views
3

J'utilise le code python suivant pour télécharger des pages Web à partir de serveurs avec la compression gzip:Quel est le problème avec ce format gzip?

url = "http://www.v-gn.de/wbb/" 
import urllib2 
request = urllib2.Request(url) 
request.add_header('Accept-encoding', 'gzip') 
response = urllib2.urlopen(request) 
content = response.read() 
response.close() 

import gzip 
from StringIO import StringIO 
html = gzip.GzipFile(fileobj=StringIO(content)).read() 

Cela fonctionne en général, mais pour l'URL spécifiée échoue avec une exception struct.error. J'obtiens un résultat similaire si j'utilise wget avec un en-tête "Accept-encoding". Cependant, les navigateurs semblent être capables de décompresser la réponse. Donc ma question est: est-ce que je peux obtenir mon code python pour décompresser la réponse HTTP sans avoir recours à la désactivation de la compression en supprimant l'en-tête "Accept-encoding"?

Pour être complet, voici la ligne que j'utilise pour wget:

wget --user-agent="Mozilla" --header="Accept-Encoding: gzip,deflate" http://www.v-gn.de/wbb/ 

Répondre

4

Il semble que vous pouvez appeler readline() sur l'objet gzip.GzipFile, mais read() soulève un struct.error parce que le fichier se termine abruptement.

Depuis readline œuvres (sauf à la fin), vous pourriez faire quelque chose comme ceci:

import urllib2 
import StringIO 
import gzip 
import struct 

url = "http://www.v-gn.de/wbb/" 
request = urllib2.Request(url) 
request.add_header('Accept-encoding', 'gzip') 
response = urllib2.urlopen(request) 
content = response.read() 
response.close() 
fh=StringIO.StringIO(content) 
html = gzip.GzipFile(fileobj=StringIO.StringIO(content)) 
try: 
    for line in html: 
     line=line.rstrip() 
     print(line) 
except struct.error: 
    pass 
3

J'ai couru la commande que vous avez spécifié. Il a téléchargé une donnée gzip-ed dans index.html. J'ai rebaptisé index.html en index.html.gz. J'ai essayé gzip -d inedx.html.gz qui mène à une erreur: gzip: index.html.gz: unexpected end of file.

deuxième essai était zcat index.html.gz qui travaillé bien, sauf que, après l'étiquette </html> il imprime la même erreur que ci-dessus.

$ zcat index.html.gz 
... 
    </td> 
</tr> 
</table> 


</body> 
</html> 
gzip: index.html.gz: unexpected end of file 

Le serveur est défectueux.

3

Créez votre gestionnaire en dérivant de urllib2.HTTPHandler et en remplaçant http_open().

import gzip 
from StringIO import StringIO 
import httplib, urllib, urllib2 
class GzipHandler(urllib2.HTTPHandler): 
    def http_open(self, req): 
     req.add_header('Accept-encoding', 'gzip') 
     r = self.do_open(httplib.HTTPConnection, req) 
     if (
      'Content-Encoding'in r.headers and 
      r.headers['Content-Encoding'] == 'gzip' 
     ): 
      fp = gzip.GzipFile(fileobj=StringIO(r.read())) 
     else: 
      fp = r 
     response = urllib.addinfourl(fp, r.headers, r.url, r.code) 
     response.msg = r.msg 
     return respsone 

puis construire votre ouvre-porte. Les différences sont que cette méthode vérifie que le serveur renvoie une réponse gzip et que c'est fait au cours de la requête pas après.

Pour plus d'informations, voir:

+0

Cela ne répond pas vraiment à la question initiale, mais est en effet la bonne façon de le faire. Je suis toujours préoccupé par les serveurs défectueux qui retournent un en-tête "Content-encoding: gzip" mais qui envoient les données brutes. – itsadok

+0

L'avez-vous essayé? Cela a fonctionné pour moi. J'ai récupéré une réponse de longueur 151860 qui n'a pas été compressée. Les autres implémentations affichées ici étaient buggées et je ne les ai pas passées en revue. Peut-être un moment plus tard. –

+0

Oui, le site dans la question initiale semble avoir corrigé leur problème, donc je n'ai pas un bon moyen de tester. Dans les deux cas, le problème serait résolu en utilisant 'readline()' au lieu de 'read()', ce qui est fait en dehors de 'retrieve()' de toute façon. – itsadok