Рубрики
Без рубрики

Руководство по синтаксическому анализу HTML с помощью BeautifulSoup в Python

Эта статья даст вам ускоренный курс по веб – скрейпингу в Python с помощью BeautifulSoup-популярной библиотеки Python для синтаксического анализа HTML и XML.

Автор оригинала: Sathiya Sarathi Gunasekaran.

Вступление

Веб-скребок-это программный сбор информации с различных веб-сайтов. В то время как существует множество библиотек и фреймворков на различных языках, которые могут извлекать веб-данные, Python уже давно является популярным выбором из-за его множества опций для веб-скрейпинга.

Эта статья даст вам ускоренный курс по веб – скрейпингу в Python с помощью BeautifulSoup – популярной библиотеки Python для синтаксического анализа HTML и XML.

Этическое Выскабливание паутины

Веб-скребок вездесущ и дает нам данные, которые мы получили бы с помощью API. Однако, как добропорядочные граждане Интернета, мы обязаны уважать владельцев сайтов, с которых мы соскребаем деньги. Вот некоторые принципы, которых должен придерживаться веб-скребок:

  • Не претендуйте на соскобленный контент как на свой собственный. Владельцы веб – сайтов иногда тратят много времени на создание статей, сбор сведений о продуктах или сбор другого контента. Мы должны уважать их труд и оригинальность.
  • Не царапайте сайт, который не хочет быть очищенным. Веб-сайты иногда поставляются с robots.txt файл – который определяет части веб-сайта, которые могут быть очищены. Многие веб-сайты также имеют Условия использования, которые могут не разрешать выскабливание. Мы должны уважать сайты, которые не хотят быть очищенными.
  • Есть ли уже доступный API? Великолепно, нам не нужно писать скребок. API-интерфейсы создаются для обеспечения доступа к данным контролируемым образом, определенным владельцами данных. Мы предпочитаем использовать API, если они доступны.
  • Выполнение запросов на веб-сайт может привести к снижению производительности веб-сайта. Веб-скребок, который делает слишком много запросов, может быть таким же изнурительным, как и DDOS-атака. Мы должны скрести ответственно, чтобы не нарушить нормальное функционирование веб-сайта.

Обзор прекрасного супа

HTML-содержимое веб-страниц может быть проанализировано и очищено с помощью BeautifulSoup. В следующем разделе мы рассмотрим те функции, которые полезны для очистки веб-страниц.

Что делает Красивый суп таким полезным, так это множество функций, которые он предоставляет для извлечения данных из HTML. Это изображение ниже иллюстрирует некоторые из функций, которые мы можем использовать:

BeautifulSoup - Обзор

Давайте возьмемся за руки и посмотрим, как мы можем анализировать HTML с помощью BeautifulSoup. Рассмотрим следующую HTML – страницу, сохраненную в файл как doc.html :



  Head's title



  

Body's title

line begins 1 2 3

line ends

Следующие фрагменты кода тестируются на Ubuntu 20.04.1 LTS . Вы можете установить модуль BeautifulSoup , набрав в терминале следующую команду:

$ pip3 install beautifulsoup4

HTML-файл doc.html нужно быть готовым. Это делается путем передачи файла в конструктор BeautifulSoup , давайте использовать для этого интерактивную оболочку Python, чтобы мы могли мгновенно распечатать содержимое определенной части страницы:

from bs4 import BeautifulSoup

with open("doc.html") as fp:
    soup = BeautifulSoup(fp, "html.parser")

Теперь мы можем использовать BeautifulSoup для навигации по нашему сайту и извлечения данных.

Переход к определенным тегам

Из объекта soup, созданного в предыдущем разделе, давайте получим тег заголовка doc.html :

soup.head.title   # returns Head's title

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

Навигация По Определенным Тегам

BeautifulSoup является мощным, потому что ваши объекты Python соответствуют вложенной структуре HTML-документа, который мы очищаем.

Чтобы получить текст первого тега , введите следующее:

soup.body.a.text  # returns '1'

Чтобы получить заголовок в теге body HTML (обозначаемом классом “title”), введите в терминале следующее:

soup.body.p.b     # returns Body's title

Для глубоко вложенных HTML-документов навигация может быстро стать утомительной. К счастью, Beautiful Soup поставляется с функцией поиска, поэтому нам не нужно перемещаться, чтобы получить HTML-элементы.

Поиск элементов тегов

Метод find_all() принимает HTML-тег в качестве строкового аргумента и возвращает список элементов, совпадающих с предоставленным тегом. Например, если мы хотим, чтобы все a теги были в doc.html :

soup.find_all("a")

Мы увидим этот список тегов a в качестве вывода:

[1, 2, 3]

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

Поиск элементов тегов

Мы также можем искать теги определенного класса, предоставляя аргумент class_ . Beautiful Soup использует class_ потому что class является зарезервированным ключевым словом в Python. Давайте поищем все теги a , имеющие класс “элемент”:

soup.find_all("a", class_="element")

Поскольку у нас есть только две ссылки с классом “элемент”, вы увидите этот вывод:

[1, 2]

Что делать, если мы хотим получить ссылки, встроенные в теги a ? Давайте получим атрибут ссылки href с помощью опции find () . Он работает так же, как find_all () , но возвращает первый соответствующий элемент вместо списка. Введите это в свою оболочку:

soup.find("a", href=True)["href"] # returns http://example.com/element1

Функции find() и find_all() также принимают регулярное выражение вместо строки. За кулисами текст будет отфильтрован с помощью метода search() скомпилированного регулярного выражения. Например:

import re

for tag in soup.find_all(re.compile("^b")):
    print(tag)

Список после итерации извлекает теги, начинающиеся с символа b , который включает в себя и :


 

Body's title

line begins 1 2 3

line ends

Body's title

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

Получение всего текста

Функция get_text() извлекает весь текст из HTML-документа. Давайте получим весь текст HTML документа:

soup.get_text()

Ваш вывод должен быть таким:

Head's title


Body's title
line begins
      1
2
3
 line ends

Иногда печатаются символы новой строки, поэтому ваш вывод также может выглядеть следующим образом:

"\n\nHead's title\n\n\nBody's title\nline begins\n    1\n2\n3\n line ends\n\n"

Теперь, когда у нас есть представление о том, как использовать Красивый суп, давайте наскребем сайт!

Красивый суп в действии – Выскабливание списка книг

Теперь, когда мы освоили компоненты BeautifulSoup, пришло время применить наши знания на практике. Давайте построим скребок для извлечения данных из https://books.toscrape.com/ и сохраните его в CSV-файл. Сайт содержит случайные данные о книгах и является отличным местом для тестирования ваших методов веб-скребка.

Сначала создайте новый файл с именем scraper.py . Давайте импортируем все библиотеки, которые нам нужны для этого скрипта:

import requests
import time
import csv
import re
from bs4 import BeautifulSoup

В модулях, упомянутых выше:

  • requests – выполняет URL-запрос и извлекает HTML-код сайта
  • time – ограничивает, сколько раз мы выскабливаем страницу одновременно
  • csv – помогает нам экспортировать наши очищенные данные в CSV-файл
  • re – позволяет писать регулярные выражения, которые пригодятся для подбора текста по его шаблону
  • bs4 – ваш покорный слуга, модуль выскабливания для разбора HTML

Вы бы уже установили bs4 , а time , csv и re являются встроенными пакетами в Python. Вам нужно будет установить модуль requests непосредственно следующим образом:

$ pip3 install requests

Прежде чем начать, вам нужно понять, как структурирован HTML – код веб-страницы. В вашем браузере давайте перейдем к http://books.toscrape.com/catalogue/page-1.html . Затем щелкните правой кнопкой мыши компоненты веб-страницы, подлежащей очистке, и нажмите кнопку inspect , чтобы понять иерархию тегов, как показано ниже.

Это покажет вам базовый HTML-код для того, что вы проверяете. На следующем рисунке показаны эти шаги:

Понимание HTML-тегов

Изучая HTML, мы узнаем, как получить доступ к URL-адресу книги, изображению обложки, названию, рейтингу, цене и другим полям из HTML. Давайте напишем функцию, которая очищает элемент книги и извлекает его данные:

def scrape(source_url, soup):  # Takes the driver and the subdomain for concats as params
    # Find the elements of the article tag
    books = soup.find_all("article", class_="product_pod")

    # Iterate over each book article tag
    for each_book in books:
        info_url = source_url+"/"+each_book.h3.find("a")["href"]
        cover_url = source_url+"/catalogue" + \
            each_book.a.img["src"].replace("..", "")

        title = each_book.h3.find("a")["title"]
        rating = each_book.find("p", class_="star-rating")["class"][1]
        # can also be written as : each_book.h3.find("a").get("title")
        price = each_book.find("p", class_="price_color").text.strip().encode(
            "ascii", "ignore").decode("ascii")
        availability = each_book.find(
            "p", class_="instock availability").text.strip()

        # Invoke the write_to_csv function
        write_to_csv([info_url, cover_url, title, rating, price, availability])

Последняя строка приведенного выше фрагмента указывает на функцию записи списка очищенных строк в CSV-файл. Давайте добавим эту функцию прямо сейчас:

def write_to_csv(list_input):
    # The scraped info will be written to a CSV here.
    try:
        with open("allBooks.csv", "a") as fopen:  # Open the csv file.
            csv_writer = csv.writer(fopen)
            csv_writer.writerow(list_input)
    except:
        return False

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

Чтобы сделать это, давайте посмотрим на URL-адрес, для которого мы пишем этот скребок:

"http://books.toscrape.com/catalogue/page-1.html"

Единственным изменяющимся элементом в URL-адресе является номер страницы. Мы можем отформатировать URL-адрес динамически, чтобы он стал seed URL :

"http://books.toscrape.com/catalogue/page-{}.html".format(str(page_number))

Этот URL-адрес в формате строки с номером страницы можно получить с помощью метода requests.get() . Затем мы можем создать новый объект BeautifulSoup . Каждый раз, когда мы получаем объект супа, проверяется наличие кнопки “далее”, чтобы мы могли остановиться на последней странице. Мы отслеживаем счетчик для номера страницы, который увеличивается на 1 после успешного выскабливания страницы.

def browse_and_scrape(seed_url, page_number=1):
    # Fetch the URL - We will be using this to append to images and info routes
    url_pat = re.compile(r"(http://.*\.com)")
    source_url = url_pat.search(seed_url).group(0)

   # Page_number from the argument gets formatted in the URL & Fetched
    formatted_url = seed_url.format(str(page_number))

    try:
        html_text = requests.get(formatted_url).text
        # Prepare the soup
        soup = BeautifulSoup(html_text, "html.parser")
        print(f"Now Scraping - {formatted_url}")

        # This if clause stops the script when it hits an empty page
        if soup.find("li", class_="next") != None:
            scrape(source_url, soup)     # Invoke the scrape function
            # Be a responsible citizen by waiting before you hit again
            time.sleep(3)
            page_number += 1
            # Recursively invoke the same function with the increment
            browse_and_scrape(seed_url, page_number)
        else:
            scrape(source_url, soup)     # The script exits here
            return True
        return True
    except Exception as e:
        return e

Приведенная выше функция browse_and_scrape () рекурсивно вызывается до тех пор, пока функция soup.find("li", class_="next") не вернет None . В этот момент код очистит оставшуюся часть веб-страницы и выйдет.

Для последней части головоломки мы инициируем поток выскабливания. Мы определяем seed_url и вызываем browse_and_scrape () , чтобы получить данные. Это делается под блоком if __name__ :

if __name__ == "__main__":
    seed_url = "http://books.toscrape.com/catalogue/page-{}.html"
    print("Web scraping has begun")
    result = browse_and_scrape(seed_url)
    if result == True:
        print("Web scraping is now complete!")
    else:
        print(f"Oops, That doesn't seem right!!! - {result}")

Если вы хотите узнать больше о блоке if __name__ , ознакомьтесь с нашим руководством по как он работает .

Вы можете выполнить сценарий, как показано ниже в вашем терминале, и получить вывод в виде:

$ python scraper.py
Web scraping has begun
Now Scraping - http://books.toscrape.com/catalogue/page-1.html
Now Scraping - http://books.toscrape.com/catalogue/page-2.html
Now Scraping - http://books.toscrape.com/catalogue/page-3.html
.
.
.
Now Scraping - http://books.toscrape.com/catalogue/page-49.html
Now Scraping - http://books.toscrape.com/catalogue/page-50.html
Web scraping is now complete!

Очищенные данные можно найти в текущем рабочем каталоге под именем файла all Books.csv . Вот пример содержимого файла:

http://books.toscrape.com/a-light-in-the-attic_1000/index.html,http://books.toscrape.com/catalogue/media/cache/2c/da/2cdad67c44b002e7ead0cc35693c0e8b.jpg,A Light in the Attic,Three,51.77,In stock
http://books.toscrape.com/tipping-the-velvet_999/index.html,http://books.toscrape.com/catalogue/media/cache/26/0c/260c6ae16bce31c8f8c95daddd9f4a1c.jpg,Tipping the Velvet,One,53.74,In stock
http://books.toscrape.com/soumission_998/index.html,http://books.toscrape.com/catalogue/media/cache/3e/ef/3eef99c9d9adef34639f510662022830.jpg,Soumission,One,50.10,In stock

Молодец! Если вы хотите взглянуть на код скребка в целом, вы можете найти его на GitHub .

Вывод

В этом уроке мы изучили этику написания хороших веб-скребков. Затем мы использовали BeautifulSoup для извлечения данных из HTML-файла, используя свойства объекта Beautifulsoup и его различные методы, такие как find () , find_all() и get_text() . Затем мы построили скребок, который извлекает список книг онлайн и экспортирует их в CSV.

Веб-скребок-это полезный навык, который помогает в различных действиях, таких как извлечение данных, таких как API, выполнение QA на веб-сайте, проверка неработающих URL-адресов на веб-сайте и многое другое. Какой следующий скребок вы собираетесь построить?