Как написать фильтр BeautifulSoup, который анализирует только объекты с определенным текстом между тегами? - PullRequest
0 голосов
/ 23 февраля 2019

Я использую Django и Python 3.7.Я хочу иметь более эффективный анализ, поэтому я читал об объектах SoupStrainer.Я создал пользовательский, чтобы помочь мне разобрать только те элементы, которые мне нужны ...

def my_custom_strainer(self, elem, attrs):
    for attr in attrs:
        print("attr:" + attr + "=" + attrs[attr])
    if elem == 'div' and 'class' in attr and attrs['class'] == "score":
        return True
    elif elem == "span" and elem.text == re.compile("my text"):
        return True

article_stat_page_strainer = SoupStrainer(self.my_custom_strainer)
soup = BeautifulSoup(html, features="html.parser", parse_only=article_stat_page_strainer)

Одно из условий - я хочу только анализировать "span" элементы, текст которых соответствует определенному шаблону.Отсюда и условие

elem == "span" and elem.text == re.compile("my text")

.Однако это приводит к ошибке

AttributeError: 'str' object has no attribute 'text'

, когда я пытаюсь выполнить вышеуказанное.Как правильно написать мой фильтр?

Ответы [ 3 ]

0 голосов
/ 27 февраля 2019

TLDR; Нет, в настоящее время это невозможно сделать в BeautifulSoup (потребуется модификация объектов BeautifulSoup и SoupStrainer).

Объяснение:

Проблема в том, что переданная фильтром функция вызывается методом handle_starttag().Как вы можете догадаться, у вас есть только значения в открывающем теге (например, имя элемента и атрибуты).

https://bazaar.launchpad.net/~leonardr/beautifulsoup/bs4/view/head:/bs4/init.py#L524

if (self.parse_only and len(self.tagStack) <= 1
    and (self.parse_only.text
     or not self.parse_only.search_tag(name, attrs))):
return None

И, как вы можете видеть, если ваша функция Strainerвозвращает False, элемент немедленно отбрасывается, без возможности принять во внимание внутренний текст (к сожалению).

С другой стороны, если вы добавите «текст» в поиск.

SoupStrainer(text="my text")

он начнет искать внутри тега текст, но у него нет контекста элемента или атрибутов - вы можете увидеть иронию: /

и объединить ее вместе просто не найдете ничего.И вы даже не можете получить доступ к родительскому элементу, как показано здесь в функции поиска: https://gist.github.com/RichardBronosky/4060082

Так что в настоящее время фильтры просто хороши для фильтрации по элементам / атрибутам.Вам нужно было бы изменить много кода Beautiful Soup, чтобы это работало.

Если вам это действительно нужно, я предлагаю наследовать объекты BeautifulSoup и SoupStrainer и изменять их поведение.

0 голосов
/ 01 марта 2019

Я недавно создал парсер lxml / BeautifulSoup для html-файлов, который также выполняет поиск между определенными тегами.

Функция, которую я написал, открывает файловый менеджер вашей операционной системы и позволяет вам выбрать указанный html-файл дляparse.

def openFile(self):
    options = QFileDialog.Options()

    options |= QFileDialog.DontUseNativeDialog
    fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()", "",
                                              "All Files (*);;Python Files (*.py)", options=options)
    if fileName:
        file = open(fileName)
        data = file.read()
        soup = BeautifulSoup(data, "lxml")
        for item in soup.find_all('strong'):
            results.append(float(item.text))
    print('Score =', results[1])
    print('Fps =', results[0])

Вы видите, что указанный мной тег был 'strong', и я пытался найти текст в этом теге.

Надеюсь, я смогу чем-нибудь помочь.

0 голосов
/ 25 февраля 2019

Кажется, вы пытаетесь зациклить элементы супа в методе my_custom_strainer.

Чтобы сделать это, вы можете сделать это следующим образом:

soup = BeautifulSoup(html, features="html.parser", parse_only=article_stat_page_strainer)
my_custom_strainer(soup, attrs)

Затем немного изменить my_custom_strainer чтобы встретить что-то вроде:

def my_custom_strainer(soup, attrs):
  for attr in attrs:
    print("attr:" + attr + "=" + attrs[attr])
  for d in soup.findAll(['div','span']):
    if d.name == 'span' and 'class' in attr and attrs['class'] == "score":
      return d.text # meet your needs here
   elif d.name == 'span' and d.text == re.compile("my text"):
      return d.text # meet your needs here

Таким образом, вы можете итеративно обращаться к объектам супа.

...