我一直有一个长期存在的问题正从一个特定网站的RSS feed。 彼时我写一个比较难看的程序来执行此功能,但我很好奇,为什么发生这种情况和任何更高级别的接口是否妥善处理这一问题。 这个问题是不是一个真正的显示塞,因为我并不需要经常检索饲料。
我已阅读,捕获异常,并返回部分内容,但由于不完整的读入根据实际检索的字节数不同,我没有把握,这样的解决方案将实际工作的解决方案。
#!/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”键的存在。
编辑:我只是想提一提,既wget和卷曲完美执行的功能,每次正确检索全部有效载荷。 我还没有找到一个纯Python方法工作,除了我的丑陋的黑客攻击,我很好奇,想知道什么是对httplib的的后台发生。 在云雀,我决定也与斜纹前些天尝试这一点,得到了同样的错误httplib的。
PS有一件事也令我很奇怪。 该IncompleteRead在有效载荷两个断点的一个持续发生。 看来,feedparser和要求读926个字节失败后,又机械化和阅读1854个字节之后的urllib2失败。 此行为consistend,而我离开没有任何解释或理解。
在一天结束时,所有其它模块的( 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'
我已经使用HTTPS而不是HTTP和它工作正常固定的问题。 在无需更改代码。