создать список текстового содержимого с помощью PyQT? - PullRequest
0 голосов
/ 18 февраля 2012

Я загрузил HTML в pyqt и хотел бы создать список всего содержимого на странице.

Затем мне нужно иметь возможность получить позицию текста, используя .geometry()

Я хотел бы получить список объектов, в которых было бы возможно следующее:

for i in list_of_content_in_html:
    print i.toPlainText(), i.geometry() #prints the text, and the position.

В случае, если мне неясно, под "содержимым" я подразумеваю в приведенном ниже HTML-коде содержимое "c", 'r1 c1', 'r1, c2', 'row2 c2', 'more contents' - текст, который пользователь в основном видит в браузере.

c
<table border="1">
<tr>
<td>r1 c1</td>
<td>r1 c2</td>
</tr>
<tr>
<td></td>
<td>row2 c2</td>
</tr>
</table>
more contents

Ответы [ 2 ]

2 голосов
/ 19 февраля 2012

Это кажется невозможным, если использовать QtWebKit и подобные страницы, которые вкладывают объекты, но не используют <p>...</p> для другого текста, который находится за пределами таблицы.В результате c и more contents не идут в отдельные элементы QWebElements.Их можно найти только в блоке уровня BODY.В качестве решения можно запустить эту страницу через парсер.Простой обход дочерних элементов currentFrame documentElement выявляет следующие элементы:

# position in element tree, bounding box, tag, text:
(0, 0) [0, 0, 75, 165] HTML - u'c\nr1 c1\tr1 c2\nrow2 c2\nmore contents'
(1, 1) [8, 8, 67, 157] BODY - u'c\nr1 c1\tr1 c2\nrow2 c2\nmore contents'
(2, 0) [8, 27, 75, 119] TABLE - u'r1 c1\tr1 c2\nrow2 c2'
(3, 0) [9, 28, 74, 118] TBODY - u'r1 c1\tr1 c2\nrow2 c2'
(4, 0) [9, 30, 74, 72] TR - u'r1 c1\tr1 c2'
(5, 0) [11, 30, 32, 72] TD - u'r1 c1'
(5, 1) [34, 30, 72, 72] TD - u'r1 c2'
(4, 1) [9, 74, 74, 116] TR - u'row2 c2'
(5, 1) [34, 74, 72, 116] TD - u'row2 c2'

Код для этого:

import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *

class WebPage(QObject):
    finished = Signal()
    def __init__(self, data, parent=None):
        super(WebPage, self).__init__(parent)
        self.output = []
        self.data = data
        self.page = QWebPage()
        self.page.loadFinished.connect(self.process)

    def start(self):
        self.page.mainFrame().setHtml(self.data)

    @Slot(bool)
    def process(self, something=False):
        self.page.setViewportSize(self.page.mainFrame().contentsSize())
        frame = self.page.currentFrame()
        elem = frame.documentElement()
        self.gather_info(elem)
        self.finished.emit()

    def gather_info(self, elem, i=0):
        if i > 200: return
        cnt = 0
        while cnt < 100:
            s = elem.toPlainText()
            rect = elem.geometry()
            name = elem.tagName()
            dim = [rect.x(), rect.y(), 
                rect.x() + rect.width(), rect.y() + rect.height()]
            if s: self.output.append(dict(pos=(i, cnt), dim=dim, tag=name, text=s))
            child = elem.firstChild()
            if not child.isNull():
                self.gather_info(child, i+1)
            elem = elem.nextSibling()
            if elem.isNull(): 
                break
            cnt += 1

webpage = None

def print_strings():
    for s in webpage.output:
        print s['pos'], s['dim'], s['tag'], '-',  repr(s['text'])

if __name__ == '__main__':
    app = QApplication(sys.argv)
    data = open(sys.argv[1]).read()
    webpage = WebPage(data)
    webpage.finished.connect(print_strings)
    webpage.start()

.


Другой подход

Желаемый курс действий зависит от того, чего вы хотите достичь.Вы можете получить все строки из QWebPage, используя webpage.currentFrame().documentElement().toPlainText(), но это просто показывает всю страницу в виде строки без информации о позиционировании, связанной со всеми тегами.Просмотр дерева QWebElement дает вам необходимую информацию, но у него есть недостатки, о которых я упоминал выше.

Если вы действительно хотите узнать положение всего текста, единственный точный способ сделать это (кромерендеринг страницы и использование OCR) разбивает текст на символы и сохраняет их отдельные ограничивающие рамки .Вот как я это сделал:

Сначала я проанализировал страницу с помощью BeautifulSoup4 и заключил в текст <span class="Nd92KSx3u2">X</span> каждый непробельный текстовый символ X.Затем я запустил скрипт PyQt (фактически скрипт PySide), который загружает измененную страницу и распечатывает символы с их ограничительными рамками после того, как я их просмотрел, используя findAllElements('span[class="Nd92KSx3u2"]').

parser.py:

import sys, cgi, re
from bs4 import BeautifulSoup, element
magical_class = "Nd92KSx3u2"
restricted_tags="title script object embed".split()
re_my_span = re.compile(r'&lt;span class="%s"&gt;(.+?)&lt;/span&gt;' % magical_class)

def no_nl(s): return str(s).replace("\r", "").replace("\n", " ")

if len(sys.argv) != 3:
    print "Usage: %s <input_html_file> <output_html_file>" % sys.argv[0]
    sys.exit(1)

def process(elem):
    for x in elem.children:
        if isinstance(x, element.Comment): continue
        if isinstance(x, element.Tag):
            if x.name in restricted_tags:
                continue
        if isinstance(x, element.NavigableString):
            if not len(no_nl(x.string).strip()):
                continue  # it's just empty space
            print '[', no_nl(x.string).strip(), ']',  # debug output of found strings
            s = ""
            for c in x.string:
                if c in (' ', '\r', '\n', '\t'): s += c
                else: s += '<span class="%s">%s</span>' % (magical_class, c)
            x.replace_with(s)
            continue
        process(x)

soup = BeautifulSoup(open(sys.argv[1]))
process(soup)
output = re_my_span.sub(r'<span class="%s">\1</span>' % magical_class, str(soup))
with open(sys.argv[2], 'w') as f:
    f.write(output)

charpos.py:

import sys
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
magical_class = "Nd92KSx3u2"

class WebPage(QObject):
    def __init__(self, data, parent=None):
        super(WebPage, self).__init__(parent)
        self.output = []
        self.data = data
        self.page = QWebPage()
        self.page.loadFinished.connect(self.process)

    def start(self):
        self.page.mainFrame().setHtml(self.data)

    @Slot(bool)
    def process(self, something=False):
        self.page.setViewportSize(self.page.mainFrame().contentsSize())
        frame = self.page.currentFrame()
        elements = frame.findAllElements('span[class="%s"]' % magical_class)
        for e in elements:
            s = e.toPlainText()
            rect = e.geometry()
            dim = [rect.x(), rect.y(), 
                rect.x() + rect.width(), rect.y() + rect.height()]
            if s and rect.width() > 0 and rect.height() > 0: print dim, s

if __name__ == '__main__':
    app = QApplication(sys.argv)
    data = open(sys.argv[1]).read()
    webpage = WebPage(data)
    webpage.start()

input.html (слегка изменен, чтобы показать больше проблем с простым выводом строки:

a<span>b<span>c</span></span>
<table border="1">
<tr><td>r1 <font>c1</font>  </td><td>r1 c2</td></tr>
<tr><td></td><td>row2 &amp; c2</td></tr>
</table>
more <b>contents</b>

и тестовым прогоном:

$ python parser.py input.html temp.html
[ a ] [ b ] [ c ] [ r1 ] [ c1 ] [ r1 c2 ] [ row2 & c2 ] [ more ] [ contents ]
$ charpos.py temp.html
[8, 8, 17, 26] a
[17, 8, 26, 26] b
[26, 8, 34, 26] c
[13, 48, 18, 66] r
[18, 48, 27, 66] 1
[13, 67, 21, 85] c
[21, 67, 30, 85] 1
[36, 48, 41, 66] r
[41, 48, 50, 66] 1
[36, 67, 44, 85] c
[44, 67, 53, 85] 2
[36, 92, 41, 110] r
[41, 92, 50, 110] o
[50, 92, 61, 110] w
[61, 92, 70, 110] 2
[36, 111, 47, 129] &
[51, 111, 59, 129] c
[59, 111, 68, 129] 2
[8, 135, 21, 153] m
[21, 135, 30, 153] o
[30, 135, 35, 153] r
[35, 135, 44, 153] e
[8, 154, 17, 173] c
[17, 154, 27, 173] o
[27, 154, 37, 173] n
[37, 154, 42, 173] t
[42, 154, 51, 173] e
[51, 154, 61, 173] n
[61, 154, 66, 173] t
[66, 154, 75, 173] s

Глядя на ограничивающие рамки, (в этом простом случае без изменений размера шрифта и таких вещей, как подписки) довольно легко склеить их обратно в слова, если хотите.

1 голос
/ 19 февраля 2012

Я разобрался.

for elem in QWebView().page().currentFrame().documentElement().findAll('*'):
    print unicode(elem.toPlainText()), unicode(elem.geometry().getCoords()), '\n'

Он совпадает с чем угодно, а затем перебирает то, что найдено - таким образом, перебирает дерево DOM.

...