在Python中将域名转换为IDN

10 投票
2 回答
15769 浏览
提问于 2025-04-16 04:24

我有一长串域名,需要生成一些报告。这个列表里有一些国际化域名(IDN),虽然我知道怎么在命令行里用Python把它们转换:

>>> domain = u"pfarmerü.com"
>>> domain
u'pfarmer\xfc.com'
>>> domain.encode("idna")
'xn--pfarmer-t2a.com'
>>> 

但是我在用一个小脚本从文本文件读取数据时遇到了困难。

#!/usr/bin/python

import sys

infile = open(sys.argv[1])

for line in infile:
    print line,
    domain = unicode(line.strip())
    print type(domain)
    print "IDN:", domain.encode("idna")
    print

我得到的输出是:

$ ./idn.py ./test 
pfarmer.com
<type 'unicode'>
IDN: pfarmer.com

pfarmerü.com
Traceback (most recent call last):
  File "./idn.py", line 9, in <module>
    domain = unicode(line.strip())
UnicodeDecodeError: 'ascii' codec can't decode byte 0xfc in position 7: ordinal not in range(128)

我也尝试过:

#!/usr/bin/python

import sys
import codecs

infile = codecs.open(sys.argv[1], "r", "utf8")

for line in infile:
    print line,
    domain = line.strip()
    print type(domain)
    print "IDN:", domain.encode("idna")
    print

结果是:

$ ./idn.py ./test       
Traceback (most recent call last):
  File "./idn.py", line 8, in <module>
    for line in infile:
  File "/usr/lib/python2.6/codecs.py", line 679, in next
    return self.reader.next()
  File "/usr/lib/python2.6/codecs.py", line 610, in next
    line = self.readline()
  File "/usr/lib/python2.6/codecs.py", line 525, in readline
    data = self.read(readsize, firstline=True)
  File "/usr/lib/python2.6/codecs.py", line 472, in read
    newchars, decodedbytes = self.decode(data, self.errors)
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 0-5: unsupported Unicode code range

这是我的测试数据文件:

pfarmer.com
pfarmerü.com

我现在非常清楚自己需要理解Unicode。

谢谢,

彼得

2 个回答

2

你的第一个例子是可以的,不过有一点需要注意:

domain = unicode(line.strip())

你需要在这里指定一个特定的编码方式:unicode(line.strip(), 'utf-8')。否则,系统会使用默认的编码方式,而默认的编码是7位ASCII,这样就会出错。你也可以用line.strip().decode('utf-8')来写,和knitti的例子是一样的,这两种写法的效果是没有区别的。

不过根据错误信息“can't decode byte 0xfc”,我觉得你的test文件可能没有保存为UTF-8格式。这可能就是为什么第二个例子看起来也没问题,但实际上却失败的原因。

实际上,它可能是ISO-8859-1编码,或者是非常相似的Windows代码页1252。如果这个文件是从西方Windows系统的文本编辑器中生成的,那肯定是后者;而现在Linux系统默认使用的是UTF-8编码。你要么确保将文件保存为UTF-8格式,要么使用编码'cp1252'来读取这个文件。

22

你需要知道你的文件是用什么编码保存的。比如说,它可能是 'utf-8'(这不是 Unicode)或者 'iso-8859-1'、'cp1252' 之类的。

然后你可以这样做(假设是 'utf-8'):


infile = open(sys.argv[1])

for line in infile:
    print line,
    domain = line.strip().decode('utf-8')
    print type(domain)
    print "IDN:", domain.encode("idna")
    print

decode 把编码的字符串转换成 Unicode。用 encode 把 Unicode 转换成字符串。如果你尝试对已经编码过的内容再进行编码,Python 会先尝试解码,默认使用的编码是 'ascii',这对于非 ASCII 的值会失败。

撰写回答