CoderCastrov logo
CoderCastrov
Парсер

innerText в Scrapy

innerText в Scrapy
просмотров
3 мин чтение
#Парсер
Illustration of what innerText returns from an HTML document

В Javascript есть замечательное свойство под названием innerText, которое учитывает отображение и возвращает все тексты потомков в виде обычного текста, как ожидается.

Нам не нужно беспокоиться о внутренней структуре элемента. Например, если в нем есть теги для жирного текста, спаны, списки и т. д. Мы просто вызываем innerText и получаем хорошее представление текста.

Это может быть невероятно полезно при парсинге блоков описания на веб-страницах. Часто они могут содержать несколько различных HTML-элементов для стилизации.

К сожалению, это менее просто в Scrapy.

Селектор текста в Scrapy

Scrapy предоставляет расширение CSS-селектора под названием ::text, которое возвращает текстовое содержимое элемента. Однако это означает, что структура вроде

<p>This is<b>great</b></p>

Выбранная с помощью response.css("p::text") вернет только This is в качестве результата. Не совсем то, что ожидалось.

Комбинирование текста потомков

Было бы замечательно, если бы Scrapy имел встроенное решение для этого, так как это обычный случай использования, но мы можем сделать это сами.

Наивное решение состоит в объединении всех текстовых элементов с помощью разделителя, например, так:

def innertext_quick(elements, delimiter=""):
    return list(delimiter.join(el.strip() for el in element.css('*::text').getall()) for element in elements)

Однако, запустив это, вы заметите, насколько сложно реализовать отображение текста. Попробуйте с немного более сложным HTML ниже, и вы обнаружите проблему:

<div id="complex-text">
    <p>This div contains <i>complex</i> text</p>
    <ul>
        <li>List item 1</li>
        <li>List item 2</li>
    </ul>
    <blockquote>Including quotes</blockquote>
</div>

Это будет отображаться как длинная строка, если мы не укажем разделители:

This div containscomplextextList item 1List item 2Including quotes

Даже с разделителями, скажем, одним пробелом, у нас возникнут проблемы.

This div contains complex text List item 1 List item 2 Including quotes

Этот код не обрабатывает переносы строк, где мы ожидаем их, например, в параграфе, списке и блоке цитаты.

Использование BeautifulSoup

Если вы уже работали с Scrapy, то вам, возможно, знакома библиотека парсинга HTML - BeautifulSoup или bs4, как обычно импортируется в Python.

BeautifulSoup лучше справляется с парсингом HTML, и мы можем использовать их метод get_text для извлечения текста элемента. Он также позволяет нам контролировать, как текст удаляется, и игнорировать определенные элементы, такие как таблицы.

from bs4 import BeautifulSoup

def innertext(selector):
    html = selector.get()
    soup = BeautifulSoup(html, 'html.parser')
    return soup.get_text().strip()

В сравнении с предыдущим примером, теперь тот же HTML дает следующий результат:

Этот div содержит сложный текст\n\nЭлемент списка 1\nЭлемент списка 2\n\nВключая кавычки

Он правильно игнорирует стилевые элементы, такие как жирный и курсив, или теги span, но сохраняет переносы строк структурных элементов, таких как абзацы, элементы списка и цитаты.

Верное использование innerText

Есть и другая альтернатива - вы можете использовать Playwright (или puppeteer), безголовый браузер, с Scrapy для получения содержимого.

Таким образом, вы получите доступ к HTML DOM, так же, как это делает браузер, и вы можете вызывать innerText, чтобы получить текст, как это делает браузер.

В большинстве случаев это избыточно, и использование Playwright значительно замедляет процесс.

Если вы анализируете сайт с большим количеством JavaScript или приложение SPA, вам все равно понадобится рендеринг, основанный на браузере, поэтому это может быть альтернативой вышеуказанному.

Полный код для справки

Вы можете использовать следующий репозиторий, чтобы поиграть с парсингом в изолированной среде.

Вы можете редактировать test.html, чтобы добавить свой HTML, который требуется разобрать, и настроить методы в innertext.py в соответствии с вашими потребностями.

Запустите text.py, чтобы скрапер запустил файл, вы увидите примеры вывода.

GitHub - ddikman/scrapy-innertext: Помощник для получения внутреннего текста любого элемента

Репозиторий, показывающий, как получить внутренний текст элементов. Код для получения внутреннего текста находится в crawler/innertext.py...

github.com

Счастливого парсинга!