Туториал по библиотеке BeautifulSoup4

Парсеры — это программы, которые скачивают из интернета странички и разбирают их на составляющие: заголовок, картинка, текст… С помощью него можно выкачать с сайта гигабайты полезной информации. Библиотека BeautifulSoup4 как раз предназначена для парсинга.

В этой статье вы узнаете как распарсить сайт Франка Сонненберга. Цель: по ссылке на пост вытащить его название, текст и картинку.

Франк Сонненберг — известный американский писатель и коуч. За свои книги он попал в “Топ 100 Американских мыслителей”, а его блог принадлежит списку “Лучшие блоги о лидерстве 21 века”.

Прежде чем начинать…

Для прохождения этого туториала вам понадобятся 3 библиотеки:

$ pip install requests BeautifulSoup4 lxml

Получить страничку поста

Будем парсить пост “Are You Grateful?”. Чтобы распарсить HTML-страничку с постом, сначала нужно её скачать. Это можно сделать с помощью requests, вот статья об этой библиотеке.

import requests


url = 'https://www.franksonnenbergonline.com/blog/are-you-grateful/'
response = requests.get(url)
response.raise_for_status()
print(response.text)

Здесь мы просто сделали запрос по ссылке и получили в ответ огромный HTML. Начинаться он будет примерно так:

<!DOCTYPE html>
<html lang="en-US">
<head >
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
...

Парсинг поста

У вас есть HTML страничка, но как достать оттуда заголовок поста, картинку и текст? Наконец, на сцену выходит BeautifulSoup. Сейчас вы получили HTML из response.text, но это просто строка с HTML кодом. Для работы с библиотекой BeautifulSoup нужно сделать из этой строки HTML-суп:

from bs4 import BeautifulSoup


soup = BeautifulSoup(response.text, 'lxml')
print(soup.prettify())

В Python-коде суп — это новый объект с кучей возможностей. Например, теперь можно вывести HTML красиво, с отступами, с помощью метода soup.prettify():

<!DOCTYPE html>
<html lang="en-US">
 <head>
  <meta charset="utf-8"/>
  <meta content="width=device-width, initial-scale=1" name="viewport"/>
  <title>
   Are You Grateful?
...

Супом он называется исторически, вот статья об этом термине. Если вкратце, то на самом деле верстальщики иногда косячат и, например, забывают закрывать теги или оставляют какие-нибудь неисправности. Такой код на HTML стали называть tag soup. Браузеры умеют самостоятельно исправлять какие-то огрехи и делать из такого “супа” нормальный, рабочий HTML. Но если вы скачиваете страничку через requests, то браузер тут ни при чём, и вы получите такой HTML, какой написали верстальщики сайта, со всеми его ошибками.

Для этого и нужна библиотека lxml, она подправит мелкие недочёты, и с ней BeautifulSoup справится даже с очень плохой вёрсткой. В этой строчке вы как раз говорите библиотеке BeautifulSoup использовать lxml:

soup = BeautifulSoup(response.text, 'lxml')

Заголовок поста

Заголовок поста можно легко найти методом супа .find(). Для начала нужно узнать в какой тег этот заголовок обёрнут. В этом помогут инструменты разработчика:

Итак, тег h1. Вот что вернёт метод .find():

print(soup.find('h1'))
# <h1><a href="https://www.franksonnenbergonline.com/"><img src="https://www.franksonnenbergonline.com/wp-content/uploads/2014/07/image_fso_logo.png"/></a></h1>

Это тоже суп, но уже не со всей HTML-страницей, а только с этим тегом и тегами внутри него. Заголовка поста тут нет: пост называется Are You Grateful?, а такого текста в этом теге нет. Похоже, что это не тот тег <h1>, который вы искали. Их на странице несколько и BeautifulSoup4 выдал первый, который нашёл. Это тег <h1>, который находится в самом верху страницы:

Как же найти заголовок поста, а не страницы? Можно уточнить запрос: заголовок поста лежит в теге <header>, а тот — в <main>:

Давайте попробуем такой запрос:

title_tag = soup.find('main').find('header').find('h1')
print(title_tag)
# <h1 class="entry-title">Are You Grateful?</h1>

Тег нашли, а как достать его текст? Всё очень просто:

title_tag = soup.find('main').find('header').find('h1')
title_text = title_tag.text
print(title_text)
# Are You Grateful?

Победа, вы добрались до заголовка поста!

Картинка поста

Картинку можно найти так же: это единственный тег <img> внутри тега <main>. Но давайте попробуем другой подход, найдём её по классу. У картинки есть классы:

У картинки есть 3 класса, они перечислены через пробел:

attachment-post-image size-post-image wp-post-image

Класс attachment-post-image переводится как “Картинка поста”, а значит наверняка он есть только у картинок поста. Вот как найти тег img, у которого есть такой класс:

soup.find('img', class_='attachment-post-image')
# <img alt="grateful, count your blessings, give thanks, do you take things for granted, consider yourself
# fortunate, things to be grateful for, why you should be grateful, Frank Sonnenberg" class="attachment-post-image
# size-post-image wp-post-image" height="400" sizes="(max-width: 800px) 100vw, 800px"
# src="https://www.franksonnenbergonline.com/wp-content/uploads/2019/10/image_are-you-grateful.jpg"
...

Тот же .find(), только указали параметр class_. Нижнее подчёркивание разработчики библиотеки добавили для того, чтобы не было пересечения со словом class из Python, которое используется для создания классов.

Осталось достать адрес картинки, он лежит в аргументе src:

soup.find('img', class_='attachment-post-image')['src']
# https://www.franksonnenbergonline.com/wp-content/uploads/2019/10/image_are-you-grateful.jpg

Домашнее задание

Осталось спарсить текст поста. Сделать это можно одним из способов выше: по классу или тегам, с помощью метода find.


Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.