Python - 使用lxml将请求替换为urlib2

时间:2013-09-25 19:51:54

标签: python urllib2 lxml python-requests

我试图在此代码中用urllib2替换requests,只需从页面中提取一些信息即可。我不是100%肯定我应该如何移动库。这就是我到目前为止所犯的错误,我做错了什么?

CODE:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests, sys
from lxml import etree
# import urllib2

# UTF8
reload(sys)
sys.setdefaultencoding("utf-8")

# url = 'http://countrycode.org/Germany'
# opener = urllib2.build_opener()
# opener.addheaders = [('User-agent', 'USERAGENT')]
r = requests.get('http://countrycode.org/Germany')
response = r.text
htmlparser = etree.HTMLParser()
tree = etree.parse(response, htmlparser)

countryCodeXpath = '//*[@id="main_table_blue_2"]/tr[3]/td[2]'
countryCode = tree.xpath(countryCodeXpath)
destCountryCode = countryCode[0].text

print destCountryCode

ERROR:

Traceback (most recent call last):
  File "/home/ubuntu/test.py", line 16, in <module>
    tree = etree.parse(response, htmlparser)
  File "lxml.etree.pyx", line 3196, in lxml.etree.parse (src/lxml/lxml.etree.c:64039)
  File "parser.pxi", line 1549, in lxml.etree._parseDocument (src/lxml/lxml.etree.c:91262)
  File "parser.pxi", line 1578, in lxml.etree._parseDocumentFromURL (src/lxml/lxml.etree.c:91546)
  File "parser.pxi", line 1478, in lxml.etree._parseDocFromFile (src/lxml/lxml.etree.c:90613)
  File "parser.pxi", line 1025, in lxml.etree._BaseParser._parseDocFromFile (src/lxml/lxml.etree.c:87527)
  File "parser.pxi", line 565, in lxml.etree._ParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:83101)
  File "parser.pxi", line 656, in lxml.etree._handleParseResult (src/lxml/lxml.etree.c:84083)
  File "parser.pxi", line 594, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:83379)
IOError: Error reading file '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<SNIP>

3 个答案:

答案 0 :(得分:3)

除了abamert的答案之外,您可以使用原始响应修复此问题:

response = requests.get(<ur>, stream = True)
tree = etree.parse(response.raw, htmlparser)

请参阅请求包文档中的Raw Response Content

这样,请求不应将所有数据都读入text属性,而应将raw响应保留为etree.parse()可读取的类文件对象。

答案 1 :(得分:1)

问题是你用字符串调用parse

在ElementTree API中(无论是stdlib版本,来自PyPI的单独模块,还是lxml实现),parse函数采用文件名或文件:

  

来源可以是以下任何一种:

     
      
  • 文件名/路径
  •   
  • 文件对象
  •   
  • 类文件对象
  •   
  • 使用HTTP或FTP协议的URL
  •   

所以,它试图打开一个名为<!DOCTYPE HTML PU…的文件,当然不存在。

正如文档所说:

  

要从字符串中解析,请改用fromstring()函数。


有一些替代方案。

首先,如上所述,lxml.etree可以为您检索网址。除非你在这里确实需要requests的任何额外功能,否则这将更加简单。它会更快,并且不需要将整个文件读入内存,它甚至允许您自动查找DTD和其他外部引用。正如文档所说:

  

请注意,从文件路径或URL解析通常比从打开的文件对象或类文件对象解析更快。支持从gzip压缩源进行透明解压缩(除非在libxml2中明确禁用)。

或者你可以使用requests流媒体协议来获取类似文件的对象而不是内容,正如塞巴斯蒂安的回答所解释的那样。这将更复杂而不是更少,并且在其他两个选项之间的速度中间......但如果您需要requests的其他功能,并且您无法承担将整个页面保留在内存中,那么它是最佳选择

但是,对于这个小到46K的文件,没有理由避免一次性加载它。

答案 2 :(得分:1)

请求返回字符串,所以首先我们需要将该字符串转换为html:

import requests
from lxml import html

response = requests.get('http://your.url')
parsed_body = html.fromstring(response.text)

来源:http://jakeaustwick.me/python-web-scraping-resource/