使用ElementTree和Requests进行XML解析

3

我正在尝试使用Yahoo天气API,但是我在解析API响应的XML时遇到了一些问题。我正在使用Python 3.4。以下是我正在使用的代码:

weather_url = 'http://weather.yahooapis.com/forecastrss?w=%s&u=%s'
url = weather_url % (zip_code, units)

try:
    rss = parse(requests.get(url, stream=True).raw).getroot()

    conditions = rss.find('channel/item/{%s}condition' % weather_ns)

    return {
        'current_condition': conditions.get('text'),
        'current_temp': conditions.get('temp'),
        'title': rss.findtext('channel/title')
    }
except:
    raise

以下是我得到的堆栈跟踪:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/home/jonathan/PycharmProjects/pyweather/pyweather/pyweather.py", line 42, in yahoo_conditions
    rss = parse(requests.get(url, stream=True).raw).getroot()
  File "/usr/lib/python3.4/xml/etree/ElementTree.py", line 1187, in parse
    tree.parse(source, parser)
  File "/usr/lib/python3.4/xml/etree/ElementTree.py", line 598, in parse
    self._root = parser._parse_whole(source)
  File "<string>", line None
xml.etree.ElementTree.ParseError: not well-formed (invalid token): line 1, column 0

xml.etree.ElementTree的解析函数不支持requests库返回的原始对象。稍微深入了解一下,这个原始对象解析为

>>> r = requests.get('http://weather.yahooapis.com/forecastrss?w=2502265', stream=True)
>>> r.raw
<requests.packages.urllib3.response.HTTPResponse object at 0x7f32c24f9e48>

我参考了这个解决方案,但仍然导致相同的问题。为什么上面的方法不起作用?是urllib3响应对象不支持ElementTree.parse函数吗?我已经阅读了所有文档,但没有任何启示。

文档列表在这里:

编辑: 经过更多的实验,我仍然没有找到上述问题的解决方案。然而,我发现了一个变通方法。如果使用ElementTree的fromstring方法来处理XML内容,一切都能正常工作。

def fetch_xml(url):
    """
    Fetch a url and parse the document's XML.

    :param url: the URL that the XML is located at.
    :return: the root element of the XML.
    :raises:
        :requests.exceptions.RequestException: Requests could not open the URL.
        :xml.etree.ElementTree.ParseError: xml.etree.ElementTree failed to parse the XML document.
    """

    return ET.fromstring(requests.get(url).content)

我想这种方法的缺点就是它使用了更多的内存。你认为呢?我希望能听到社区的意见。

2个回答

2

你为什么要使用流式传输来下载一些RSS XML数据呢?你想一直保持连接吗?天气变化不会那么快,所以为什么不每5分钟轮询一次服务呢?

以下是使用BeautifulSoup和requests进行轮询和解析的完整代码。简短而精悍。

import requests
from bs4 import BeautifulSoup

r = requests.get('http://weather.yahooapis.com/forecastrss?w=%s&u=%s' % (2459115, "c"))
if r.status_code == 200:
    soup = BeautifulSoup(r.text)
    print("Current condition: ", soup.find("description").string)
    print("Temperature: ", soup.find('yweather:condition')['temp'])
    print("Title: ", soup.find("title").string)
else:
    r.raise_for_status()

输出:

Current condition:  Yahoo! Weather for New York, NY
Temperature:  28
Title:  Yahoo! Weather - New York, NY

使用Beautifulsoup可以完成很多事情。查阅其优秀的文档了解更多。


我在使用API获取原始数据时,必须将'stream=True'打开。具体请参见此处的文档。我的第二个解决方案更加优雅且完全可用。感谢您提供的库,我会好好研究的! - Jonathan

2
如果您在XML内容上使用ElementTree的fromstring方法,一切都可以正常工作。
def fetch_xml(url):
    """
    Fetch a url and parse the document's XML.

    :param url: the URL that the XML is located at.
    :return: the root element of the XML.
    :raises:
        :requests.exceptions.RequestException: Requests could not open the URL.
        :xml.etree.ElementTree.ParseError: xml.etree.ElementTree failed to parse the XML document.
    """

    return ET.fromstring(requests.get(url).content)

我想这种方法的缺点在于它会使用更多的内存。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接