从技术上讲,这些不是有效的 URL,但它们是有效的 IRI (Internationalized Resource Identifiers),如 RFC 3987 中所定义。
将 IRI 编码为 URI 的方式是:
例如(取自链接的维基百科文章),这个 IRI:
https://en.wiktionary.org/wiki/Ῥόδος
…映射到这个 URI:
https://en.wiktionary.org/wiki/%E1%BF%AC%CF%8C%CE%B4%CE%BF%CF%82
我相信requests 可以开箱即用地处理这些问题(尽管只是最近,而且直到 3.0 才提供“部分支持”,我不确定这意味着什么)。我很确定 Python2.7 中的 urllib2 不会,而 Python 3.6 中的 urllib.request 可能也不会。
无论如何,如果您选择的 HTTP 库不处理 IRI,您可以手动处理:
def iri_to_uri(iri):
p = urllib.parse.urlparse(iri)
path = urllib.parse.quote_from_bytes(p.path.encode('utf-8'))
p = [:2] + (path,) + p[3:]
return urllib.parse.urlunparse(p2)
还有许多第三方库可以处理 IRI,它们大多是从 Twisted 和 Amara 等其他项目中分离出来的。可能值得在 PyPI 中搜索一个而不是自己构建它。
或者您可能想要一个更高级别的库,例如 hyperlink 来处理 RFC 3987 中的所有复杂问题(以及 RFC 3986,当前版本的 URI 规范 - requests 2.x 和Python 3.6 stdlib 句柄非常正确)。
如果您必须手动处理 IRI,则很有可能您还必须处理 IDN Internationalized Domain Names 来代替 ASCII 域名,即使从技术上讲它们是不相关的规范。所以你可能想做这样的事情:
def iri_to_uri(iri):
p = urllib.parse.urlparse(iri)
netloc = p.netloc.encode('idna').decode('ascii')
path = urllib.parse.quote_from_bytes(p.path.encode('utf-8'))
p = [:1] + (netloc, path) + p[3:]
return urllib.parse.urlunparse(p2)