Туториал по библиотеке 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
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.