IncompleteRead используя httplib
У меня была постоянная проблема получения RSS-канала с определенного веб-сайта. Я закончил писать довольно уродливую процедуру для выполнения этой функции, но мне любопытно, почему это происходит и правильно ли справляются с этой проблемой интерфейсы более высокого уровня. Эта проблема на самом деле не является пробкой шоу, так как мне не нужно часто извлекать канал.
Я прочитал решение, которое ловит исключение и возвращает частичное содержимое, но так как неполное чтение различаясь количеством байтов, которые фактически извлекаются, я не уверен, что такое решение действительно будет работать.
#!/usr/bin/env python
import os
import sys
import feedparser
from mechanize import Browser
import requests
import urllib2
from httplib import IncompleteRead
url = 'http://hattiesburg.legistar.com/Feed.ashx?M=Calendar&ID=543375&GUID=83d4a09c-6b40-4300-a04b-f88884048d49&Mode=2013&Title=City+of+Hattiesburg%2c+MS+-+Calendar+(2013)'
content = feedparser.parse(url)
if 'bozo_exception' in content:
print content['bozo_exception']
else:
print "Success!!"
sys.exit(0)
print "If you see this, please tell me what happened."
# try using mechanize
b = Browser()
r = b.open(url)
try:
r.read()
except IncompleteRead, e:
print "IncompleteRead using mechanize", e
# try using urllib2
r = urllib2.urlopen(url)
try:
r.read()
except IncompleteRead, e:
print "IncompleteRead using urllib2", e
# try using requests
try:
r = requests.request('GET', url)
except IncompleteRead, e:
print "IncompleteRead using requests", e
# this function is old and I categorized it as ...
# "at least it works darnnit!", but I would really like to
# learn what's happening. Please help me put this function into
# eternal rest.
def get_rss_feed(url):
response = urllib2.urlopen(url)
read_it = True
content = ''
while read_it:
try:
content += response.read(1)
except IncompleteRead:
read_it = False
return content, response.info()
content, info = get_rss_feed(url)
feed = feedparser.parse(content)
Как уже говорилось, это не критическая проблема миссии, но любопытство, так как, хотя я могу ожидать, что urllib2 будет иметь эту проблему, я удивлен, что эта ошибка встречается в механизации и запросах. Модуль feedparser даже не выдает ошибку, поэтому проверка на наличие ошибок зависит от наличия "bozo_exception" ключ.
Edit: я просто хотел упомянуть, что wget и curl выполняют функцию безупречно, каждый раз правильно извлекая полную полезную нагрузку. Мне еще предстоит найти чистый метод python для работы, за исключением моего уродливого Хака, и мне очень любопытно узнать, что происходит на бэкэнде httplib. В шутку я решил также попробовать это с twill на днях и получил ту же ошибку httplib.
2 ответов
в конце концов, все остальные модули (feedparser
, mechanize
и urllib2
) вызов httplib
где создается исключение.
теперь, во-первых, я также загрузил это с wget, и полученный файл был 1854 байт. Затем я попробовал с urllib2
:
>>> import urllib2
>>> url = 'http://hattiesburg.legistar.com/Feed.ashx?M=Calendar&ID=543375&GUID=83d4a09c-6b40-4300-a04b-f88884048d49&Mode=2013&Title=City+of+Hattiesburg%2c+MS+-+Calendar+(2013)'
>>> f = urllib2.urlopen(url)
>>> f.headers.headers
['Cache-Control: private\r\n',
'Content-Type: text/xml; charset=utf-8\r\n',
'Server: Microsoft-IIS/7.5\r\n',
'X-AspNet-Version: 4.0.30319\r\n',
'X-Powered-By: ASP.NET\r\n',
'Date: Mon, 07 Jan 2013 23:21:51 GMT\r\n',
'Via: 1.1 BC1-ACLD\r\n',
'Transfer-Encoding: chunked\r\n',
'Connection: close\r\n']
>>> f.read()
< Full traceback cut >
IncompleteRead: IncompleteRead(1854 bytes read)
таким образом, он читает все 1854 байта, но затем думает, что еще впереди. Если мы явно скажем ему читать только 1854 байта, он работает:
>>> f = urllib2.urlopen(url)
>>> f.read(1854)
'\xef\xbb\xbf<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">...snip...</rss>'
очевидно, это полезно только в том случае, если мы всегда заранее знаем точную длину. Мы можем использовать тот факт, что частичное чтение возвращается как атрибут исключения, чтобы захватить все содержимое:
>>> try:
... contents = f.read()
... except httplib.IncompleteRead as e:
... contents = e.partial
...
>>> print contents
'\xef\xbb\xbf<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">...snip...</rss>'
этот блог предполагает, что это ошибка сервера, и описывает, как обезьяна-патч httplib.HTTPResponse.read()
метод try..except
блок выше, чтобы обрабатывать вещи за кулисами:
import httplib
def patch_http_response_read(func):
def inner(*args):
try:
return func(*args)
except httplib.IncompleteRead, e:
return e.partial
return inner
httplib.HTTPResponse.read = patch_http_response_read(httplib.HTTPResponse.read)
я применил патч, а потом feedparser
работал:
>>> import feedparser
>>> url = 'http://hattiesburg.legistar.com/Feed.ashx?M=Calendar&ID=543375&GUID=83d4a09c-6b40-4300-a04b-f88884048d49&Mode=2013&Title=City+of+Hattiesburg%2c+MS+-+Calendar+(2013)'
>>> feedparser.parse(url)
{'bozo': 0,
'encoding': 'utf-8',
'entries': ...
'status': 200,
'version': 'rss20'}
это не самый лучший способ делать вещи, но, кажется, это работает. Я недостаточно эксперт в протоколах HTTP, чтобы сказать наверняка, делает ли сервер что-то неправильно, или httplib
неправильно обрабатывает крайний случай.
я узнаю в моем случае, отправьте запрос HTTP / 1.0, исправьте проблему, просто добавив это в код:
import httplib
httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'
после того как я сделать запрос :
req = urllib2.Request(url, post, headers)
filedescriptor = urllib2.urlopen(req)
img = filedescriptor.read()
после того, как я вернусь к http 1.1 с (для соединений, поддерживающих 1.1):
httplib.HTTPConnection._http_vsn = 11
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.1'