Skip to content

Instantly share code, notes, and snippets.

@keinen87

keinen87/bs4.md Secret

Last active December 9, 2019 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save keinen87/707c75e887b48327ba2c102781c570e3 to your computer and use it in GitHub Desktop.
Save keinen87/707c75e887b48327ba2c102781c570e3 to your computer and use it in GitHub Desktop.

Туториал по BeautifulSoup4

Парсинг - автоматический сбор информации с какого-либо сайта с целью его дальнейшей обработки и преобразования.

Модуль Beautiful Soup предназначен для ускорения сбора информации с HTML и XML.

1. Откройте сайт Франка Сонненберга.

На нём потренируемся. Видно, что не все посты помещены на одной странице и всего страниц 92:

2. Откройте какой-нибудь пост на первой странице. Например, вот этот.

А теперь представьте, что надо собрать только текст поста с картинкой без всяких левых ссылок, рекламы и прочего:

Do You Let Envy Get the Better of You?

If you could trade places with anyone, who would it be?
Why would you select that individual over others?
Is it because of their personality, relationships, possessions, career achievements, or their overall lifestyle?
The million-dollar question is: Do you admire that person or do you envy him or her?
...
Ну и весь текст до конца.

И не только этого поста, а всех постов здесь и здесь и вообще на всём сайте. Без помощи специальной библиотеки здесь не обойтись. Для этого есть библиотека BeautifulSoup4.

3. Чтобы работать с веб-страницами, их нужно будет запрашивать

Импортируйте модуль(библиотеку) requests**.

>>> import requests

4. Присвойте url-адрес поста переменной url и сохраните результат запроса в переменную page

>>> url = 'https://www.franksonnenbergonline.com/blog/do-you-let-envy-get-the-better-of-you/'
>>> page = requests.get(url)

5. И не забудьте проверить, успешно ли загружена страница

>>> page.status_code
200

6. А теперь попробуйте вывести текстовое содержимое

>>> page.text
'<!DOCTYPE html>\n<html lang="en-US">\n<head >\n<meta charset="UTF-8" />\n<meta 
tter of You? — Frank Sonnenberg Online</title>\n\n<!-- This site is optimized wi
on" content="Your self-image and self-worth are influenced, in part, by comparin
nippet:-1, max-image-preview:large, max-video-preview:-1"/>\n<link rel="canonica
ta property="og:locale" content="en_US" />\n<meta property="og:type" content="ar
berg Online" />\n<meta property="og:description" content="Your self-image and se
f you?" />\n<meta property="og:url" content="https://www.franksonnenbergonline.c
nnenberg Online" />\n<meta property="article:author" content="https://www.facebo
meta property="article:tag" content="envy" />\n<meta property="article:tag" cont
rticle:tag" content="keeping up with the Joneses" />\n<meta property="article:ta
roperty="article:tag" content="the truth about envy" />\n<meta property="article
rty="article:published_time" content="2019-10-01T10:02:05+00:00" />\n<meta prope
" content="2019-10-01T10:03:03+00:00" />\n<meta property="og:image" content="htt
of-you.jpg" />\n<meta property="og:image:secure_url" content="https://www.franks
n<meta property="og:image:width" content="800" />\n<meta property="og:image:heig
graph--main\'>{"@context":"https://schema.org","@graph":[{"@type":"WebSite","@id
name":"Frank Sonnenberg Online","potentialAction":{"@type":"SearchAction","targe
...
И так далее.

Полный текст страницы был отображен со всеми тегами HTML. Но это очень трудночитаемый текст.

Парсинг с помощью Beautiful Soup

7. Импортируйте Beautiful Soup. Затем нужно запустить обработку документа page.text, чтобы получить объект BeautifulSoup, то есть дерево синтаксического разбора этой страницы, полученное с помощью встроенного html.parser через HTML. Построенный объект представляет документ поста как вложенную структуру данных, которая присваивается переменной soup.

from bs4 import BeautifulSoup
soup = BeautifulSoup(page.text, 'html.parser')

8. Чтобы отобразить содержимое страницы в терминале, используйте метод prettify(). Он превратит дерево разбора Beautiful Soup в красиво отформатированную строку Unicode.

В выводе все теги будут размещены в отдельных строках. Теги вложены из-за схемы дерева, которую использует Beautiful Soup:

>>> print(soup.prettify())
<!DOCTYPE html>
<html lang="en-US">
 <head>
  <meta charset="utf-8"/>
  <meta content="width=device-width, initial-scale=1" name="viewport"/>
  <title>
   Do You Let Envy Get the Better of You? — Frank Sonnenberg Online
  </title>
  <!-- This site is optimized with the Yoast SEO plugin v12.2 - https://yoast.com/wordpress/plugins/seo/ -->
  <meta content="Your self-image and self-worth are influenced, in part, by comparing yourself to others. Do you let envy get the better of you?" name="description"/>
  <meta content="max-snippet:-1, max-image-preview:large, max-video-preview:-1" name="robots"/>
  <link href="https://www.franksonnenbergonline.com/blog/do-you-let-envy-get-the-better-of-you/" rel="canonical"/>
  <meta content="en_US" property="og:locale"/>
  <meta content="article" property="og:type"/>
  <meta content="Do You Let Envy Get the Better of You? — Frank Sonnenberg Online" property="og:title"/>
  <meta content="Your self-image and self-worth are influenced, in part, by comparing yourself to others. Do you let envy get the better of you?" property="og:description"/>
  <meta content="https://www.franksonnenbergonline.com/blog/do-you-let-envy-get-the-better-of-you/" property="og:url"/>
  <meta content="Frank Sonnenberg Online" property="og:site_name"/>
...
И так далее.

Поиск экземпляров тега

8. Извлечь один тег со страницы можно с помощью метода find_all. Он вернет все экземпляры данного тега в документе.

Например, нужно найти заголовок Do You Let Envy Get the Better of You?. Для начала нужно узнать в какой тег этот заголовок обёрнут. В этом помогут инструменты разработчика:

Итак, тег h1. Это тег заголовка первого уровня. Метод find_all вернёт список объектов тега. В списке будет 2 объекта bs4.element.Tag:

>>> soup.find_all('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>, <h1 class="entry-title">Do You Let Envy Get the Better of You?</h1>]
>>> len(soup.find_all('h1'))
2
>>> type(soup.find_all('h1')[0])
<class 'bs4.element.Tag'>

9. А теперь достаньте текст внутри тега

Как видно, нужный нам заголовок второй в списке. Чтобы достать чисто текст, используйте метод get_text():

>>> soup.find_all('h1')[1].get_text()
'Do You Let Envy Get the Better of You?'

Или:

>>> soup.find_all('h1')[1].text
'Do You Let Envy Get the Better of You?'

Поиск тегов по классам и ID

10. Элементы HTML, относящиеся к селекторам CSS, такие как класс и ID, могут быть полезны при работе с веб-данными и Beautiful Soup. Настроить целевые классы и ID можно с помощью метода find_all(), передав ему строки класса и ID в качестве аргументов.

Как видно из гифки вверху, заголовок Do You Let Envy Get the Better of You? не только обёрнут в тег h1, но и стилизуется классом class="entry-title":

Поэтому можно точнее 'поймать' этот заголовок. В списке будет один единственный объект. Ну а дальше вы уже знаете:

>>> soup.find_all('h1', class_='entry-title')
[<h1 class="entry-title">Do You Let Envy Get the Better of You?</h1>]
>>> soup.find_all('h1', class_='entry-title')[0].text
'Do You Let Envy Get the Better of You?'

11. Настала очередь найти ссылку на картинку поста

Через инструменты разработчика найдите разметку картинки поста:

Как видно ссылка на картинку лежит в теге img. Выведите все ссылки, обратившись к объекту по ключу src:

>>> for obj in soup.find_all('img'):
...     print(obj['src'])
...
https://www.franksonnenbergonline.com/wp-content/uploads/2014/07/image_fso_logo.png
https://franksonnenbergonline.com/wp-content/uploads/2014/07/image_character.png
https://www.franksonnenbergonline.com/wp-content/uploads/2014/07/badge_21stcentury.png
https://www.franksonnenbergonline.com/wp-content/uploads/2019/09/image_do-you-let-envy-get-the-better-of-you.jpg
https://secure.gravatar.com/avatar/78d40f7acc174d02a97c96e5e19089b9?s=80&d=mm&r=g
https://secure.gravatar.com/avatar/021ef7abdec73766bc6b0536b7cde573?s=48&d=mm&r=g
https://secure.gravatar.com/avatar/78d40f7acc174d02a97c96e5e19089b9?s=48&d=mm&r=g
https://www.franksonnenbergonline.com/wp-content/plugins/si-captcha-for-wordpress/captcha/securimage_show.php?si_sm_captcha=1&si_form_id=com&prefix=aTB40N14hSwgFeoq
https://www.franksonnenbergonline.com/wp-content/plugins/si-captcha-for-wordpress/captcha/images/refresh.png
https://www.franksonnenbergonline.com/wp-content/uploads/2018/11/SF_Blog_read-a-free-sample-R1.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2016/02/trust.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2015/02/badge_leaders-ama_new.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2015/02/badge_cmoe.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2015/02/badge_taa-lifetime.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2015/12/top-leadership-blog-award-125x125.png
https://blog-cdn.feedspot.com/wp-content/uploads/2017/04/Inspirational-transparent_216px.png
https://www.franksonnenbergonline.com/wp-content/uploads/2016/05/badge_mostinfluential.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2016/05/careersherpa.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2017/09/Best-Inspirational-Blogs-Badge.png
https://franksonnenbergonline.com/wp-content/uploads/2014/07/badge_switchshift.jpg
https://franksonnenbergonline.com/wp-content/uploads/2014/07/badge_alltop.jpg
https://franksonnenbergonline.com/wp-content/uploads/2014/07/badge_21stcentury.png
//widget.aggregage.com/images/leadershipdigital/badge-125x125.png

Ого, вывелись все ссылки на картинки, а нам нужна только одна. Теперь идите вверх по иерархии. Тег img вложен в тег article. Поищите по классам post, type-post:

>>> for obj in soup.find_all(class_='post')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
>>>
>>> for obj in soup.find_all(class_='type-post')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
>>>

Безрезультатно. Идите ещё выше. Тег article в свою очередь вложен в тег main с классом class="content", который вложен в тег div с классом class="content-sidebar-wrap", который в свою очередь вложен в тег div с классом class="site-inner". Теперь попробуйте поискать более точное попадание:

>>> for obj in soup.find_all(class_='content')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
https://www.franksonnenbergonline.com/wp-content/uploads/2019/09/image_do-you-let-envy-get-the-better-of-you.jpg
https://secure.gravatar.com/avatar/78d40f7acc174d02a97c96e5e19089b9?s=80&d=mm&r=g
https://secure.gravatar.com/avatar/021ef7abdec73766bc6b0536b7cde573?s=48&d=mm&r=g
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "C:\Users\user038\Desktop\env\lib\site-packages\bs4\element.py", line 646, in __getattr__
    self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'img'
>>>
>>> for obj in soup.find_all(class_='content-sidebar-wrap')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
https://www.franksonnenbergonline.com/wp-content/uploads/2019/09/image_do-you-let-envy-get-the-better-of-you.jpg
https://www.franksonnenbergonline.com/wp-content/uploads/2018/11/SF_Blog_read-a-free-sample-R1.jpg
>>>
>>> for obj in soup.find_all(class_='site-inner')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
https://www.franksonnenbergonline.com/wp-content/uploads/2019/09/image_do-you-let-envy-get-the-better-of-you.jpg

Получается, что более точный результат будет если искать по классу site-inner. Проверьте себя - выведите пару картинок с других постов, например, с этого и с этого. Потом откройте эти ссылки в браузере и убедитесь, что это те картинки.

>>> url = 'https://www.franksonnenbergonline.com/posters/do-you-repeat-mistakes-or-learn-from-them/'
>>> page = requests.get(url)
>>> soup = BeautifulSoup(page.text, 'html.parser')
>>> for obj in soup.find_all(class_='site-inner')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
https://www.franksonnenbergonline.com/wp-content/uploads/2019/10/poster_12-reasons-why-people-repeat-mistakes.jpg
>>>
>>> url = 'https://www.franksonnenbergonline.com/posters/impossible-is-always-possible/'
>>> page = requests.get(url)
>>> soup = BeautifulSoup(page.text, 'html.parser')
>>> for obj in soup.find_all(class_='site-inner')[0]:
...     if obj.img:
...         print(obj.img['src'])
...
https://www.franksonnenbergonline.com/wp-content/uploads/2019/09/fso_poster_impossible-is-always-possible.jpg
>>>

12. Аналогично ищите сам текст поста

Найдите нужные классы и вытащите весь текст. Поможет поиск по классам site-inner, entry-content:

>>> print(soup.find_all(class_='site-inner')[0].find_all(class_='entry-content')[0].text)

Impossible Is Always Possible
There are so many things that once seemed impossible but are commonplace today. It took people with faith, hard work, and determination to make these things happen. They didn’t listen to naysayers or give in to the grueling fight. They put their heads down, followed their dreams, and refused to accept “no” for an answer. They believed the impossible was possible — And now, the “impossible” is a reality!
How do you feel about the challenge that lies before you? Will you do what it takes to win?
Anything is possible when you believe!
Do You Believe?
Please leave a comment and tell us what you think or share it with someone who can benefit from the information.
Additional Reading: The Power of a Positive Attitude BelieveThere’s Always Hope You Get What You Expect
If you like this article, subscribe to our blog so that you don’t miss a single post. Get future posts by RSS feed, email or Facebook. It’s FREE.

12. Ну и напоследок разберитесь в пагинации

Для того, чтобы собрать текст с картинкой со всех постов, нужно пробежаться во всем страницам на сайте. Перейдите на вторую страницу, потом на третью и посмотрите как меняется ссылка в адресной строке браузера.

То есть меняется только цифра. (Ссылку на первую страницу тоже можно написать как https://www.franksonnenbergonline.com/page/1/). Просто ее перенаправит на https://www.franksonnenbergonline.com)

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

Первый способ.

Видно, что номер последней страницы - 92 лежит в одном из тегов li, а эти теги обернуты в <div class="archive-pagination pagination">. Значит ищите по классу pagination, а в результате ищите по тегу li:

>>> main_page_soup.find_all(class_='pagination')[0].find_all('li')
[<li class="active"><a aria-current="page" aria-label="Current page" href="https://www.franksonnenbergonline.com/">1</a></li>, <li><a href="https://www.franksonnenbergonline.com/page/2/">2</a></li>, <li><a href="https://www.franksonnenbergonline.com/page/3/">3</a></li>, <li class="pagination-omission">…</li>, <li><a href="https://www.franksonnenbergonline.com/page/92/">92</a></li>, <li class="pagination-next"><a href="https://www.franksonnenbergonline.com/page/2/">Next Page »</a></li>]
>>>

А дальше дело техники. Срез списка и свойство text

>>> main_page_soup.find_all(class_='pagination')[0].find_all('li')[-2:-1][0].text
'92'

Второй способ.

Можно добраться до Next Page через его класс class="pagination-next", а потом идти вверх по иерархии - номер 92 же выше находится. Метод previous поможет:

>>> pagination_next = main_page_soup.find_all(class_='pagination-next')
>>> pagination_next[0].previous.previous
'92'
>>>

А дальше все ссылки на посты можно собрать в цикле по всем страницам.

Узнать больше

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment