使用 Python 请求获取 html?

Get html using Python requests?

我正在尝试自学一些基本的网络抓取。使用 Python 的请求模块,我能够为各种网站获取 html,直到我尝试了这个:

>>> r = requests.get('http://www.wrcc.dri.edu/WRCCWrappers.py?sodxtrmts+028815+por+por+pcpn+none+mave+5+01+F')

我得到了:

而不是作为此页面来源的基本 html
>>> r.text
'\x1f\ufffd\x08\x00\x00\x00\x00\x00\x00\x03\ufffd]o\u06f8\x12\ufffd\ufffd\ufffd+\ufffd]...

>>> r.content
b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xed\x9d]o\xdb\xb8\x12\x86\xef\xfb+\x88]\x14h...

我已经尝试了 get/post 的多种组合以及我可以从文档、SO 和其他示例中猜到的每种语法。我不明白我在上面看到的是什么,没能把它变成任何我能读懂的东西,也不知道如何得到我真正想要的东西。我的问题是,如何获得上述页面的 html?

有问题的服务器正在给你一个 gzipped 响应。服务器也非常崩溃;它发送以下 headers:

$ curl -D - -o /dev/null -s -H 'Accept-Encoding: gzip, deflate' http://www.wrcc.dri.edu/WRCCWrappers.py?sodxtrmts+028815+por+por+pcpn+none+mave+5+01+F
HTTP/1.1 200 OK
Date: Tue, 06 Jan 2015 17:46:49 GMT
Server: Apache
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd"><html xmlns="http: //www.w3.org/1999/xhtml" lang="en-US">
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 3659
Content-Type: text/html

<!DOCTYPE..> 不是有效的 HTTP header。因此,Server 之后剩余的 header 秒被 忽略 。为什么服务器会插入尚不清楚;在很可能的情况下 WRCCWrappers.py 是一个 CGI 脚本,它不输出 headers 但在 doctype 行之后包含一个双换行符,欺骗 Apache 服务器在那里插入额外的 headers。

因此,requests 也没有检测到数据是 gzip-encoded。数据都在那里,你只需要解码它。或者你可以,如果它不是相当不完整的话。

work-around是告诉服务器不要压缩:

headers = {'Accept-Encoding': 'identity'}
r = requests.get(url, headers=headers)

并返回未压缩的响应。

顺便说一句,在 Python 2 上,HTTP header 解析器并不那么严格,并且设法声明文档类型 a header:

>>> pprint(dict(r.headers))
{'<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en" "dtd/xhtml1-transitional.dtd"><html xmlns="http': '//www.w3.org/1999/xhtml" lang="en-US">',
 'connection': 'Keep-Alive',
 'content-encoding': 'gzip',
 'content-length': '3659',
 'content-type': 'text/html',
 'date': 'Tue, 06 Jan 2015 17:42:06 GMT',
 'keep-alive': 'timeout=5, max=100',
 'server': 'Apache',
 'vary': 'Accept-Encoding'}

并且 content-encoding 信息保留下来,因此 requests 会如预期的那样为您解码内容。

此 URL 的 HTTP headers 现已修复。

>>> import requests
>>> print requests.__version__
2.5.1
>>> r = requests.get('http://www.wrcc.dri.edu/WRCCWrappers.py?sodxtrmts+028815+por+por+pcpn+none+mave+5+01+F')
>>> r.text[:100]
u'\n<!DOCTYPE html>\n<HTML>\n<HEAD><TITLE>Monthly Average of Precipitation, Station id: 028815</TITLE></H'
>>> r.headers
{'content-length': '3672', 'content-encoding': 'gzip', 'vary': 'Accept-Encoding', 'keep-alive': 'timeout=5, max=100', 'server': 'Apache', 'connection': 'Keep-Alive', 'date': 'Thu, 12 Feb 2015 18:59:37 GMT', 'content-type': 'text/html; charset=utf-8'}

我会用更简单的方法解决这个问题。只需导入 html 库即可解码 HTML 个特殊字符:

import html

r = requests.get('http://www.wrcc.dri.edu/WRCCWrappers.py?sodxtrmts+028815+por+por+pcpn+none+mave+5+01+F')

print(html.unescape(r.text))

这里是一个使用 BeautifulSoup 库的例子。它“使从网页中抓取信息变得容易。”

from bs4 import BeautifulSoup

import requests

# request web page
resp = requests.get("http://example.com")

# get the response text. in this case it is HTML
html = resp.text

# parse the HTML
soup = BeautifulSoup(html, "html.parser")

# print the HTML as text
print(soup.body.get_text().strip())

结果

Example Domain
This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.
More information...