Уведомления

Группа в Telegram: @pythonsu

#1 Окт. 24, 2012 20:36:50

kappa-sama
Зарегистрирован: 2012-09-21
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

Народ, подскажите…

На странице есть тег <p> вида:

<p xmlns="http://www.w3.org/1999/xhtml">Текст<br/>Текст2<br/>Текст3 и т.д.<br/><p>

сам тег я вытаскиваю с помощью lxml + xpath
doc = lxml.html.document_fromstring(getPage(URL,getCookies(PARAMETERS)))
pages = doc.xpath(PATHPOSTBOX)

Собственно, проблема:
Как вывести содержимое тега? Я пробовал через
pages[0].text
- в этом случае он выводит только до первого “<br/>” (В данном примере только - “Текст”)

Отредактировано kappa-sama (Окт. 24, 2012 20:37:57)

Офлайн

#2 Окт. 24, 2012 21:26:06

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

pages[0].text_content()
Но тогда <br/> будут удалены совсем. Нужно их заменить на ‘\n’ до передачи в lxml.
Или еще есть вариант
texts = doc.xpath('НЕИЗВЕСТНО//text()') 
text = '\n'.join(texts)
но если внутри кроме <br/> есть какие-то тэги, то все будет поделено ‘\n’.

Офлайн

#3 Окт. 24, 2012 21:58:27

odnochlen
Зарегистрирован: 2012-06-28
Сообщения: 794
Репутация: +  14  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

Тоже как раз возился с этой темой. .text - текст внутри тега. Просто текст вводится через tag.tail, tag - идущий перед текстом тег.
Т.е. как я понимаю, весь текст должен быть доступен или через .text, или через .tail.

lxml, видимо, интерпретирует дерево так:

p--| text = "текст1"
br tail = "тескт2"
br tail = "текст3"

Какая-то слишком сложная и ублюдошная эта lxml. Сидишь над ней и думаешь - может ну его нах и уже регекспами все сделать?

Из последнего - как сделать, чтобы подряд шли не больше 2 тегов <br> без текста после тега? В конце концов загнал дерево в строку и прошелся регекспом - и все.

Отредактировано odnochlen (Окт. 24, 2012 22:04:43)

Офлайн

#4 Окт. 24, 2012 22:08:28

kappa-sama
Зарегистрирован: 2012-09-21
Сообщения: 15
Репутация: +  0  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

reclosedev
Спасибо, второй вариант - то что нужно.

odnochlen
Я тоже уже об этом подумывал, но, к счастью, обошлось)

Офлайн

#5 Окт. 25, 2012 04:30:46

odnochlen
Зарегистрирован: 2012-06-28
Сообщения: 794
Репутация: +  14  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

Тогда я свое здесь оставлю.

Как удалить определенную разметку и оставить текст? Например, таблицы?

Как заменить больше 2 <br> подряд без текста между ними? Регекспами - элементарно.

В чем разница между Element и lxml.etree._ElementTree и почему parse() возвращает tree, а document_fromstring() - корневой Element?

Это баг?

from lxml.html import builder as E
print lxml.etree.tostring(E.HTML(E.BR(), E.BR()))
# <html><br/><br/></html>
br = E.BR()
print lxml.etree.tostring(E.HTML(br, br))
# <html><br/></html>

Можно сделать как-то так?
E.BR(tail='text')

Отредактировано odnochlen (Окт. 25, 2012 05:06:02)

Офлайн

#6 Окт. 25, 2012 08:58:11

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

odnochlen
Как удалить определенную разметку и оставить текст? Например, таблицы?

Как заменить больше 2 <br> подряд без текста между ними? Регекспами - элементарно.
# -*- coding: utf-8 -*-
import lxml.html
from lxml.html.clean import Cleaner
 
html = """
<html>
<body>
    <div id="remove_table">
        before table
        <table>
            <tr>
                <td>11</td>
                <td>12</td>
            </tr>
            <tr>
                <td>21</td>
                <td>22</td>
            </tr>
        </table>
        after table
    </div>
    <div id="remove_br">
        line1<br/>
        line2<br/>
        text
        <br/><br/><br/>
    </div>
</body>
</html>
"""
# Как удалить определенную разметку и оставить текст? Например, таблицы?
cleaner = Cleaner(remove_tags=['table', 'td', 'tr'])
print cleaner.clean_html(html)
 
# Как заменить больше 2 <br> подряд без текста между ними? 
doc = lxml.html.fromstring(html)
elements = doc.xpath('//br[count(preceding-sibling::br) >= 2]')
for el in elements:
    if not el.tail:
        el.getparent().remove(el)
print lxml.html.tostring(doc)
odnochlen
В чем разница между Element и lxml.etree._ElementTree и почему parse() возвращает tree, а document_fromstring() - корневой Element?
http://lxml.de/tutorial.html#the-elementtree-class
An ElementTree is mainly a document wrapper around a tree with a root node. It provides a couple of methods for parsing, serialisation and general document handling. One of the bigger differences is that it serialises as a complete document, as opposed to a single Element. This includes top-level processing instructions and comments, as well as a DOCTYPE and other DTD content in the document

odnochlen
Это баг?
Наверное нет, нужно новый элемент создавать.

odnochlen
Можно сделать как-то так?
E.BR(tail='text')
в конструкторе текст, аттрибуты или дети, придется писать на строчку больше
br = E.BR()
br.tail = 'text'

odnochlen
Сидишь над ней и думаешь - может ну его нах и уже регекспами все сделать?
В ситуации когда регекспы проще и понятней, почему бы их не использовать?

upd: if not el.tail
Согласен, что br проще регекспом, только для универсальности нужно будет учитывать разные варианты написания br: <br>, <br/>, <br />, </br>. Хотя можно перед заменой нормализовать все с помощью lxml.html.tostring.

Отредактировано reclosedev (Окт. 25, 2012 10:15:52)

Офлайн

#7 Окт. 25, 2012 12:25:28

odnochlen
Зарегистрирован: 2012-06-28
Сообщения: 794
Репутация: +  14  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

reclosedev
в конструкторе текст, аттрибуты или дети, придется писать на строчку больше
В таком случае - ощутимо больше.
lxml.etree.tostring(E.HTML(
                                      E.BR(), E.BR()
))

Офлайн

#8 Окт. 25, 2012 18:16:43

reclosedev
От: Н.Новгород
Зарегистрирован: 2012-03-29
Сообщения: 870
Репутация: +  173  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

odnochlen
В таком случае - ощутимо больше.
Если реальная необходимость есть, можно функцию-помощник написать: def BR(tail=''): …

Офлайн

#9 Окт. 27, 2012 05:03:27

odnochlen
Зарегистрирован: 2012-06-28
Сообщения: 794
Репутация: +  14  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

reclosedev
elements = doc.xpath('//br[count(preceding-sibling::br) >= 2]')
Интересно, но я до этого еще не дошел. Как я понял, на отсутствие текста между br оно не проверяет?

reclosedev
http://lxml.de/tutorial.html#the-elementtree-class
An ElementTree is mainly a document wrapper around a tree with a root node. It provides a couple of methods for parsing, serialisation and general document handling. One of the bigger differences is that it serialises as a complete document, as opposed to a single Element. This includes top-level processing instructions and comments, as well as a DOCTYPE and other DTD content in the document
Это не совсем ответ на мой вопрос.

Как я уже понял методом тыка, через Element можно итерировать только по детям (через .iter() или foreach), а через tree - по всему дереву (через .iterchildren()). xpath есть и там, и там.

reclosedev
odnochlen
Это баг?
Наверное нет, нужно новый элемент создавать.
И это можно узнать опять же методом тыка.

reclosedev
Согласен, что br проще регекспом
Ну вот, а то чуть что, так сразу дают ссылку.

Офлайн

#10 Окт. 29, 2012 11:13:33

odnochlen
Зарегистрирован: 2012-06-28
Сообщения: 794
Репутация: +  14  -
Профиль   Отправить e-mail  

lxml + xpath + <p>

reclosedev
br = E.BR()
br.tail = 'text'
С хвостом проще, но аттрибуты - только через костыли.
E.BR(), text, E.BR()

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version