Парсинг — это автоматический поиск различных паттернов (на основе заранее определенных конструкций) из текстовых источников данных для извлечения специфической информации.
Не смотря на то, что парсинг — широкое понятие, чаще всего под этим термином подразумевают процесс сбора и анализа данных с удаленных веб-ресурсов.
В языке программирования Python программы для парсинга данных со сторонних сайтов могут быть созданы с помощью двух ключевых инструментов:
Стандартного пакета HTTP-запросов
Внешней библиотеки обработки HTML-разметки
Впрочем, возможности по обработке данных не ограничиваются лишь HTML-документами.
Благодаря множеству внешних библиотек в языке Python можно организовать парсинг документов любой сложности, будь то произвольный текст, популярный язык разметки (например, XML) или редкий язык программирования.
В том случае, если подходящей библиотеки для парсинга не существует, ее можно реализовать вручную на основе тех низкоуровневых методов, которые язык Python предоставляет по умолчанию. Например, на простом поиске вхождений или регулярных выражениях. Хотя, конечно, это требует дополнительных навыков.
В этом руководстве будут рассмотрены способы организации парсеров в языке программирования Python. Речь пойдет об извлечении данных с HTML-страниц на основе заранее указанных тегов и атрибутов.
Все показанные примеры запускались с помощью интерпретатора Python версии 3.10.12 на облачном сервере Timeweb Cloud под управлением операционной системы Ubuntu 22.04. В качестве пакетного менеджера использовался Pip версии 22.0.2.
Любой документ, написанный на HTML, состоит из тегов двух типов:
Открывающий. Указывается внутри символов меньше (<
) и больше (>
). Например, <div>
.
Закрывающий. Указывается в внутри символов меньше (<
) и больше (>
) с указанием косой черты (/
). Например, </div>
.
При этом каждый тег может иметь различные атрибуты, значения которых записываются в кавычках через символ равно. Например, чаще всего используются атрибуты, показывающие принадлежность тега к определенному типу:
href. Ссылка на ресурс. Например, href="https://timeweb.cloud"
.
class. Класс объекта. Например, class="surface panel panel_closed"
.
id. Идентификатор объекта. Например, id="menu"
.
Каждый тег с атрибутами (и без них) является элементом (объектом) так называемого дерева DOM (Document Object Model), которое строится практически любым интерпретатором (процессором) HTML-разметки.
Таким образом выстраивается иерархия элементов, когда вложенные теги являются дочерними по отношению к своим родительским тегам.
Например, в браузере доступ к элементам и их атрибутам выполняется через скрипты JavaScript. А в Python для этого применяются отдельные библиотеки.
Разница лишь в том, что браузер после парсинга HTML-документа не просто строит DOM-дерево, а также выполняет его отображение на мониторе.
Простой HTML-документ может выглядеть так:
<!DOCTYPE html>
<html>
<head>
<title>Это название страницы</title>
</head>
<body>
<h1>Это заголовок</h1>
<p>Это простой текст.</p>
</body>
</html>
Разметка этой страницы построена на тегах с соблюдением иерархии без указания каких-либо атрибутов:
html
head
title
body
h1
p
Такой структуры документа уже более чем достаточно для извлечения информации. Считывая данные между открывающим и закрывающим тегами, можно выполнять парсинг.
Однако теги реальных сайтов имеют дополнительные атрибуты, указывающие браузеру как на специфическую функцию конкретного элемента, так и на его особое оформление (которое описывается в отдельных CSS-файлах):
<!DOCTYPE html>
<html>
<body>
<h1 class="h1_bright">Это заголовок</h1>
<p>Это простой текст.</p>
<div class="block" href="https://timeweb.cloud/services/cloud-servers">
<div class="block__title">Облачные сервисы</div>
<div class="block__information">Сервера в облаке</div>
</div>
<div class="block" href="https://timeweb.cloud/services/dedicated-server">
<div class="block__title">Выделенные серверы</div>
<div class="block__information">Инфраструктура в облаке</div>
</div>
<div class="block" href="https://timeweb.cloud/services/apps">
<div class="block__title">Apps</div>
<div class="block__information">Приложения в облаке</div>
</div>
</body>
</html>
Таким образом, помимо явно указанных тегов искомую информацию можно уточнять конкретными атрибутами, вычленяя из дерева DOM только необходимые элементы.
cloud
Страницы сайтов в сети интернет могут быть двух типов:
Статические. В процессе загрузки и просмотра сайта HTML-разметка остается неизменной. Парсинг не требует эмуляции работы браузера.
Динамические. В процессе загрузки и просмотра сайта (Single-page application, SPA) HTML-разметка модифицируется с помощью JavaScript. Парсинг требует эмуляции работы браузера.
Парсинг статических сайтов довольно прост — выполняется удаленный запрос, после чего из полученного HTML-документа извлекаются необходимые данные.
Парсинг динамических сайтов требует более сложного подхода. После выполнения удаленного запроса на локальную машину загружается как сам HTML-документ, так и JavaScript-скрипты, управляющие им. Последние, в свою очередь, как правило, автоматически выполняют несколько удаленных запросов, подгружая контент и достраивая HTML-документ уже во время просмотра.
По этой причине парсинг динамических сайтов требует эмуляции работы браузера и действий пользователя на стороне локальной машины. В противном случае необходимые данные просто не будут загружены.
Большинство современных сайтов так или иначе подгружают дополнительный контент с помощью скриптов на JavaScript.
Вариативность технических реализаций современных сайтов настолько велика, что их нельзя назвать ни полностью статическими, ни полностью динамическими.
Как правило, общая информация загружается сразу, а специфическая — уже потом.
Большинство HTML-парсеров заточены под статические страницы. Системы, эмулирующие работу браузера для генерации динамического контента встречаются гораздо реже.
В языке Python библиотеки (пакеты), предназначенные для анализа HTML-разметки, можно разделить на две группы:
Низкоуровневые процессоры. Компактные, но синтаксически запутанные пакеты со сложной реализацией, которые выполняют разбор синтаксиса HTML (или XML) и строят иерархическое дерево элементов.
Высокоуровневые библиотеки и фреймворки. Обширные, но синтаксически лаконичные пакеты, обладающие широким набором функций для извлечения формализованных данных из сырых HTML-документов. В эту группу входят не только компактные HTML-парсеры, но и полноценные системы сборки. Зачастую такие пакеты в качестве ядра парсинга используют низкоуровневые пакеты (процессоры) из предыдущей группы.
Для Python написано несколько низкоуровневых библиотек:
lxml. Низкоуровневый процессор синтаксиса XML, который также используется для анализа HTML. В его основе лежит популярная библиотека libxml2
, написанная на языке C.
html5lib. Библиотека анализа синтаксиса HTML, написанная на чистом Python в соответствии с HTML-спецификацией от организации WHATWG (The Web Hypertext Application Technology Working Group), которой следуют все современные браузеры.
Тем не менее, использование высокоуровневых библиотек быстрее и проще — их синтаксис понятнее, а набор функций шире:
BeautifulSoup. Простая, но гибкая библиотека для Python, позволяющая парсить документы на HTML и XML путем создания полноценного DOM-дерева элементов с последующим извлечением необходимых данных.
Scrapy. Полноценный фреймворк парсинга данных из HTML-страниц, представляющий собой набор автономных «пауков» (веб-краулеров) с заданными инструкциями.
Selectolax. Довольно быстрый парсер HTML-страниц, использующий так называемые CSS-селекторы для извлечения информации из тегов.
Parsel. Библиотека со специфическим синтаксисом селекторов, написанная на Python, позволяющая извлекать данные из документов на HTML, JSON и XML.
requests-html. Удобная библиотека, написанная на Python, которая практически точь-в-точь имитирует браузерные CSS-селекторы языка JavaScript.
В этом руководстве будет рассмотрено несколько подобных высокоуровневых библиотек.
Все библиотеки парсинга (впрочем, как и многие другие пакеты) в Python загружаются через стандартный пакетный менеджер pip — его потребуется установить отдельно.
Для начала рекомендуется обновить список доступных репозиториев:
sudo apt update
А уже потом выполнить установку самого pip через пакетный менеджер APT:
sudo apt install python3-pip -y
Флаг -y
позволит утвердительно ответить на все вопросы консольного терминала, возникающие во время установки.
Корректность установки можно проверить, запросив версию pip:
pip3 --version
После этого в консольном терминале появится сообщение с версией пакетного менеджера и адресом его установки:
pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
Как видно, в этом руководстве используется pip версии 22.0.2.
Как правило, к интерпретатору Python по умолчанию прилагается стандартный пакет Requests, позволяющий совершать запросы к удаленным серверам. Именно он будет использоваться в примерах из этого руководства.
Однако в некоторых случаях его может не быть. Поэтому рекомендуется вручную установить Requests через пакетный менеджер pip:
pip install requests
Если пакет уже присутствует в системе, то в консольном терминале появится такое сообщение:
Requirement already satisfied: requests in /usr/lib/python3/dist-packages (2.25.1)
В противном случае Requests будет загружен в список пакетов, доступных для импорта в скриптах Python.
Установка пакета BeautifulSoup версии 4 выполняется через пакетный менеджер pip:
pip install beautifulsoup4
После этого библиотека станет доступна для импорта в скриптах Python. Однако для ее работы требуется дополнительно установить ранее описанные низкоуровневые процессоры HTML.
Поэтому сперва установим lxml
:
pip install lxml
А после этого html5lib
:
pip install html5lib
В дальнейшем в коде приложения Python можно будет указать один из этих процессоров в качестве ядра парсера BeautifulSoup.
В домашней директории создадим новый файл:
sudo nano bs.py
После чего наполним его кодом:
import requests # импортируем библиотеку удаленных запросов
from bs4 import BeautifulSoup # импортируем библиотеку парсинга BeautifulSoup
response = requests.get('https://timeweb.cloud') # выполняем запрос к удаленному серверу Timeweb Cloud
page = BeautifulSoup(response.text, 'html5lib') # парсим ответ удаленного сервера, указывая в качестве процессора парсера библиотеку html5lib
pageTitle = page.find('title') # извлекаем заголовок с тегами <title></title>
print(pageTitle) # выводим заголовок с тегами
print(pageTitle.string) # выводим заголовок без тегов
print("")
pageParagraphs = page.find_all('a') # извлекаем все ссылки с тегами <a></a>
# выводим текст всех найденных элементов
if len(pageParagraphs) >= 3:
# выводим содержимое (без тегов) найденных ссылок
for i in range(3):
print(pageParagraphs[i].string)
print("")
pageItemprop = page.find_all(itemprop='sameAs') # извлекаем все теги, содержащие атрибут itemprop со значением sameAs
# выводим значения атрибута href всех найденных элементов
for item in pageItemprop:
print(item['href'])
print("")
pageMetas = page.find('div', itemprop='address').find_all('meta') # извлекаем все теги meta, размещенные внутри элемента div с атрибутом itemprop, значение которого равно address
# выводим значения атрибута content всех найденных элементов
for meta in pageMetas:
print(meta['content'])
Теперь можно запустить скрипт:
python bs.py
Результатом его работы станет вот такой консольный вывод:>
<title>Облачная инфраструктура для бизнеса — Timeweb Cloud</title>
Облачная инфраструктура для бизнеса — Timeweb Cloud
О компании
Документация
Бухгалтерам
https://github.com/timeweb-cloud
https://vk.com/timewebru
https://www.youtube.com/channel/UCTSnrzx_YKQOzTR1Y6OxxSQ?sub_confirmation=1
https://habr.com/ru/company/timeweb/profile/
https://vc.ru/u/66957-timeweb-cloud
https://dzen.ru/timewebcloud
https://pikabu.ru/@Timeweb.Cloud
https://tenchat.ru/timewebcloud
Россия
196006
Санкт-Петербург
улица Заставская, дом 22, к.2, лит. А, помещ. 303
Разумеется, вместо html5lib
можно указывать lxml
:
...
page = BeautifulSoup(response.text, 'lxml')
...
Однако лучше всего в качестве процессора использовать библиотеку html5lib
. В отличие от lxml
, которая заточена исключительно под работу с XML-разметкой, html5lib
была разработана с учетом всех современных стандартов HTML5.
Несмотря на то, что библиотека BeautifulSoup имеет лаконичный синтаксис, она не поддерживает эмуляцию браузера, а значит не способна динамически подгружать контент.
Фреймворк Scrapy реализован в более объектно-ориентированном виде. Парсинг сайтов в нем основан на трех базовых сущностях:
Пауки (Spiders). Классы, содержащие информацию о нюансах парсинга указанных сайтов: URL-адреса, селекторы (CSS или XPath) элементов и механизм просмотра страниц.
Элементы (Items). Переменные для хранения извлеченных данных, представляющие собой более сложную форму словарей Python с особой внутренней структурой.
Каналы (Pipelines). Промежуточные обработчики извлеченных данных, способные изменять элементы и взаимодействовать с внешним ПО (например, базами данных).
Установка Scrapy выполняется через пакетный менеджер pip:
pip install scrapy
После этого необходимо инициализировать проект парсера, тем самым создав отдельную директорию со своей структурой каталогов и конфигурационных файлов:
scrapy startproject parser
Теперь можно перейти в только что созданную директорию:
cd parser
Проверим содержимое текущего каталога:
ls
Он состоит из общего конфигурационного файла и директории с исходниками проекта:
parser scrapy.cfg
Переходим к исходникам:
cd parser
Если проверить ее содержимое:
ls
-— то можно увидеть как специальные скрипты Python, каждый из которых выполняет свою функцию, так и отдельный каталог для пауков:
__init__.py items.py middlewares.py pipelines.py settings.py spiders
Откроем файл настроек:
nano settings.py
По умолчанию большая часть параметров закомментированы с помощью символа решетки (#
). Для корректной работы парсера часть этих параметров необходимо раскомментировать, не изменяя указанные в файле значения по умолчанию:
USER_AGENT
ROBOTSTXT_OBEY
CONCURRENT_REQUESTS
DOWNLOAD_DELAY
COOKIES_ENABLED
Так или иначе, для каждого конкретного проекта потребуется более точная конфигурация фреймворка. Поэтому список всех параметров, доступных для настройки, можно найти на отдельной странице в официальной документации.
После этого можно сгенерировать нового паука:
scrapy genspider timewebspider timeweb.cloud
В консольном терминале должно появиться сообщение о создании нового паука:
Created spider 'timewebspider' using template 'basic' in module:
parser.spiders.timewebspider
Теперь, если проверить содержимое каталога с пауками:
ls spiders
— то в нем можно увидеть пустые исходники нового паука:
__init__.py __pycache__ timewebspider.py
Откроем файл скрипта:
nano spiders/timewebspider.py
И наполним следующим кодом:
from pathlib import Path # пакет для работы с файлами
import scrapy # пакет фреймворка Scrapy
class TimewebSpider(scrapy.Spider): # класс паука наследуется от класса Spider
name = 'timewebspider' # имя паука
def start_requests(self):
urls = ["https://timeweb.cloud"]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
open("output", "w").close()
someFile = open("output", "a") # создаем новый файл
dataTitle = response.css("title::text").get() # извлекаем заголовок из ответа сервера с помощью CSS-селектора
dataA = response.css("a").getall() # извлекаем все ссылки из ответа сервера с помощью CSS-селектора
someFile.write(dataTitle + "\n\n")
for i in range(3): someFile.write(dataA[i] + "\n")
someFile.close()
Теперь можно запустить созданный паук:
scrapy crawl timewebspider
После этого в текущей директории появится файл output
:
cat output
Его содержимое будет следующим:
Облачная инфраструктура для бизнеса — Timeweb Cloud
<a itemprop="url" class="link---GC2"><span itemprop="name">О компании</span></a>
<a itemprop="url" class="link---GC2"><span itemprop="name">Документация</span></a>
<a href="https://timeweb.com/ru/documents/" itemprop="url" target="_blank" class="link---GC2"><span itemprop="name">Бухгалтерам</span></a>
Более подробную информацию об извлечении данных с помощью селекторов (как CSS, так и XPath) можно найти в официальной документации Scrapy.
Размещайте Python-проекты в облаке Timeweb Cloud
Парсинг данных из удаленных источников в языке Python возможен благодаря двум основным компонентам:
Пакет для выполнения удаленных запросов
Библиотеки для парсинга данных
Последние могут быть как простыми, пригодными лишь для парсинга статических сайтов, так и более сложными, способными эмулировать работу браузера и, как следствие, парсить динамические сайты.
В языке Python наиболее популярны библиотеки для парсинга статических данных:
BeautifulSoup
Scrapy
Эти инструменты, подобно функциям в JavaScript (например, getElementsByClassName()
, использующая CSS-селекторы), позволяют извлекать данные (атрибуты и текст) из элементов DOM-дерева произвольного HTML-документа.