Как решить проблему с разбором html файла с кириллицей? - PullRequest
3 голосов
/ 15 ноября 2010

У меня есть html-файл с элементами span:

<html>
<body>
<span class="one">Text</span>some text</br>
<span class="two">Привет</span>Текст на русском</br>
</body>
</html>

Чтобы получить "некоторый текст":

# -*- coding:cp1251 -*-
import lxml
from lxml import html

filename = "t.html"
fread = open(filename, 'r')
source = fread.read()

tree = html.fromstring(source)
fread.close()


tags = tree.xpath('//span[@class="one" and text()="Text"]') #This OK
print "name: ",tags[0].text
print "value: ",tags[0].tail

tags = tree.xpath('//span[@class="two" and text()="Привет"]') #This False

print "name: ",tags[0].text
print "value: ",tags[0].tail

Это шоу:

name: Text
value: some text

Traceback: ... in line `tags = tree.xpath('//span[@class="two" and text()="Привет"]')`
    ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes

Как решить эту проблему?

Ответы [ 4 ]

4 голосов
/ 24 октября 2012

Попробуйте это

tree = html.fromstring(source.decode('utf-8'))

и

tags = tree.xpath('//span[@class="two" and text()="%s"]' % u'Привет' )
4 голосов
/ 15 ноября 2010

LXML

(Как уже отмечалось, это немного неестественно между системными кодировками и, по-видимому, не работает должным образом в Windows XP, хотя это работало в Linux.)

Я заставил его работать, расшифровав исходную строку - tree = html.fromstring(source.decode('utf-8')):

# -*- coding:cp1251 -*-
import lxml
from lxml import html

filename = "t.html"
fread = open(filename, 'r')
source = fread.read()

tree = html.fromstring(source.decode('utf-8'))
fread.close()


tags = tree.xpath('//span[@class="one" and text()="Text"]') #This OK
print "name: ",tags[0].text
print "value: ",tags[0].tail

tags = tree.xpath('//span[@class="two" and text()="Привет"]') #This is now OK too

print "name: ",tags[0].text
print "value: ",tags[0].tail

Это означает, что фактическое дерево - это все unicode объектов. Если вы просто поместите параметр xpath как unicode, он найдет 0 совпадений.

BeautifulSoup

Я все равно предпочитаю использовать BeautifulSoup для любых подобных вещей. Вот мой интерактивный сеанс; Я сохранил файл в cp1251.

>>> from BeautifulSoup import BeautifulSoup
>>> filename = '/tmp/cyrillic'
>>> fread = open(filename, 'r')
>>> source = fread.read()
>>> source  # Scary
'<html>\n<body>\n<span class="one">Text</span>some text</br>\n<span class="two">\xcf\xf0\xe8\xe2\xe5\xf2</span>\xd2\xe5\xea\xf1\xf2 \xed\xe0 \xf0\xf3\xf1\xf1\xea\xee\xec</br>\n</body>\n</html>\n'
>>> source = source.decode('cp1251')  # Let's try getting this right.
u'<html>\n<body>\n<span class="one">Text</span>some text</br>\n<span class="two">\u041f\u0440\u0438\u0432\u0435\u0442</span>\u0422\u0435\u043a\u0441\u0442 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c</br>\n</body>\n</html>\n'
>>> soup = BeautifulSoup(source)
>>> soup  # OK, that's looking right now. Note the </br> was dropped as that's bad HTML with no meaning.
<html>
<body>
<span class="one">Text</span>some text
<span class="two">Привет</span>Текст на русском
</body>
</html>

>>> soup.find('span', 'one').findNextSibling(text=True)
u'some text'
>>> soup.find('span', 'two').findNextSibling(text=True)  # This looks a bit daunting ...
u'\u0422\u0435\u043a\u0441\u0442 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u043e\u043c'
>>> print _  # ... but it's not, really. Just Unicode chars.
Текст на русском
>>> # Then you may also wish to get things by text:
>>> print soup.find(text=u'Привет').findParent().findNextSibling(text=True)
Текст на русском
>>> # You can't get things by attributes and the contained NavigableString at the same time, though. That may be a limitation.

В конце концов, возможно, стоит подумать о попытке source.decode('cp1251') вместо source.decode('utf-8'), когда вы берете его из файловой системы. Тогда может действительно работать lxml.

1 голос
/ 18 апреля 2011

Я получил ту же ошибку при создании XML с помощью lxml. Найденное решение здесь: http://lethain.com/stripping-illegal-characters-from-xml-in-python/

Я только что сделал:

remove_re = re.compile(u'[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]')
etree_sub_el.text = remove_re.sub('', text)
0 голосов
/ 15 ноября 2010

Не проверял, но это должно сделать обращение к tags[0].tail во встроенной функции unicode (): unicode(tags[0].tail)

...