lxml.html和Unicode:提取链接

时间:2012-03-22 19:14:31

标签: python html unicode utf-8 lxml

以下代码从网页中提取链接并在浏览器中显示。使用大量UTF-8编码的网页,效果很好。但是法语维基百科页面http://fr.wikipedia.org/wiki/États_unis例如会产生错误。

# -*- coding: utf-8 -*-

print 'Content-Type: text/html; charset=utf-8\n'
print '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Show Links</title>
</head>
<body>'''

import urllib2, lxml.html as lh

def load_page(url):
    headers = {'User-Agent' : 'Mozilla/5.0 (compatible; testbot/0.1)'}
    try:
        req = urllib2.Request(url, None, headers)
        response = urllib2.urlopen(req)
        page = response.read()
        return page
    except:
        print '<b>Couldn\'t load:', url, '</b><br>'
        return None

def show_links(page):
    tree = lh.fromstring(page)
    for node in tree.xpath('//a'):
        if 'href' in node.attrib:
            url = node.attrib['href']
            if '#' in url:
                url=url.split('#')[0]
            if '@' not in url and 'javascript' not in url:
                if node.text:
                    linktext = node.text
                else:
                    linktext = '-'
                print '<a href="%s">%s</a><br>' % (url, linktext.encode('utf-8'))

page = load_page('http://fr.wikipedia.org/wiki/%C3%89tats_unis')
show_links(page)

print '''
</body>
</html>
'''

我收到以下错误:

Traceback (most recent call last):
  File "C:\***\question.py", line 42, in <module>
    show_links(page)
  File "C:\***\question.py", line 39, in show_links
    print '<a href="%s">%s</a><br>' % (url, linktext.encode('utf-8'))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)

我的系统:Python 2.6(Windows),lxml 2.3.3,Apache Server(显示结果)

我做错了什么?

2 个答案:

答案 0 :(得分:2)

您还需要对url进行编码。

问题可能类似于:

>>> "%s%s" % (u"", "€ <-non-ascii char in a bytestring")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in
range(128)

但这有效:

>>> "%s%s" % (u"".encode('utf-8'), "€ <-non-ascii char in a bytestring")
'\xe2\x82\xac <-non-ascii char in a bytestring'

空Unicode字符串强制将整个表达式转换为Unicode。因此,您会看到Unicode Decode 错误。

一般来说,混合Unicode和字节串是个坏主意。它可能看似有效,但迟早会破裂。收到文本后立即将文本转换为Unicode,处理它然后将其转换为I / O的字节。

答案 1 :(得分:1)

lxml返回bytestrings而不是unicode。在编码为utf-8之前,使用页面所服务的任何编码对decode字节串进行unicode可能更好。

如果您的文字已经在utf-8中,则无需进行任何编码或解码 - 只需执行该操作即可。

但是,如果您的linktext类型为unicode(正如您所说),那么它是一个unicode字符串(每个元素代表一个unicode代码点),而编码为utf-8应该可以很好地工作。

我怀疑问题是你的url字符串也是一个unicode字符串,并且在被替换成你的字节字符串之前还需要编码为utf-8。