Python 3 的 UTF-8 编码问题
UTF-8 encoding issue with Python 3
我上周 Python 写了 a Wikipedia scraper。
它抓取法语页面,所以我必须管理 UTF-8 编码以避免错误。我在脚本的开头用这些行来做到这一点:
#!/usr/bin/python
# -*- coding: utf-8 -*-
我也像这样对抓取的字符串进行编码:
adresse = monuments[1].get_text().encode('utf-8')
我的第一个脚本在 Python 2.7 上运行得非常好,但我为 Python 3 重写了它(尤其是为了使用 urllib.request)并且 UTF-8 不再工作了。
抓取前几个元素后出现这些错误:
File "scraper_monu_historiques_ge_py3.py", line 19, in <module>
url = urllib.request.urlopen(url_ville).read() # et on ouvre chacune d'entre elles
File "/usr/lib/python3.4/urllib/request.py", line 153, in urlopen
return opener.open(url, data, timeout)
File "/usr/lib/python3.4/urllib/request.py", line 455, in open
response = self._open(req, data)
File "/usr/lib/python3.4/urllib/request.py", line 473, in _open
'_open', req)
File "/usr/lib/python3.4/urllib/request.py", line 433, in _call_chain
result = func(*args)
File "/usr/lib/python3.4/urllib/request.py", line 1217, in https_open
context=self._context, check_hostname=self._check_hostname)
File "/usr/lib/python3.4/urllib/request.py", line 1174, in do_open
h.request(req.get_method(), req.selector, req.data, headers)
File "/usr/lib/python3.4/http/client.py", line 1090, in request
self._send_request(method, url, body, headers)
File "/usr/lib/python3.4/http/client.py", line 1118, in _send_request
self.putrequest(method, url, **skips)
File "/usr/lib/python3.4/http/client.py", line 975, in putrequest
self._output(request.encode('ascii'))
UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' in position 58: ordinal not in range(128)
我不明白为什么,因为它在 Python 2.7 中运行良好...我发布了 a version of this WIP on Github。
您正在将包含非 ASCII 字符的字符串传递给 urllib.urlopen
,这不是有效的 URI(但它是有效的 IRI 或国际资源标识符)。
在将 IRI 传递给 urlopen
之前,您需要使 IRI 成为有效的 URI。这件事的具体
取决于 IRI 的哪一部分包含非 ASCII 字符:域部分应使用 Punycode, while the path should use percent-encoding.
进行编码
由于您的问题完全是由于包含 Unicode 字符的路径,假设您的 IRI 存储在变量 iri
中,您可以使用以下方法修复它:
import urllib.parse
import urllib.request
split_url = list(urllib.parse.urlsplit(iri))
split_url[2] = urllib.parse.quote(split_url[2]) # the third component is the path of the URL/IRI
url = urllib.parse.urlunsplit(split_url)
urllib.request.urlopen(url).read()
但是,如果您可以避免 urllib
并可以选择使用 requests
库,我建议您这样做。该库更易于使用,并具有自动 IRI 处理功能。
我上周 Python 写了 a Wikipedia scraper。
它抓取法语页面,所以我必须管理 UTF-8 编码以避免错误。我在脚本的开头用这些行来做到这一点:
#!/usr/bin/python
# -*- coding: utf-8 -*-
我也像这样对抓取的字符串进行编码:
adresse = monuments[1].get_text().encode('utf-8')
我的第一个脚本在 Python 2.7 上运行得非常好,但我为 Python 3 重写了它(尤其是为了使用 urllib.request)并且 UTF-8 不再工作了。
抓取前几个元素后出现这些错误:
File "scraper_monu_historiques_ge_py3.py", line 19, in <module>
url = urllib.request.urlopen(url_ville).read() # et on ouvre chacune d'entre elles
File "/usr/lib/python3.4/urllib/request.py", line 153, in urlopen
return opener.open(url, data, timeout)
File "/usr/lib/python3.4/urllib/request.py", line 455, in open
response = self._open(req, data)
File "/usr/lib/python3.4/urllib/request.py", line 473, in _open
'_open', req)
File "/usr/lib/python3.4/urllib/request.py", line 433, in _call_chain
result = func(*args)
File "/usr/lib/python3.4/urllib/request.py", line 1217, in https_open
context=self._context, check_hostname=self._check_hostname)
File "/usr/lib/python3.4/urllib/request.py", line 1174, in do_open
h.request(req.get_method(), req.selector, req.data, headers)
File "/usr/lib/python3.4/http/client.py", line 1090, in request
self._send_request(method, url, body, headers)
File "/usr/lib/python3.4/http/client.py", line 1118, in _send_request
self.putrequest(method, url, **skips)
File "/usr/lib/python3.4/http/client.py", line 975, in putrequest
self._output(request.encode('ascii'))
UnicodeEncodeError: 'ascii' codec can't encode character '\xe9' in position 58: ordinal not in range(128)
我不明白为什么,因为它在 Python 2.7 中运行良好...我发布了 a version of this WIP on Github。
您正在将包含非 ASCII 字符的字符串传递给 urllib.urlopen
,这不是有效的 URI(但它是有效的 IRI 或国际资源标识符)。
在将 IRI 传递给 urlopen
之前,您需要使 IRI 成为有效的 URI。这件事的具体
取决于 IRI 的哪一部分包含非 ASCII 字符:域部分应使用 Punycode, while the path should use percent-encoding.
由于您的问题完全是由于包含 Unicode 字符的路径,假设您的 IRI 存储在变量 iri
中,您可以使用以下方法修复它:
import urllib.parse
import urllib.request
split_url = list(urllib.parse.urlsplit(iri))
split_url[2] = urllib.parse.quote(split_url[2]) # the third component is the path of the URL/IRI
url = urllib.parse.urlunsplit(split_url)
urllib.request.urlopen(url).read()
但是,如果您可以避免 urllib
并可以选择使用 requests
库,我建议您这样做。该库更易于使用,并具有自动 IRI 处理功能。