Красивый суп - Краткое руководство
В современном мире у нас есть тонны неструктурированных данных / информации (в основном веб-данные), доступные бесплатно. Иногда свободно доступные данные легко читать, а иногда нет. Независимо от того, как ваши данные доступны, веб-парсинг - очень полезный инструмент для преобразования неструктурированных данных в структурированные данные, которые легче читать и анализировать. Другими словами, один из способов сбора, организации и анализа этого огромного количества данных - это парсинг веб-страниц. Итак, давайте сначала разберемся, что такое веб-парсинг.
Что такое веб-скрапинг?
Парсинг - это просто процесс извлечения (различными способами), копирования и проверки данных.
Когда мы производим парсинг или извлекаем данные или каналы из Интернета (например, с веб-страниц или веб-сайтов), это называется веб-парсингом.
Таким образом, веб-скрапинг, который также известен как извлечение веб-данных или сбор веб-данных, - это извлечение данных из Интернета. Короче говоря, веб-скрапинг предоставляет разработчикам возможность собирать и анализировать данные из Интернета.
Почему веб-парсинг?
Веб-скрапинг предоставляет один из отличных инструментов для автоматизации большинства вещей, которые человек делает во время просмотра. Веб-скрапинг используется на предприятии по-разному:
Данные для исследования
Умный аналитик (например, исследователь или журналист) использует веб-скреппер вместо ручного сбора и очистки данных с веб-сайтов.
Сравнение цен и популярности товаров
В настоящее время существует несколько сервисов, которые используют веб-скребки для сбора данных с многочисленных онлайн-сайтов и используют их для сравнения популярности товаров и цен.
SEO мониторинг
Существует множество инструментов SEO, таких как Ahrefs, Seobility, SEMrush и т. Д., Которые используются для конкурентного анализа и для извлечения данных с веб-сайтов ваших клиентов.
Поисковые системы
Есть несколько крупных ИТ-компаний, чей бизнес зависит исключительно от парсинга веб-страниц.
Продажи и маркетинг
Данные, собранные с помощью веб-скрапинга, могут использоваться маркетологами для анализа различных ниш и конкурентов или специалистами по продажам для продажи услуг контент-маркетинга или продвижения в социальных сетях.
Почему Python для веб-парсинга?
Python - один из самых популярных языков для парсинга веб-страниц, поскольку он очень легко справляется с большинством задач, связанных со сканированием веб-страниц.
Ниже приведены некоторые из причин, по которым стоит выбрать Python для парсинга веб-страниц:
Легкость использования
Большинство разработчиков согласны с тем, что код Python очень прост. Нам не нужно использовать фигурные скобки «{}» или точки с запятой «;» в любом месте, что делает его более читабельным и простым в использовании при разработке веб-парсеров.
Огромная поддержка библиотеки
Python предоставляет огромный набор библиотек для различных требований, поэтому он подходит для парсинга веб-страниц, а также для визуализации данных, машинного обучения и т. Д.
Легко объяснимый синтаксис
Python - очень читаемый язык программирования, поскольку синтаксис Python прост для понимания. Python очень выразителен, а отступы кода помогают пользователям различать различные блоки или области в коде.
Язык с динамической типизацией
Python - это язык с динамической типизацией, что означает, что данные, присвоенные переменной, говорят о том, какой это тип переменной. Это экономит время и ускоряет работу.
Огромное сообщество
Сообщество Python огромно, и оно поможет вам, где бы вы ни застряли при написании кода.
Знакомство с Beautiful Soup
Beautiful Soup - это библиотека на питоне, названная в честь одноименного стихотворения Льюиса Кэрролла из «Приключений Алисы в стране чудес». Beautiful Soup - это пакет Python, который, как следует из названия, анализирует нежелательные данные и помогает организовать и отформатировать беспорядочные веб-данные, исправляя плохой HTML и представляя нам в легко перемещаемых структурах XML.
Короче говоря, Beautiful Soup - это пакет Python, который позволяет извлекать данные из документов HTML и XML.
Поскольку BeautifulSoup не является стандартной библиотекой Python, нам необходимо сначала установить ее. Мы собираемся установить новейшую библиотеку BeautifulSoup 4 (также известную как BS4).
Чтобы изолировать нашу рабочую среду и не нарушать существующую настройку, давайте сначала создадим виртуальную среду.
Создание виртуальной среды (необязательно)
Виртуальная среда позволяет нам создавать изолированную рабочую копию python для конкретного проекта, не влияя на внешнюю настройку.
Лучший способ установить любую машину с пакетом python - использовать pip, однако, если pip еще не установлен (вы можете проверить его, используя - «pip –version» в командной строке или приглашении оболочки), вы можете установить, введя команду ниже -
Среда Linux
$sudo apt-get install python-pip
Среда Windows
Чтобы установить пип в окнах, сделайте следующее:
Загрузите get-pip.py из https://bootstrap.pypa.io/get-pip.py или из гитхаба на свой компьютер.
Откройте командную строку и перейдите в папку, содержащую файл get-pip.py.
Выполните следующую команду -
>python get-pip.py
Вот и все, pip теперь установлен на вашем компьютере с Windows.
Вы можете проверить установленный пип, выполнив команду ниже -
>pip --version
pip 19.2.3 from c:\users\yadur\appdata\local\programs\python\python37\lib\site-packages\pip (python 3.7)
Установка виртуальной среды
Выполните следующую команду в командной строке -
>pip install virtualenv
После запуска вы увидите снимок экрана ниже -
Команда ниже создаст виртуальную среду («myEnv») в вашем текущем каталоге -
>virtualenv myEnv
Скриншот
Чтобы активировать виртуальную среду, выполните следующую команду -
>myEnv\Scripts\activate
На приведенном выше снимке экрана вы можете видеть, что у нас есть префикс myEnv, который говорит нам, что мы находимся в виртуальной среде myEnv.
Чтобы выйти из виртуальной среды, запустите deactivate.
(myEnv) C:\Users\yadur>deactivate
C:\Users\yadur>
Поскольку наша виртуальная среда готова, теперь давайте установим beautifulsoup.
Установка BeautifulSoup
Поскольку BeautifulSoup не является стандартной библиотекой, нам необходимо ее установить. Мы собираемся использовать пакет BeautifulSoup 4 (известный как bs4).
Linux машина
Чтобы установить bs4 в Debian или Ubuntu linux с помощью системного диспетчера пакетов, выполните следующую команду -
$sudo apt-get install python-bs4 (for python 2.x)
$sudo apt-get install python3-bs4 (for python 3.x)
Вы можете установить bs4 с помощью easy_install или pip (если вы обнаружите проблему при установке с помощью системного упаковщика).
$easy_install beautifulsoup4
$pip install beautifulsoup4
(Возможно, вам придется использовать easy_install3 или pip3 соответственно, если вы используете python3)
Windows машина
Установить beautifulsoup4 в windows очень просто, особенно если у вас уже установлен pip.
>pip install beautifulsoup4
Итак, теперь на нашем компьютере установлен beautifulsoup4. Поговорим о некоторых проблемах, возникших после установки.
Проблемы после установки
На компьютере с Windows вы можете столкнуться с ошибкой установки неправильной версии в основном из-за -
ошибка: ImportError “No module named HTMLParser”, то вы должны использовать версию кода для Python 2 под Python 3.
ошибка: ImportError “No module named html.parser” ошибка, то вы должны использовать версию кода Python 3 под Python 2.
Лучший способ выйти из двух вышеуказанных ситуаций - снова переустановить BeautifulSoup, полностью удалив существующую установку.
Если вы получите SyntaxError “Invalid syntax” в строке ROOT_TAG_NAME = u '[document]', тогда вам нужно преобразовать код python 2 в python 3, просто установив пакет -
$ python3 setup.py install
или вручную запустив скрипт преобразования Python 2 в 3 в каталоге bs4 -
$ 2to3-3.2 -w bs4
Установка парсера
По умолчанию Beautiful Soup поддерживает синтаксический анализатор HTML, включенный в стандартную библиотеку Python, однако он также поддерживает многие внешние сторонние синтаксические анализаторы Python, такие как синтаксический анализатор lxml или синтаксический анализатор html5lib.
Чтобы установить парсер lxml или html5lib, используйте команду -
Linux машина
$apt-get install python-lxml
$apt-get insall python-html5lib
Windows машина
$pip install lxml
$pip install html5lib
Как правило, пользователи используют lxml для скорости, и рекомендуется использовать парсер lxml или html5lib, если вы используете старую версию python 2 (до версии 2.7.3) или python 3 (до версии 3.2.2), поскольку встроенный в Python парсер HTML является не очень хорошо справляется со старой версией.
Бегущий красивый суп
Пришло время протестировать наш пакет Beautiful Soup на одной из html-страниц (взяв веб-страницу - https://www.tutorialspoint.com/index.htm, вы можете выбрать любую другую веб-страницу) и извлечь с нее некоторую информацию.
В приведенном ниже коде мы пытаемся извлечь заголовок с веб-страницы -
from bs4 import BeautifulSoup
import requests
url = "https://www.tutorialspoint.com/index.htm"
req = requests.get(url)
soup = BeautifulSoup(req.text, "html.parser")
print(soup.title)
Вывод
<title>H2O, Colab, Theano, Flutter, KNime, Mean.js, Weka, Solidity, Org.Json, AWS QuickSight, JSON.Simple, Jackson Annotations, Passay, Boon, MuleSoft, Nagios, Matplotlib, Java NIO, PyTorch, SLF4J, Parallax Scrolling, Java Cryptography</title>
Одна из распространенных задач - извлечь все URL-адреса на веб-странице. Для этого нам просто нужно добавить следующую строку кода -
for link in soup.find_all('a'):
print(link.get('href'))
Вывод
https://www.tutorialspoint.com/index.htm
https://www.tutorialspoint.com/about/about_careers.htm
https://www.tutorialspoint.com/questions/index.php
https://www.tutorialspoint.com/online_dev_tools.htm
https://www.tutorialspoint.com/codingground.htm
https://www.tutorialspoint.com/current_affairs.htm
https://www.tutorialspoint.com/upsc_ias_exams.htm
https://www.tutorialspoint.com/tutor_connect/index.php
https://www.tutorialspoint.com/whiteboard.htm
https://www.tutorialspoint.com/netmeeting.php
https://www.tutorialspoint.com/index.htm
https://www.tutorialspoint.com/tutorialslibrary.htm
https://www.tutorialspoint.com/videotutorials/index.php
https://store.tutorialspoint.com
https://www.tutorialspoint.com/gate_exams_tutorials.htm
https://www.tutorialspoint.com/html_online_training/index.asp
https://www.tutorialspoint.com/css_online_training/index.asp
https://www.tutorialspoint.com/3d_animation_online_training/index.asp
https://www.tutorialspoint.com/swift_4_online_training/index.asp
https://www.tutorialspoint.com/blockchain_online_training/index.asp
https://www.tutorialspoint.com/reactjs_online_training/index.asp
https://www.tutorix.com
https://www.tutorialspoint.com/videotutorials/top-courses.php
https://www.tutorialspoint.com/the_full_stack_web_development/index.asp
….
….
https://www.tutorialspoint.com/online_dev_tools.htm
https://www.tutorialspoint.com/free_web_graphics.htm
https://www.tutorialspoint.com/online_file_conversion.htm
https://www.tutorialspoint.com/netmeeting.php
https://www.tutorialspoint.com/free_online_whiteboard.htm
http://www.tutorialspoint.com
https://www.facebook.com/tutorialspointindia
https://plus.google.com/u/0/+tutorialspoint
http://www.twitter.com/tutorialspoint
http://www.linkedin.com/company/tutorialspoint
https://www.youtube.com/channel/UCVLbzhxVTiTLiVKeGV7WEBg
https://www.tutorialspoint.com/index.htm
/about/about_privacy.htm#cookies
/about/faq.htm
/about/about_helping.htm
/about/contact_us.htm
Точно так же мы можем извлечь полезную информацию с помощью beautifulsoup4.
Теперь давайте разберемся с «супом» в примере выше.
В предыдущем примере кода мы разбираем документ с помощью красивого конструктора, используя строковый метод. Другой способ - передать документ через открытый дескриптор файла.
from bs4 import BeautifulSoup
with open("example.html") as fp:
soup = BeautifulSoup(fp)
soup = BeautifulSoup("<html>data</html>")
Сначала документ преобразуется в Unicode, а объекты HTML преобразуются в символы Unicode: </p>
import bs4
html = '''<b>tutorialspoint</b>, <i>&web scraping &data science;</i>'''
soup = bs4.BeautifulSoup(html, 'lxml')
print(soup)
Вывод
<html><body><b>tutorialspoint</b>, <i>&web scraping &data science;</i></body></html>
BeautifulSoup затем анализирует данные с помощью синтаксического анализатора HTML, или вы явно указываете ему на синтаксический анализ с помощью синтаксического анализатора XML.
HTML древовидная структура
Прежде чем мы рассмотрим различные компоненты HTML-страницы, давайте сначала разберемся с древовидной структурой HTML.
Корневым элементом в дереве документа является HTML-код, который может иметь родителей, потомков и братьев и сестер, и это определяется его положением в древовидной структуре. Чтобы перемещаться между элементами HTML, атрибутами и текстом, вы должны перемещаться между узлами в своей древовидной структуре.
Предположим, что веб-страница выглядит так, как показано ниже -
Что переводится в html-документ следующим образом:
<html><head><title>TutorialsPoint</title></head><h1>Tutorialspoint Online Library</h1><p<<b>It's all Free</b></p></body></html>
Это просто означает, что для указанного выше html-документа у нас есть древовидная структура html следующим образом:
Когда мы передаем html-документ или строку конструктору beautifulsoup, beautifulsoup в основном преобразует сложную html-страницу в различные объекты python. Ниже мы обсудим четыре основных типа объектов:
Tag
NavigableString
BeautifulSoup
Comments
Объекты тегов
Тег HTML используется для определения различных типов содержимого. Объект тега в BeautifulSoup соответствует тегу HTML или XML на реальной странице или в документе.
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup('<b class="boldest">TutorialsPoint</b>')
>>> tag = soup.html
>>> type(tag)
<class 'bs4.element.Tag'>
Теги содержат множество атрибутов и методов, и две важные особенности тега - это его имя и атрибуты.
Имя (tag.name)
Каждый тег содержит имя и доступен через суффикс .name. tag.name вернет тип тега.
>>> tag.name
'html'
Однако, если мы изменим имя тега, то же самое отразится в разметке HTML, созданной BeautifulSoup.
>>> tag.name = "Strong"
>>> tag
<Strong><body><b class="boldest">TutorialsPoint</b></body></Strong>
>>> tag.name
'Strong'
Атрибуты (tag.attrs)
Объект тега может иметь любое количество атрибутов. Тег <b class = ”boldest”> имеет атрибут “class”, значение которого - “boldest”. Все, что НЕ является тегом, в основном является атрибутом и должно содержать значение. Вы можете получить доступ к атрибутам либо путем доступа к ключам (например, доступ к «классу» в приведенном выше примере), либо напрямую через «.attrs»
>>> tutorialsP = BeautifulSoup("<div class='tutorialsP'></div>",'lxml')
>>> tag2 = tutorialsP.div
>>> tag2['class']
['tutorialsP']
Мы можем вносить любые изменения в атрибуты нашего тега (добавлять / удалять / изменять).
>>> tag2['class'] = 'Online-Learning'
>>> tag2['style'] = '2007'
>>>
>>> tag2
<div class="Online-Learning" style="2007"></div>
>>> del tag2['style']
>>> tag2
<div class="Online-Learning"></div>
>>> del tag['class']
>>> tag
<b SecondAttribute="2">TutorialsPoint</b>
>>>
>>> del tag['SecondAttribute']
>>> tag
</b>
>>> tag2['class']
'Online-Learning'
>>> tag2['style']
KeyError: 'style'
Многозначные атрибуты
Некоторые атрибуты HTML5 могут иметь несколько значений. Чаще всего используется атрибут класса, который может иметь несколько значений CSS. Другие включают "rel", "rev", "headers", "accesskey" и "accept-charset". Многозначные атрибуты красивого супа показаны в виде списка.
>>> from bs4 import BeautifulSoup
>>>
>>> css_soup = BeautifulSoup('<p class="body"></p>')
>>> css_soup.p['class']
['body']
>>>
>>> css_soup = BeautifulSoup('<p class="body bold"></p>')
>>> css_soup.p['class']
['body', 'bold']
Однако, если какой-либо атрибут содержит более одного значения, но он не является многозначным атрибутом любой версией стандарта HTML, красивый суп оставит атрибут в покое -
>>> id_soup = BeautifulSoup('<p id="body bold"></p>')
>>> id_soup.p['id']
'body bold'
>>> type(id_soup.p['id'])
<class 'str'>
Вы можете объединить несколько значений атрибутов, если превратите тег в строку.
>>> rel_soup = BeautifulSoup("<p> tutorialspoint Main <a rel='Index'> Page</a></p>")
>>> rel_soup.a['rel']
['Index']
>>> rel_soup.a['rel'] = ['Index', ' Online Library, Its all Free']
>>> print(rel_soup.p)
<p> tutorialspoint Main <a rel="Index Online Library, Its all Free"> Page</a></p>
Используя get_attribute_list, вы получаете значение, которое всегда является списком, строкой, независимо от того, многозначное оно или нет.
id_soup.p.get_attribute_list(‘id’)
Однако, если вы проанализируете документ как 'xml', многозначных атрибутов нет -
>>> xml_soup = BeautifulSoup('<p class="body bold"></p>', 'xml')
>>> xml_soup.p['class']
'body bold'
NavigableString
Объект navigablestring используется для представления содержимого тега. Для доступа к содержимому используйте «.string» с тегом.
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<h2 id='message'>Hello, Tutorialspoint!</h2>")
>>>
>>> soup.string
'Hello, Tutorialspoint!'
>>> type(soup.string)
>
Вы можете заменить строку другой строкой, но не можете редактировать существующую строку.
>>> soup = BeautifulSoup("<h2 id='message'>Hello, Tutorialspoint!</h2>")
>>> soup.string.replace_with("Online Learning!")
'Hello, Tutorialspoint!'
>>> soup.string
'Online Learning!'
>>> soup
<html><body><h2 id="message">Online Learning!</h2></body></html>
BeautifulSoup
BeautifulSoup - это объект, который создается, когда мы пытаемся очистить веб-ресурс. Итак, это полный документ, который мы пытаемся очистить. В большинстве случаев это объект тега.
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup("<h2 id='message'>Hello, Tutorialspoint!</h2>")
>>> type(soup)
<class 'bs4.BeautifulSoup'>
>>> soup.name
'[document]'
Комментарии
Объект комментария иллюстрирует часть комментария веб-документа. Это просто особый тип NavigableString.
>>> soup = BeautifulSoup('<p><!-- Everything inside it is COMMENTS --></p>')
>>> comment = soup.p.string
>>> type(comment)
<class 'bs4.element.Comment'>
>>> type(comment)
<class 'bs4.element.Comment'>
>>> print(soup.p.prettify())
<p>
<!-- Everything inside it is COMMENTS -->
</p>
Объекты NavigableString
Объекты navigablestring используются для представления текста внутри тегов, а не самих тегов.
В этой главе мы обсудим навигацию по тегам.
Ниже наш html-документ -
>>> html_doc = """
<html><head><title>Tutorials Point</title></head>
<body>
<p class="title"><b>The Biggest Online Tutorials Library, It's all Free</b></p>
<p class="prog">Top 5 most used Programming Languages are:
<a href="https://www.tutorialspoint.com/java/java_overview.htm" class="prog" id="link1">Java</a>,
<a href="https://www.tutorialspoint.com/cprogramming/index.htm" class="prog" id="link2">C</a>,
<a href="https://www.tutorialspoint.com/python/index.htm" class="prog" id="link3">Python</a>,
<a href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" class="prog" id="link4">JavaScript</a> and
<a href="https://www.tutorialspoint.com/ruby/index.htm" class="prog" id="link5">C</a>;
as per online survey.</p>
<p class="prog">Programming Languages</p>
"""
>>>
>>> from bs4 import BeautifulSoup
>>> soup = BeautifulSoup(html_doc, 'html.parser')
>>>
Основываясь на приведенном выше документе, мы попытаемся перейти от одной части документа к другой.
Спускаться
Одной из важных частей любого элемента HTML-документа являются теги, которые могут содержать другие теги / строки (дочерние элементы тега). Beautiful Soup предоставляет разные способы навигации и перебора дочерних элементов тега.
Навигация с использованием имен тегов
Самый простой способ поиска в дереве синтаксического анализа - поиск тега по его имени. Если вам нужен тег <head>, используйте soup.head -
>>> soup.head
<head>&t;title>Tutorials Point</title></head>
>>> soup.title
<title>Tutorials Point</title>
Чтобы получить конкретный тег (например, первый тег <b>) в теге <body>.
>>> soup.body.b
<b>The Biggest Online Tutorials Library, It's all Free</b>
Использование имени тега в качестве атрибута даст вам только первый тег с этим именем -
>>> soup.a
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
Чтобы получить все атрибуты тега, вы можете использовать метод find_all () -
>>> soup.find_all("a")
[<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>, <a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>, <a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>, <a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>, <a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>]>>> soup.find_all("a")
[<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>, <a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>, <a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>, <a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>, <a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>]
.contents и .children
Мы можем искать дочерние элементы тега в списке по его .contents -
>>> head_tag = soup.head
>>> head_tag
<head><title>Tutorials Point</title></head>
>>> Htag = soup.head
>>> Htag
<head><title>Tutorials Point</title></head>
>>>
>>> Htag.contents
[<title>Tutorials Point</title>
>>>
>>> Ttag = head_tag.contents[0]
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.contents
['Tutorials Point']
У самого объекта BeautifulSoup есть дочерние элементы. В этом случае тег <html> является дочерним элементом объекта BeautifulSoup -
>>> len(soup.contents)
2
>>> soup.contents[1].name
'html'
Строка не имеет .contents, потому что она ничего не может содержать -
>>> text = Ttag.contents[0]
>>> text.contents
self.__class__.__name__, attr))
AttributeError: 'NavigableString' object has no attribute 'contents'
Вместо того, чтобы получать их в виде списка, используйте генератор .children для доступа к дочерним элементам тега -
>>> for child in Ttag.children:
print(child)
Tutorials Point
потомки
Атрибут .descendants позволяет вам рекурсивно перебирать всех дочерних элементов тега -
его прямые дети и дети его прямых детей и так далее -
>>> for child in Htag.descendants:
print(child)
<title>Tutorials Point</title>
Tutorials Point
Тег <head> имеет только один дочерний элемент, но имеет два потомка: тег <title> и дочерний элемент тега <title>. У объекта beautifulsoup есть только один прямой дочерний элемент (тег <html>), но у него много потомков -
>>> len(list(soup.children))
2
>>> len(list(soup.descendants))
33
.string
Если у тега только один дочерний элемент, и этот дочерний элемент является NavigableString, дочерний элемент становится доступным как .string -
>>> Ttag.string
'Tutorials Point'
Если единственным дочерним элементом тега является другой тег, и этот тег имеет .string, то считается, что родительский тег имеет ту же .string, что и его дочерний тег -
>>> Htag.contents
[<title>Tutorials Point</title>]
>>>
>>> Htag.string
'Tutorials Point'
Однако, если тег содержит более одного объекта, тогда неясно, на что следует ссылаться .string, поэтому .string имеет значение None -
>>> print(soup.html.string)
None
.strings и stripped_strings
Если внутри тега более одного объекта, вы все равно можете просматривать только строки. Используйте генератор .strings -
>>> for string in soup.strings:
print(repr(string))
'\n'
'Tutorials Point'
'\n'
'\n'
"The Biggest Online Tutorials Library, It's all Free"
'\n'
'Top 5 most used Programming Languages are: \n'
'Java'
',\n'
'C'
',\n'
'Python'
',\n'
'JavaScript'
' and\n'
'C'
';\n \nas per online survey.'
'\n'
'Programming Languages'
'\n'
Чтобы удалить лишние пробелы, используйте генератор .stripped_strings -
>>> for string in soup.stripped_strings:
print(repr(string))
'Tutorials Point'
"The Biggest Online Tutorials Library, It's all Free"
'Top 5 most used Programming Languages are:'
'Java'
','
'C'
','
'Python'
','
'JavaScript'
'and'
'C'
';\n \nas per online survey.'
'Programming Languages'
Подниматься
По аналогии с «генеалогическим деревом» каждый тег и каждая строка имеет родителя: тег, который его содержит:
.parent
Чтобы получить доступ к родительскому элементу элемента, используйте атрибут .parent.
>>> Ttag = soup.title
>>> Ttag
<title>Tutorials Point</title>
>>> Ttag.parent
<head>title>Tutorials Point</title></head>
В нашем html_doc сама строка заголовка имеет родителя: тег <title>, который ее содержит -
>>> Ttag.string.parent
<title>Tutorials Point</title>
Родителем тега верхнего уровня, такого как <html>, является сам объект Beautifulsoup -
>>> htmltag = soup.html
>>> type(htmltag.parent)
<class 'bs4.BeautifulSoup'>
Родитель объекта Beautifulsoup определяется как None -
>>> print(soup.parent)
None
.родители
Чтобы перебрать все родительские элементы, используйте атрибут .parents.
>>> link = soup.a
>>> link
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
>>>
>>> for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
p
body
html
[document]
Идти боком
Ниже один простой документ -
>>> sibling_soup = BeautifulSoup("<a><b>TutorialsPoint</b><c><strong>The Biggest Online Tutorials Library, It's all Free</strong></b></a>")
>>> print(sibling_soup.prettify())
<html>
<body>
<a>
<b>
TutorialsPoint
</b>
<c>
<strong>
The Biggest Online Tutorials Library, It's all Free
</strong>
</c>
</a>
</body>
</html>
В приведенном выше документе теги <b> и <c> находятся на одном уровне, и оба являются дочерними элементами одного тега. Оба тега <b> и <c> являются братьями и сестрами.
.next_sibling и .previous_sibling
Используйте .next_sibling и .previous_sibling для перехода между элементами страницы, которые находятся на одном уровне дерева синтаксического анализа:
>>> sibling_soup.b.next_sibling
<c><strong>The Biggest Online Tutorials Library, It's all Free</strong></c>
>>>
>>> sibling_soup.c.previous_sibling
<b>TutorialsPoint</b>
Тег <b> имеет .next_sibling, но не имеет .previous_sibling, поскольку на том же уровне дерева перед тегом <b> нет ничего, тот же случай с тегом <c>.
>>> print(sibling_soup.b.previous_sibling)
None
>>> print(sibling_soup.c.next_sibling)
None
Две строки не являются братьями и сестрами, поскольку у них нет одного и того же родителя.
>>> sibling_soup.b.string
'TutorialsPoint'
>>>
>>> print(sibling_soup.b.string.next_sibling)
None
.next_siblings и .previous_siblings
Чтобы перебирать родственников тега, используйте .next_siblings и .previous_siblings.
>>> for sibling in soup.a.next_siblings:
print(repr(sibling))
',\n'
<a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>
',\n'
>a class="prog" href="https://www.tutorialspoint.com/python/index.htm" id="link3">Python</a>
',\n'
<a class="prog" href="https://www.tutorialspoint.com/javascript/javascript_overview.htm" id="link4">JavaScript</a>
' and\n'
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm"
id="link5">C</a>
';\n \nas per online survey.'
>>> for sibling in soup.find(id="link3").previous_siblings:
print(repr(sibling))
',\n'
<a class="prog" href="https://www.tutorialspoint.com/cprogramming/index.htm" id="link2">C</a>
',\n'
<a class="prog" href="https://www.tutorialspoint.com/java/java_overview.htm" id="link1">Java</a>
'Top 5 most used Programming Languages are: \n'
Туда и обратно
Теперь давайте вернемся к первым двум строкам в нашем предыдущем примере «html_doc» -
&t;html><head><title>Tutorials Point</title></head>
<body>
<h4 class="tagLine"><b>The Biggest Online Tutorials Library, It's all Free</b></h4>
Анализатор HTML берет указанную выше строку символов и превращает ее в серию событий, таких как «открыть тег <html>», «открыть тег <head>», «открыть тег <title>», «добавить строку», «Закрыть тег </title>», «закрыть тег </head>», «открыть тег <h4>» и так далее. BeautifulSoup предлагает различные методы для восстановления исходного синтаксического анализа документа.
.next_element и .previous_element
Атрибут .next_element тега или строки указывает на то, что было проанализировано сразу после этого. Иногда он похож на .next_sibling, но не полностью. Ниже показан последний тег <a> в нашем примере документа «html_doc».
>>> last_a_tag = soup.find("a", id="link5")
>>> last_a_tag
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>
>>> last_a_tag.next_sibling
';\n \nas per online survey.'
Однако .next_element этого тега <a>, то, что было проанализировано сразу после тега <a>, не является остальной частью этого предложения: это слово «C»:
>>> last_a_tag.next_element
'C'
Вышеуказанное поведение связано с тем, что в исходной разметке перед точкой с запятой стояла буква «C». Синтаксический анализатор обнаружил тег <a>, затем букву «C», затем закрывающий тег </a>, затем точку с запятой и оставшуюся часть предложения. Точка с запятой находится на том же уровне, что и тег <a>, но буква «C» встречается первой.
Атрибут .previous_element - полная противоположность .next_element. Он указывает на любой элемент, который был проанализирован непосредственно перед этим.
>>> last_a_tag.previous_element
' and\n'
>>>
>>> last_a_tag.previous_element.next_element
<a class="prog" href="https://www.tutorialspoint.com/ruby/index.htm" id="link5">C</a>
.next_elements и .previous_elements
Мы используем эти итераторы для перемещения вперед и назад к элементу.
>>> for element in last_a_tag.next_e lements:
print(repr(element))
'C'
';\n \nas per online survey.'
'\n'
<p class="prog">Programming Languages</p>
'Programming Languages'
'\n'
Существует множество методов Beautifulsoup, которые позволяют нам искать дерево синтаксического анализа. Два наиболее распространенных и используемых метода - это find () и find_all ().
Прежде чем говорить о find () и find_all (), давайте рассмотрим несколько примеров различных фильтров, которые вы можете передать в эти методы.
Виды фильтров
У нас есть разные фильтры, которые мы можем передать в эти методы, и понимание этих фильтров имеет решающее значение, поскольку эти фильтры используются снова и снова во всем API поиска. Мы можем использовать эти фильтры на основе имени тега, его атрибутов, текста строки или их сочетания.
Строка
Один из самых простых типов фильтров - это строка. Передав строку в метод поиска, Beautifulsoup выполнит сопоставление с этой точной строкой.
Код ниже найдет все теги <p> в документе -
>>> markup = BeautifulSoup('<p>Top Three</p><p><pre>Programming Languages are:</pre></p><p><b>Java, Python, Cplusplus</b></p>')
>>> markup.find_all('p')
[<p>Top Three</p>, <p></p>, <p><b>Java, Python, Cplusplus</b></p>]
Регулярное выражение
Вы можете найти все теги, начинающиеся с заданной строки / тега. Перед этим нам нужно импортировать модуль re, чтобы использовать регулярное выражение.
>>> import re
>>> markup = BeautifulSoup('<p>Top Three</p><p><pre>Programming Languages are:</pre></p><p><b>Java, Python, Cplusplus</b></p>')
>>>
>>> markup.find_all(re.compile('^p'))
[<p>Top Three</p>, <p></p>, <pre>Programming Languages are:</pre>, <p><b>Java, Python, Cplusplus</b></p>]
Список
Вы можете передать несколько тегов для поиска, предоставив список. Код ниже находит все теги <b> и <pre> -
>>> markup.find_all(['pre', 'b'])
[<pre>Programming Languages are:</pre>, <b>Java, Python, Cplusplus</b>]
Правда
True вернет все теги, которые может найти, но не строки сами по себе -
>>> markup.find_all(True)
[<html><body><p>Top Three</p><p></p><pre>Programming Languages are:</pre>
<p><b>Java, Python, Cplusplus</b> </p> </body></html>,
<body><p>Top Three</p><p></p><pre> Programming Languages are:</pre><p><b>Java, Python, Cplusplus</b></p>
</body>,
<p>Top Three</p>, <p></p>, <pre>Programming Languages are:</pre>, <p><b>Java, Python, Cplusplus</b></p>, <b>Java, Python, Cplusplus</b>]
Чтобы вернуть только теги из вышеуказанного супа -
>>> for tag in markup.find_all(True):
(tag.name)
'html'
'body'
'p'
'p'
'pre'
'p'
'b'
найти все()
Вы можете использовать find_all для извлечения всех вхождений определенного тега из ответа страницы как -
Синтаксис
find_all(name, attrs, recursive, string, limit, **kwargs)
Давайте извлечем некоторые интересные данные из IMDB- «Лучшие фильмы за все время».
>>> url="https://www.imdb.com/chart/top/?ref_=nv_mv_250"
>>> content = requests.get(url)
>>> soup = BeautifulSoup(content.text, 'html.parser')
#Extract title Page
>>> print(soup.find('title'))
<title>IMDb Top 250 - IMDb</title>
#Extracting main heading
>>> for heading in soup.find_all('h1'):
print(heading.text)
Top Rated Movies
#Extracting sub-heading
>>> for heading in soup.find_all('h3'):
print(heading.text)
IMDb Charts
You Have Seen
IMDb Charts
Top India Charts
Top Rated Movies by Genre
Recently Viewed
Сверху мы видим, что find_all предоставит нам все элементы, соответствующие определенным нами критериям поиска. Все фильтры, которые мы можем использовать с find_all (), можно использовать с find () и другими методами поиска, такими как find_parents () или find_siblings ().
находить()
Мы видели выше, find_all () используется для сканирования всего документа, чтобы найти все содержимое, кроме чего-то, требуется найти только один результат. Если вы знаете, что документ содержит только один тег <body>, поиск по всему документу будет пустой тратой времени. Один из способов - каждый раз вызывать find_all () с limit = 1, иначе мы можем использовать метод find (), чтобы сделать то же самое -
Синтаксис
find(name, attrs, recursive, string, **kwargs)
Итак, ниже два разных метода дают один и тот же результат -
>>> soup.find_all('title',limit=1)
[<title>IMDb Top 250 - IMDb</title>]
>>>
>>> soup.find('title')
<title>IMDb Top 250 - IMDb</title>
В приведенных выше выходных данных мы видим, что метод find_all () возвращает список, содержащий один элемент, тогда как метод find () возвращает единственный результат.
Еще одно различие между методами find () и find_all () -
>>> soup.find_all('h2')
[]
>>>
>>> soup.find('h2')
Если метод soup.find_all () ничего не может найти, он возвращает пустой список, тогда как find () возвращает None.
find_parents () и find_parent ()
В отличие от методов find_all () и find (), которые проходят по дереву, глядя на потомков тегов, методы find_parents () и find_parents () делают противоположное: они перемещаются по дереву вверх и смотрят на родителей тега (или строки).
Синтаксис
find_parents(name, attrs, string, limit, **kwargs)
find_parent(name, attrs, string, **kwargs)
>>> a_string = soup.find(string="The Godfather")
>>> a_string
'The Godfather'
>>> a_string.find_parents('a')
[<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>]
>>> a_string.find_parent('a')
<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>
>>> a_string.find_parent('tr')
<tr>
<td class="posterColumn">
<span data-value="2" name="rk"></span>
<span data-value="9.149038526210072" name="ir"></span>
<span data-value="6.93792E10" name="us"></span>
<span data-value="1485540" name="nv"></span>
<span data-value="-1.850961473789928" name="ur"></span>
<a href="/title/tt0068646/"> <img alt="The Godfather" height="67" src="https://m.media-amazon.com/images/M/MV5BM2MyNjYxNmUtYTAwNi00MTYxLWJmNWYtYzZlODY3ZTk3OTFlXkEyXkFqcGdeQXVyNzkwMjQ5NzM@._V1_UY67_CR1,0,45,67_AL_.jpg" width="45"/>
</a> </td>
<td class="titleColumn">
2.
<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>
<span class="secondaryInfo">(1972)</span>
</td>
<td class="ratingColumn imdbRating">
<strong title="9.1 based on 1,485,540 user ratings">9.1</strong>
</td>
<td class="ratingColumn">
<div class="seen-widget seen-widget-tt0068646 pending" data-titleid="tt0068646">
<div class="boundary">
<div class="popover">
<span class="delete"> </span><ol><li>1<li>2<li>3<li>4<li>5<li>6<li>7<li>8<li>9<li>10</li>0</li></li></li></li&td;</li></li></li></li></li></ol> </div>
</div>
<div class="inline">
<div class="pending"></div>
<div class="unseeable">NOT YET RELEASED</div>
<div class="unseen"> </div>
<div class="rating"></div>
<div class="seen">Seen</div>
</div>
</div>
</td>
<td class="watchlistColumn">
<div class="wlb_ribbon" data-recordmetrics="true" data-tconst="tt0068646"></div>
</td>
</tr>
>>>
>>> a_string.find_parents('td')
[<td class="titleColumn">
2.
<a href="/title/tt0068646/" title="Francis Ford Coppola (dir.), Marlon Brando, Al Pacino">The Godfather</a>
<span class="secondaryInfo">(1972)</span>
</td>]
Есть восемь других подобных методов -
find_next_siblings(name, attrs, string, limit, **kwargs)
find_next_sibling(name, attrs, string, **kwargs)
find_previous_siblings(name, attrs, string, limit, **kwargs)
find_previous_sibling(name, attrs, string, **kwargs)
find_all_next(name, attrs, string, limit, **kwargs)
find_next(name, attrs, string, **kwargs)
find_all_previous(name, attrs, string, limit, **kwargs)
find_previous(name, attrs, string, **kwargs)
Где,
find_next_siblings() и find_next_sibling() методы будут перебирать всех братьев и сестер элемента, которые идут после текущего.
find_previous_siblings() и find_previous_sibling() методы будут перебирать всех братьев и сестер, предшествующих текущему элементу.
find_all_next() и find_next() методы будут перебирать все теги и строки, которые идут после текущего элемента.
find_all_previous и find_previous() методы будут перебирать все теги и строки, которые идут перед текущим элементом.
Селекторы CSS
Библиотека BeautifulSoup для поддержки наиболее часто используемых селекторов CSS. Вы можете искать элементы с помощью селекторов CSS с помощью метода select ().
Вот несколько примеров -
>>> soup.select('title')
[<title>IMDb Top 250 - IMDb</title>, <title>IMDb Top Rated Movies</title>]
>>>
>>> soup.select("p:nth-of-type(1)")
[<p>The Top Rated Movie list only includes theatrical features.</p>, <p> class="imdb-footer__copyright _2-iNNCFskmr4l2OFN2DRsf">© 1990- by IMDb.com, Inc.</p>]
>>> len(soup.select("p:nth-of-type(1)"))
2
>>> len(soup.select("a"))
609
>>> len(soup.select("p"))
2
>>> soup.select("html head title")
[<title>IMDb Top 250 - IMDb</title>, <title>IMDb Top Rated Movies</title>]
>>> soup.select("head > title")
[<title>IMDb Top 250 - IMDb</title>]
#print HTML code of the tenth li elemnet
>>> soup.select("li:nth-of-type(10)")
[<li class="subnav_item_main">
<a href="/search/title?genres=film_noir&sort=user_rating,desc&title_type=feature&num_votes=25000,">Film-Noir
</a> </li>]
Одним из важных аспектов BeautifulSoup является поиск в дереве синтаксического анализа, который позволяет вам вносить изменения в веб-документ в соответствии с вашими требованиями. Мы можем вносить изменения в свойства тега, используя его атрибуты, такие как метод .name, .string или .append (). Он позволяет добавлять новые теги и строки к существующему тегу с помощью методов .new_string () и .new_tag (). Есть и другие методы, такие как .insert (), .insert_before () или .insert_after (), для внесения различных изменений в ваш HTML- или XML-документ.
Изменение имен тегов и атрибутов
После того, как вы создали суп, можно легко внести изменения, например переименовать тег, изменить его атрибуты, добавить новые атрибуты и удалить атрибуты.
>>> soup = BeautifulSoup('<b class="bolder">Very Bold</b>')
>>> tag = soup.b
Модификация и добавление новых атрибутов заключаются в следующем:
>>> tag.name = 'Blockquote'
>>> tag['class'] = 'Bolder'
>>> tag['id'] = 1.1
>>> tag
<Blockquote class="Bolder" id="1.1">Very Bold</Blockquote>
Удаление атрибутов выглядит следующим образом -
>>> del tag['class']
>>> tag
<Blockquote id="1.1">Very Bold</Blockquote>
>>> del tag['id']
>>> tag
<Blockquote>Very Bold</Blockquote>
Изменение .string
Вы можете легко изменить атрибут тега .string -
>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">Must for every <i>Learner>/i<</a>'
>>> Bsoup = BeautifulSoup(markup)
>>> tag = Bsoup.a
>>> tag.string = "My Favourite spot."
>>> tag
<a href="https://www.tutorialspoint.com/index.htm">My Favourite spot.</a>
Сверху мы можем увидеть, содержит ли тег какие-либо другие теги, они и все их содержимое будут заменены новыми данными.
добавить ()
Добавление новых данных / содержимого в существующий тег осуществляется с помощью метода tag.append (). Он очень похож на метод append () в списке Python.
>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">Must for every <i>Learner</i></a>'
>>> Bsoup = BeautifulSoup(markup)
>>> Bsoup.a.append(" Really Liked it")
>>> Bsoup
<html><body><a href="https://www.tutorialspoint.com/index.htm">Must for every <i>Learner</i> Really Liked it</a></body></html>
>>> Bsoup.a.contents
['Must for every ', <i>Learner</i>, ' Really Liked it']
NavigableString () и .new_tag ()
Если вы хотите добавить строку в документ, это можно легко сделать с помощью конструктора append () или NavigableString () -
>>> soup = BeautifulSoup("<b></b>")
>>> tag = soup.b
>>> tag.append("Start")
>>>
>>> new_string = NavigableString(" Your")
>>> tag.append(new_string)
>>> tag
<b>Start Your</b>
>>> tag.contents
['Start', ' Your']
Note: Если вы обнаружите какое-либо имя Error при доступе к функции NavigableString (), выполните следующие действия:
NameError: имя NavigableString не определено
Просто импортируйте каталог NavigableString из пакета bs4 -
>>> from bs4 import NavigableString
Мы можем решить указанную выше ошибку.
Вы можете добавить комментарии к существующему тегу или добавить какой-нибудь другой подкласс NavigableString, просто вызовите конструктор.
>>> from bs4 import Comment
>>> adding_comment = Comment("Always Learn something Good!")
>>> tag.append(adding_comment)
>>> tag
<b>Start Your<!--Always Learn something Good!--></b>
>>> tag.contents
['Start', ' Your', 'Always Learn something Good!']
Добавление полностью нового тега (не добавление к существующему тегу) может быть выполнено с помощью встроенного метода Beautifulsoup BeautifulSoup.new_tag () -
>>> soup = BeautifulSoup("<b></b>")
>>> Otag = soup.b
>>>
>>> Newtag = soup.new_tag("a", href="https://www.tutorialspoint.com")
>>> Otag.append(Newtag)
>>> Otag
<b><a href="https://www.tutorialspoint.com"></a></b>
Требуется только первый аргумент, имя тега.
вставить ()
Подобно методу .insert () в списке Python, tag.insert () будет вставлять новый элемент, однако, в отличие от tag.append (), новый элемент не обязательно помещается в конец родительского содержимого. Новый элемент можно добавить в любую позицию.
>>> markup = '<a href="https://www.djangoproject.com/community/">Django Official website <i>Huge Community base</i></a>'
>>> soup = BeautifulSoup(markup)
>>> tag = soup.a
>>>
>>> tag.insert(1, "Love this framework ")
>>> tag
<a href="https://www.djangoproject.com/community/">Django Official website Love this framework <i>Huge Community base</i></a>
>>> tag.contents
['Django Official website ', 'Love this framework ', <i>Huge Community base</i
>]
>>>
insert_before () и insert_after ()
Чтобы вставить тег или строку непосредственно перед чем-то в дереве синтаксического анализа, мы используем insert_before () -
>>> soup = BeautifulSoup("Brave")
>>> tag = soup.new_tag("i")
>>> tag.string = "Be"
>>>
>>> soup.b.string.insert_before(tag)
>>> soup.b
<b><i>Be</i>Brave</b>
Аналогично, чтобы вставить тег или строку сразу после чего-либо в дереве синтаксического анализа, используйте insert_after ().
>>> soup.b.i.insert_after(soup.new_string(" Always "))
>>> soup.b
<b><i>Be</i> Always Brave</b>
>>> soup.b.contents
[<i>Be</i>, ' Always ', 'Brave']
Чисто()
Чтобы удалить содержимое тега, используйте tag.clear () -
>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical&lr;/i> Contents</a>'
>>> soup = BeautifulSoup(markup)
>>> tag = soup.a
>>> tag
<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical</i> Contents</a>
>>>
>>> tag.clear()
>>> tag
<a href="https://www.tutorialspoint.com/index.htm"></a>
extract ()
Чтобы удалить тег или строки из дерева, используйте PageElement.extract ().
>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">For <i&gr;technical & Non-technical</i> Contents</a>'
>>> soup = BeautifulSoup(markup)
>>> a_tag = soup.a
>>>
>>> i_tag = soup.i.extract()
>>>
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">For Contents</a>
>>>
>>> i_tag
<i>technical & Non-technical</i>
>>>
>>> print(i_tag.parent)
None
разложить ()
Tag.decompose () удаляет тег из дерева и удаляет все его содержимое.
>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical</i> Contents</a>'
>>> soup = BeautifulSoup(markup)
>>> a_tag = soup.a
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">For <i>technical & Non-technical</i> Contents</a>
>>>
>>> soup.i.decompose()
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">For Contents</a>
>>>
Заменить()
Как следует из названия, функция pageElement.replace_with () заменит старый тег или строку новым тегом или строкой в дереве -
>>> markup = '<a href="https://www.tutorialspoint.com/index.htm">Complete Python <i>Material</i></a>'
>>> soup = BeautifulSoup(markup)
>>> a_tag = soup.a
>>>
>>> new_tag = soup.new_tag("Official_site")
>>> new_tag.string = "https://www.python.org/"
>>> a_tag.i.replace_with(new_tag)
<i>Material</i>
>>>
>>> a_tag
<a href="https://www.tutorialspoint.com/index.htm">Complete Python <Official_site>https://www.python.org/</Official_site></a>
В приведенном выше выводе вы заметили, что replace_with () возвращает тег или строку, которая была заменена (например, «Материал» в нашем случае), поэтому вы можете проверить ее или добавить обратно в другую часть дерева.
заворачивать()
PageElement.wrap () заключил элемент в указанный вами тег и возвращает новую оболочку -
>>> soup = BeautifulSoup("<p>tutorialspoint.com</p>")
>>> soup.p.string.wrap(soup.new_tag("b"))
<b>tutorialspoint.com</b>
>>>
>>> soup.p.wrap(soup.new_tag("Div"))
<Div><p><b>tutorialspoint.com</b></p></Div>
разворачивать ()
Tag.unwrap () прямо противоположен wrap () и заменяет тег тем, что внутри этого тега.
>>> soup = BeautifulSoup('<a href="https://www.tutorialspoint.com/">I liked <i>tutorialspoint</i></a>')
>>> a_tag = soup.a
>>>
>>> a_tag.i.unwrap()
<i></i>
>>> a_tag
<a href="https://www.tutorialspoint.com/">I liked tutorialspoint</a>
Вы заметили, что, как и replace_with (), unwrap () возвращает тег, который был заменен.
Ниже приведен еще один пример unwrap (), чтобы лучше понять это -
>>> soup = BeautifulSoup("<p>I <strong>AM</strong> a <i>text</i>.</p>")
>>> soup.i.unwrap()
<i></i>
>>> soup
<html><body><p>I <strong>AM</strong> a text.</p></body></html>
unwrap () хорош для удаления разметки.
Все документы HTML или XML написаны в определенной кодировке, такой как ASCII или UTF-8. Однако когда вы загружаете этот HTML / XML-документ в BeautifulSoup, он был преобразован в Unicode.
>>> markup = "<p>I will display £</p>"
>>> Bsoup = BeautifulSoup(markup)
>>> Bsoup.p
<p>I will display £</p>
>>> Bsoup.p.string
'I will display £'
Вышеуказанное поведение связано с тем, что BeautifulSoup внутренне использует подбиблиотеку Unicode, Dammit, чтобы определять кодировку документа и затем преобразовывать ее в Unicode.
Однако не всегда Unicode, черт возьми, угадывает правильно. Поскольку документ просматривается побайтово, чтобы угадать кодировку, это занимает много времени. Вы можете сэкономить время и избежать ошибок, если вы уже знаете кодировку, передав ее в конструктор BeautifulSoup как from_encoding.
Ниже приведен один пример, в котором BeautifulSoup ошибочно идентифицирует документ ISO-8859-8 как ISO-8859-7 -
>>> markup = b"<h1>\xed\xe5\xec\xf9</h1>"
>>> soup = BeautifulSoup(markup)
>>> soup.h1
<h1>νεμω</h1>
>>> soup.original_encoding
'ISO-8859-7'
>>>
Чтобы решить указанную выше проблему, передайте ее BeautifulSoup с помощью from_encoding -
>>> soup = BeautifulSoup(markup, from_encoding="iso-8859-8")
>>> soup.h1
<h1>ולש </h1>
>>> soup.original_encoding
'iso-8859-8'
>>>
Еще одна новая функция, добавленная в BeautifulSoup 4.4.0, - exclude_encoding. Его можно использовать, если вы не знаете правильную кодировку, но уверены, что Unicode, Dammit, показывает неправильный результат.
>>> soup = BeautifulSoup(markup, exclude_encodings=["ISO-8859-7"])
Кодировка вывода
Результатом BeautifulSoup является документ UTF-8, независимо от того, какой документ был введен в BeautifulSoup. Ниже документ, где есть символы полировки в формате ISO-8859-2.
html_markup = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-2">
</HEAD>
<BODY>
ą ć ę ł ń ó ś ź ż Ą Ć Ę Ł Ń Ó Ś Ź Ż
</BODY>
</HTML>
"""
>>> soup = BeautifulSoup(html_markup)
>>> print(soup.prettify())
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
</head>
<body>
ą ć ę ł ń ó ś ź ż Ą Ć Ę Ł Ń Ó Ś Ź Ż
</body>
</html>
В приведенном выше примере, если вы заметили, тег <meta> был переписан, чтобы отразить сгенерированный документ из BeautifulSoup теперь в формате UTF-8.
Если вам не нужен сгенерированный вывод в UTF-8, вы можете назначить желаемую кодировку в prettify ().
>>> print(soup.prettify("latin-1"))
b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n<html>\n <head>\n <meta content="text/html; charset=latin-1" http-equiv="content-type"/>\n </head>\n <body>\n ą ć ę ł ń \xf3 ś ź ż Ą Ć Ę Ł Ń \xd3 Ś Ź Ż\n </body>\n</html>\n'
В приведенном выше примере мы закодировали весь документ, однако вы можете кодировать любой конкретный элемент в супе, как если бы они были строкой Python -
>>> soup.p.encode("latin-1")
b'<p>0My first paragraph.</p>'
>>> soup.h1.encode("latin-1")
b'<h1>My First Heading</h1>'
Любые символы, которые не могут быть представлены в выбранной вами кодировке, будут преобразованы в числовые ссылки на объекты XML. Ниже один такой пример -
>>> markup = u"<b>\N{SNOWMAN}</b>"
>>> snowman_soup = BeautifulSoup(markup)
>>> tag = snowman_soup.b
>>> print(tag.encode("utf-8"))
b'<b>\xe2\x98\x83</b>'
Если вы попытаетесь закодировать указанное выше в «latin-1» или «ascii», будет сгенерировано «☃», указывающее, что для этого нет представления.
>>> print (tag.encode("latin-1"))
b'<b>☃</b>'
>>> print (tag.encode("ascii"))
b'<b>☃</b>'
Юникод, черт возьми
Unicode, Dammit используется в основном, когда входящий документ находится в неизвестном формате (в основном на иностранном языке), и мы хотим кодировать в каком-то известном формате (Unicode), а также нам не нужен Beautifulsoup для всего этого.
Отправной точкой любого проекта BeautifulSoup является объект BeautifulSoup. Объект BeautifulSoup представляет входной документ HTML / XML, используемый для его создания.
Мы можем передать строку или объект в виде файла для Beautiful Soup, где файлы (объекты) либо хранятся локально на нашем компьютере, либо на веб-странице.
Наиболее распространенные объекты BeautifulSoup:
- Tag
- NavigableString
- BeautifulSoup
- Comment
Сравнение объектов на равенство
Согласно красивому супу, две доступные для навигации строковые или теговые объекты равны, если они представляют одну и ту же разметку HTML / XML.
Теперь давайте посмотрим на приведенный ниже пример, где два тега <b> рассматриваются как равные, даже если они находятся в разных частях дерева объектов, потому что оба они выглядят как «<b> Java </b>».
>>> markup = "<p>Learn Python and <b>Java</b> and advanced <b>Java</b>! from Tutorialspoint</p>"
>>> soup = BeautifulSoup(markup, "html.parser")
>>> first_b, second_b = soup.find_all('b')
>>> print(first_b == second_b)
True
>>> print(first_b.previous_element == second_b.previous_element)
False
Однако, чтобы проверить, относятся ли две переменные к одним и тем же объектам, вы можете использовать следующее:
>>> print(first_b is second_b)
False
Копирование объектов Beautiful Soup
Чтобы создать копию любого тега или NavigableString, используйте функцию copy.copy (), как показано ниже -
>>> import copy
>>> p_copy = copy.copy(soup.p)
>>> print(p_copy)
<p>Learn Python and <b>Java</b> and advanced <b>Java</b>! from Tutorialspoint</p>
>>>
Хотя две копии (исходная и скопированная) содержат одинаковую разметку, однако они не представляют один и тот же объект -
>>> print(soup.p == p_copy)
True
>>>
>>> print(soup.p is p_copy)
False
>>>
Единственное реальное отличие состоит в том, что копия полностью отделяется от исходного дерева объектов Beautiful Soup, как если бы для него был вызван extract ().
>>> print(p_copy.parent)
None
Вышеуказанное поведение связано с двумя разными объектами тегов, которые не могут занимать одно и то же пространство одновременно.
Существует несколько ситуаций, когда вы хотите извлечь определенные типы информации (только теги <a>) с помощью Beautifulsoup4. Класс SoupStrainer в Beautifulsoup позволяет анализировать только определенную часть входящего документа.
Один из способов - создать SoupStrainer и передать его конструктору Beautifulsoup4 в качестве аргумента parse_only.
СупСтрейнер
SoupStrainer сообщает BeautifulSoup, какие части извлекаются, а дерево синтаксического анализа состоит только из этих элементов. Если вы сузите необходимую информацию до определенной части HTML, это ускорит результат поиска.
product = SoupStrainer('div',{'id': 'products_list'})
soup = BeautifulSoup(html,parse_only=product)
Вышеупомянутые строки кода будут анализировать только заголовки с сайта продукта, которые могут находиться внутри поля тега.
Точно так же, как и выше, мы можем использовать другие объекты soupStrainer для анализа конкретной информации из HTML-тега. Ниже приведены некоторые из примеров -
from bs4 import BeautifulSoup, SoupStrainer
#Only "a" tags
only_a_tags = SoupStrainer("a")
#Will parse only the below mentioned "ids".
parse_only = SoupStrainer(id=["first", "third", "my_unique_id"])
soup = BeautifulSoup(my_document, "html.parser", parse_only=parse_only)
#parse only where string length is less than 10
def is_short_string(string):
return len(string) < 10
only_short_strings =SoupStrainer(string=is_short_string)
Обработка ошибок
В BeautifulSoup необходимо обрабатывать два основных типа ошибок. Эти две ошибки связаны не с вашим сценарием, а со структурой фрагмента, поскольку BeautifulSoup API выдает ошибку.
Две основные ошибки заключаются в следующем:
AttributeError
Это происходит, когда точечная нотация не находит родственный тег для текущего HTML-тега. Например, вы могли столкнуться с этой ошибкой из-за отсутствия «тега привязки», cost-key будет выдавать ошибку при прохождении и требует тега привязки.
KeyError
Эта ошибка возникает, если отсутствует обязательный атрибут тега HTML. Например, если у нас нет атрибута data-pid во фрагменте, ключ pid выдаст ошибку ключа.
Чтобы избежать двух перечисленных выше ошибок при синтаксическом анализе результата, этот результат будет пропущен, чтобы убедиться, что искаженный фрагмент не вставлен в базы данных -
except(AttributeError, KeyError) as er:
pass
диагностировать ()
Всякий раз, когда мы сталкиваемся с трудностями в понимании того, что BeautifulSoup делает с нашим документом или HTML, просто передайте это в функцию diagnose (). Передав файл документа в функцию Diagnose (), мы можем показать, как список различных парсеров обрабатывает документ.
Ниже приведен один пример, демонстрирующий использование функции Diagnose () -
from bs4.diagnose import diagnose
with open("20 Books.html",encoding="utf8") as fp:
data = fp.read()
diagnose(data)
Вывод
Ошибка разбора
Есть два основных типа ошибок синтаксического анализа. Вы можете получить исключение, подобное HTMLParseError, когда вы загрузите свой документ в BeautifulSoup. Вы также можете получить неожиданный результат, когда дерево синтаксического анализа BeautifulSoup будет сильно отличаться от ожидаемого результата от документа синтаксического анализа.
Ни одна из ошибок синтаксического анализа не вызвана BeautifulSoup. Это из-за внешнего парсера, который мы используем (html5lib, lxml), поскольку BeautifulSoup не содержит кода парсера. Один из способов устранить вышеуказанную ошибку синтаксического анализа - использовать другой анализатор.
from HTMLParser import HTMLParser
try:
from HTMLParser import HTMLParseError
except ImportError, e:
# From python 3.5, HTMLParseError is removed. Since it can never be
# thrown in 3.5, we can just define our own class as a placeholder.
class HTMLParseError(Exception):
pass
Встроенный в Python синтаксический анализатор HTML вызывает две наиболее распространенные ошибки синтаксического анализа: HTMLParser.HTMLParserError: неправильный начальный тег и HTMLParser.HTMLParserError: плохой конечный тег, и для решения этой проблемы в основном используется другой синтаксический анализатор: lxml или html5lib.
Другой распространенный тип неожиданного поведения - вы не можете найти тег, который, как вы знаете, есть в документе. Однако при запуске find_all () возвращает [] или find () возвращает None.
Это может быть связано с тем, что встроенный в Python анализатор HTML иногда пропускает теги, которые он не понимает.
Ошибка парсера XML
По умолчанию пакет BeautifulSoup анализирует документы как HTML, однако он очень прост в использовании и очень элегантно обрабатывает плохо сформированный XML с помощью beautifulsoup4.
Чтобы проанализировать документ как XML, вам понадобится парсер lxml, и вам просто нужно передать «xml» в качестве второго аргумента конструктору Beautifulsoup -
soup = BeautifulSoup(markup, "lxml-xml")
или же
soup = BeautifulSoup(markup, "xml")
Одна распространенная ошибка синтаксического анализа XML -
AttributeError: 'NoneType' object has no attribute 'attrib'
Это может произойти в случае, если какой-то элемент отсутствует или не определен при использовании функции find () или findall ().
Другие ошибки парсинга
Ниже приведены некоторые из других ошибок синтаксического анализа, которые мы собираемся обсудить в этом разделе.
Экологическая проблема
Помимо вышеупомянутых ошибок синтаксического анализа, вы можете столкнуться с другими проблемами синтаксического анализа, такими как проблемы со средой, когда ваш сценарий может работать в одной операционной системе, но не в другой операционной системе, или может работать в одной виртуальной среде, но не в другой виртуальной среде, или может не работать. вне виртуальной среды. Все эти проблемы могут быть вызваны тем, что в двух средах доступны разные библиотеки анализатора.
Рекомендуется знать или проверять парсер по умолчанию в вашей текущей рабочей среде. Вы можете проверить текущий синтаксический анализатор по умолчанию, доступный для текущей рабочей среды, или явно передать требуемую библиотеку синтаксического анализатора в качестве второго аргумента конструктору BeautifulSoup.
Без учета регистра
Поскольку теги и атрибуты HTML не чувствительны к регистру, все три анализатора HTML преобразуют имена тегов и атрибутов в нижний регистр. Однако, если вы хотите сохранить теги и атрибуты в смешанном или верхнем регистре, лучше анализировать документ как XML.
UnicodeEncodeError
Давайте посмотрим на сегмент кода ниже -
soup = BeautifulSoup(response, "html.parser")
print (soup)
Вывод
UnicodeEncodeError: 'charmap' codec can't encode character '\u011f'
Вышеупомянутая проблема может быть связана с двумя основными ситуациями. Возможно, вы пытаетесь распечатать символ Юникода, который ваша консоль не умеет отображать. Во-вторых, вы пытаетесь записать в файл и передаете символ Unicode, который не поддерживается вашей кодировкой по умолчанию.
Один из способов решить вышеуказанную проблему - закодировать текст / символ ответа перед приготовлением супа, чтобы получить желаемый результат, следующим образом:
responseTxt = response.text.encode('UTF-8')
KeyError: [attr]
Это вызвано доступом к тегу ['attr'], когда рассматриваемый тег не определяет атрибут attr. Наиболее частые ошибки: «KeyError: 'href'» и «KeyError: 'class'». Используйте tag.get ('attr'), если вы не уверены, что attr определен.
for item in soup.fetch('a'):
try:
if (item['href'].startswith('/') or "tutorialspoint" in item['href']):
(...)
except KeyError:
pass # or some other fallback action
AttributeError
Вы можете столкнуться с AttributeError следующим образом -
AttributeError: 'list' object has no attribute 'find_all'
Вышеупомянутая ошибка в основном возникает из-за того, что вы ожидали, что find_all () вернет один тег или строку. Однако soup.find_all возвращает список элементов Python.
Все, что вам нужно сделать, это перебрать список и получить данные из этих элементов.