Регулярное выражение Python не извлекает значение - Python 3.x - PullRequest
1 голос
/ 28 сентября 2019

Я перебираю очень большой (~ 5 ГБ) текстовый документ, например, так:

<P ID=912>
bird
dog
dog
dog
</P>

<P ID=5>
aardvark
bird
bird
cat
egret
</P>

<P ID=291>
aardvark
aardvark
aardvark
aardvark
aardvark
bird
dog
fish
fish
fish
</P>

<P ID=621>
aardvark
aardvark
bird
dog
fish
fish
fish
</P>

<P ID=5>
bird
egret
egret
</P>

<P ID=1>
bird
</P>

Документ очень "не в порядке" в том смысле, что идентификаторы не организованы.Мне нужно создать решение, которое проходит через каждый абзац (обозначается тегами <P ID = x></P>, которые всегда будут там) и извлекать идентификационный номер.

Я использую NLTK для токенизации абзацев, которыеотлично работает, моя проблема в том, что я не могу извлечь ID из тега.

import nltk
from nltk.tokenize import word_tokenize, RegexpTokenizer
import re

def get_input(filepath):
    f = open(filepath, 'r')
    content = f.read()
    return content

def main():
    myfile = get_input("filepath")
    p = r'<P ID=\d+>(.*?)</P>'    
    paras = RegexpTokenizer(p)
    para_id = 0

    for para in paras.tokenize(myfile):
        para_id = re.match("<P ID=\d+>", para)        
        print("Current paragraph Number: {}".format(para_id))

main()

В результате:

Current paragraph Number: None
Current paragraph Number: None
Current paragraph Number: None
Current paragraph Number: None
Current paragraph Number: None
Current paragraph Number: None

Однако я ожидаю, что это будет выглядеть так:

Current paragraph Number: 912
Current paragraph Number: 5
Current paragraph Number: 291
Current paragraph Number: 621
Current paragraph Number: 5
Current paragraph Number: 1

Как мне нужно изменить: para_id = re.match("<P ID=\d+>", para)

Редактировать: Я также пытался: para_id = [i['id'] for i in soup(para, 'html.parser').find_all('p')], но это дает пустое [] Я неконечно, почему я не могу создать суп из единственного абзаца

NB - я должен упомянуть, что это минимальный пример кода.Реальная программа намного больше и требует анализа NLTK, потому что я много работаю со стоп-словами и текстовыми токенизациями.

Ответы [ 3 ]

1 голос
/ 28 сентября 2019

Используйте r'(?s)<P\s*ID\s*=\s*(\d+)\s*>(.*?)</P\s*>' с поиском findall ().
ID находится в группе захвата 1, а Content находится в группе захвата 2.

Пример

>>> input = """
... <P ID=912>
... bird
... dog
... dog
... dog
... </P>
...
... <P ID=5>
... aardvark
... bird
... bird
... cat
... egret
... </P>
...
... <P ID=291>
... aardvark
... aardvark
... aardvark
... aardvark
... aardvark
... bird
... dog
... fish
... fish
... fish
... </P>
...
... <P ID=621>
... aardvark
... aardvark
... bird
... dog
... fish
... fish
... fish
... </P>
...
... <P ID=5>
... bird
... egret
... egret
... </P>
...
... <P ID=1>
... bird
... </P>
... """
>>>
>>> import re
>>> p = re.compile(r'(?s)<P\s*ID\s*=\s*(\d+)\s*>(.*?)</P\s*>')
>>>
>>> ids = p.findall(input)
>>>
>>> i = 0
>>> ids_len = len(ids)
>>>
>>> while ( i < ids_len ):
...     print(ids[i])     # The ID
...     print(ids[i+1])   # The Content
...     i += 2
...
('912', '\nbird\ndog\ndog\ndog\n')
('5', '\naardvark\nbird\nbird\ncat\negret\n')
('291', '\naardvark\naardvark\naardvark\naardvark\naardvark\nbird\ndog\nfish\nfish\nfish\n')
('621', '\naardvark\naardvark\nbird\ndog\nfish\nfish\nfish\n')
('5', '\nbird\negret\negret\n')
('1', '\nbird\n')
>>>
1 голос
/ 28 сентября 2019

Вы, где захватываете текст абзаца, но вам нужно захватить весь абзац, включая P-теги, после этого вы захватили Id абзаца, я использовал ваш простой в data.txt:

from nltk.tokenize import word_tokenize, RegexpTokenizer
import re

def get_input(filepath):
    f = open(filepath, 'r')
    content = f.read()
    f.close()  # don't forget to close file
    return content

def main():
    myfile = get_input("data.txt")
    # here capture the full paragraph
    p = r'<P ID=\d+>.*?</P>'
    paras = RegexpTokenizer(p)
    para_id = 0
    for para in paras.tokenize(myfile):
        # and here just catch the ID
        para_id = re.match("<P ID=(\d+)>", para)
        print("Current paragraph Number: {}".format(para_id.group(1)))

main()

OUPUT:

Current paragraph Number: 912
Current paragraph Number: 5
Current paragraph Number: 291
Current paragraph Number: 621
Current paragraph Number: 5
Current paragraph Number: 1

Вы читаете весь файл размером 5 ГБ. Я думаю, вам следует использовать генератор для этого. Если вам просто нужно напечатать идентификатор пункта:

import re


def main():
    with open("data.txt") as f:  # Using context manager to close resource
        for line in f:
            # and here just catch the ID
            match = re.match("<P ID=(\d+)>", line)
            if match:
                print("Current paragraph Number: {}".format(match.group(1)))

main()

Это сгенерирует то же самоерезультат без загрузки всего 5 ГБ в память.

0 голосов
/ 28 сентября 2019

Одним из возможных решений является передача вашего ввода после обработки с использованием NLTK на BeautifulSoup:

from bs4 import BeautifulSoup as soup
results = [i['id'] for i in soup(content, 'html.parser').find_all('p')]

Вывод:

['912', '5', '291', '621', '5', '1']

BeautifulSoup дает вамвозможность доступа к содержимому абзаца с помощью soup.contents:

for i in soup(content, 'html.parser').find_all('p'):
   print(i.contents)

Вывод:

['\nbird\ndog\ndog\ndog\n']
['\naardvark\nbird\nbird\ncat\negret\n']
['\naardvark\naardvark\naardvark\naardvark\naardvark\nbird\ndog\nfish\nfish\nfish\n']
['\naardvark\naardvark\nbird\ndog\nfish\nfish\nfish\n']
['\nbird\negret\negret\n']
['\nbird\n']
...