Этот пост изначально был опубликован в моем блоге - https://thecodingpie.com

Вы когда-нибудь задумывались, как автоматизировать процесс очистки веб-сайта, сбора данных и их экспорта в полезный формат, например CSV? Если вы занимаетесь наукой о данных / машинным обучением, возможно, вы были в этой ситуации несколько раз.

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

И хорошо то, что вам больше не нужно вручную сканировать веб-страницы!

Звучит интересно? Тогда приступим.

Вы можете скачать готовый код здесь из моего репозитория на Github - Web Scraper

Что такое веб-парсинг

Веб-парсинг - это процесс сбора полезной / необходимой информации с любого веб-сайта в Интернете. Как и любой другой процесс, есть два способа сделать это: один - вручную скопировать и вставить необходимые данные с веб-сайта. А другой способ, путь легенд, - это умно автоматизировать!

Я надеюсь, что вы хотите попасть во вторую категорию. Но при этом есть некоторые проблемы ...

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

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

Альтернатива парсинга

Если есть так много проблем, есть ли альтернатива? Да, есть альтернатива под названием API. Интерфейс прикладного программирования - это единственный законный и стабильный способ получения данных с любого веб-сайта.

Большинство веб-сайтов предоставляют API, с помощью которого вы можете получать нужные данные в более удобном формате, таком как JSON или XML. Но есть одна загвоздка: возможно, вам придется заплатить деньги. Конечно, может быть бесплатный план, но в конечном итоге вам придется заплатить им, чтобы использовать их ценные данные.

Вот где пригодится концепция веб-скрапинга!

Что мы собираемся построить

Мы узнаем все о веб-парсинге с помощью Python и BeautifulSoup4, создав реальный проект.

Я не хочу вызывать у вас головную боль, обучая чистить постоянно меняющийся динамический веб-сайт. Итак, я создал статический веб-сайт с фильмами под названием TopMovies, который содержит список 25 лучших фильмов IMDb. Это веб-сайт, который мы собираемся очистить. Поэтому, прежде чем двигаться дальше, пожалуйста, сначала изучите его - TopMovies.

На сайте TopMovies есть список 25 лучших фильмов IMDb. Каждый фильм содержит следующие детали:

  • title
  • genre
  • rating
  • length - время выполнения фильма
  • year
  • budget
  • gross
  • img

Мы собираемся соскрести эти детали с того сайта TopMovies. Затем, после получения всех этих деталей, мы собираемся экспортировать его в полезный формат, например CSV, чтобы вы могли позже импортировать его в свой проект по науке о данных и без проблем делать некоторые прогнозы!

Короче говоря, мы собираемся очистить следующий веб-сайт:

Экспортируйте извлеченные данные в файл CSV следующим образом:

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

Если вы не занимаетесь наукой о данных / машинным обучением, не беспокойтесь об этом последнем изображении, просто забыли его!

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

Как мы это сделаем?

Это очень просто:

  • Сначала мы получим нужную веб-страницу с помощью библиотеки requests.
  • Затем мы превратим эту страницу в объект BeautifulSoup с помощью подходящего парсера, такого как lxml. Это значительно упростит процесс соскабливания.
  • Затем мы очистим все необходимые данные от этого объекта супа.
  • Наконец, мы экспортируем все очищенные данные в файл с именем top25.csv с помощью модуля csv.

Вот и все!

Предпосылки

  • Вы должны хорошо разбираться в python3.
  • Вы должны хорошо разбираться в HTML и немного в CSS.
  • На вашем компьютере должна быть установлена ​​версия python3.4 или более поздняя. Вы можете прочитать этот пост, чтобы узнать, как настроить python3 в любой операционной системе - https://realpython.com/installing-python/
  • У вас должен быть установлен venv.
  • Наконец, вам понадобится современный редактор кода, например код Visual Studio. Вы можете скачать здесь код Visual Studio в зависимости от вашей операционной системы - https://code.visualstudio.com/download.

Теперь, когда все это настроено, давайте приступим.

Начальные настройки

  • Сначала создайте папку с именем web_scraper в любом месте на вашем компьютере.
  • Затем откройте его в коде Visual Studio.
  • Теперь давайте создадим новую виртуальную среду с помощью venv и активируем ее. Для этого:
  • В текстовом редакторе откройте Терминал ›Новый терминал.
  • Затем введите:
python3 -m venv venv

Эта команда создаст для нас виртуальную среду с именем venv.

  • Чтобы активировать его, если вы находитесь в окнах, введите следующее:
venv\Scripts\activate.bat
  • Если вы используете Linux / Mac, введите вместо этого следующее:
source venv/bin/activate

Теперь вы должны увидеть что-то вроде этого:

  • Наконец, создайте новый файл с именем scraper.py прямо внутри папки web_scraper:

Теперь у вас должна быть файловая структура, подобная этой:

Примечание. Если вы все еще не знаете, как настроить виртуальную среду, прочтите это Краткое руководство.

Вот и все, что вы сделали, теперь самое время заняться чем-то интересным!

Получение веб-страницы

Позвольте мне задать вам вопрос. Что вы сделаете в первую очередь, чтобы очистить веб-сайт вручную, я имею в виду скопировать и вставить данные с веб-сайта?

Во-первых, вам нужно открыть веб-браузер и ввести URL-адрес, верно? Потому что для того, чтобы получить данные с веб-страницы, вы должны сначала загрузить ее. И это именно то, что мы собираемся здесь делать.

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

Итак, введите следующую команду в терминале и установите модуль requests:

pip install requests

Затем в типе файла scraper.py:

import requests
# fetch the web page
page = requests.get('https://the-coding-pie.github.io/top_movies/')
  • Этот код будет get объект Response целиком из URL https://the-coding-pie.github.io/top_movies/. Но нам нужна сама веб-страница или сама веб-страница content, верно?

Чтобы получить content веб-страницу, вы должны получить доступ к content из переменной page следующим образом:

Если вы print(page.content):

тогда вы увидите HTML веб-страницы, подобный этому:

Но тут есть проблема. Если вы посмотрите на type(page.content):

Тогда вы можете увидеть, что это типа bytes:

Мы не можем проанализировать этот тип байтов! Типы байтов бесполезны, если вы не конвертируете их в какой-либо другой полезный формат / тип.

Что нам теперь делать?

BeautifulSoup на помощь!

Beautiful Soup - это библиотека Python для извлечения данных из форматов HTML и XML, как указано выше.

BeautfulSoup с помощью parser преобразует сложный документ HTML в сложное дерево объектов Python.

Примечание. Я не хочу подробно рассказывать, как работает BeautifulSoup, в этом руководстве. Если вам интересно это узнать, воспользуйтесь этой ссылкой - Official Beautiful Soup Docs.

Короче говоря, с помощью BeautfulSoup и parser мы можем легко перемещаться, искать, очищать и изменять проанализированное содержимое HTML / XML, как указано выше (байтовый тип), рассматривая все в нем как объект Python!

Итак, давайте установим BeautifulSoup4 и парсер, например lxml. Введите следующие команды в окне терминала:

pip install beautifulsoup4
pip install lxml
  • lxml - это синтаксический анализатор, рекомендованный сообществом BeautifulSoup. Также есть альтернатива, например html5lib. Но мы собираемся придерживаться парсера lxml.

Теперь введите следующий код в файл scraper.py на самом верху:

from bs4 import BeautifulSoup
  • Здесь мы импортируем bs4 из библиотеки BeautifulSoup.

Затем под этой строкой - page = requests.get(‘https://the-coding-pie.github.io/top_movies/') введите следующее:

# turn page into a BeautifulSoup object
soup = BeautifulSoup(page.content, 'lxml')
  • Здесь мы конвертируем наш page.content, имеющий тип bytes, в объект BeautifulSoup.

Давайте очистим страницу

Теперь у нас в руках вся эта веб-страница (в удобном формате). Одна из двух оставшихся работ - очистить его. Так что давай сделаем это. Нам нужно очистить веб-страницу от следующих вещей:

  • titles - все названия фильмов
  • genres - все жанры
  • ratings - все рейтинги фильмов
  • lengths - все среды выполнения фильма
  • years - за все годы выхода фильма
  • budgets - все бюджеты
  • grosses - вся общая информация
  • img_urls - src URL всего изображения.

Итак, давайте сделаем их по очереди.

Сначала давайте очистим все titles:

title, который мы ищем, находится внутри элемента HTML с именем <h3>. Подожди, откуда я это знаю?

Это просто:

  • Откройте URL-адрес, который вы хотите очистить, в браузере.

В нашем случае откройте этот сайт TopMovies. Потом:

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

  • Теперь появится новое окно:

  • И видите, я сказал вам, что title, который мы ищем, находится внутри элемента HTML с именем <h3>:

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

""" first, scraping using find_all() method """
# scrape all the titles
titles = [] 
for h3 in soup.find_all('h3'):
  titles.append(h3.string.strip())

Вот построчное объяснение приведенного выше кода:

  • Мы собираемся хранить все наши заголовки в массиве с именем titles, и это то, что мы делаем в первой строке, мы создаем этот массив titles.
  • Затем мы используем метод find_all() в объекте soup, который мы создали ранее, чтобы найти все h3 элементы, которые нам нужны. Этот find_all() метод возвращает повторяющийся список. Итак, мы перебираем все найденные h3 элементы и…
  • И в последней строке внутри for loop мы берем значение h3.string. Почему string значение? Потому что каждый h3 в целом будет таким <h3> Title inside </h3>. Но внутри этого нам нужно только самое сокровенное, правда? Итак, мы используем h3.string. Взяв его, мы .strip() удалим все конечные пробелы. Затем мы .append() это в массив titles.

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

Объект soup, который мы изначально создали с использованием данных типа HTML bytes, дает нам множество встроенных методов для легкой навигации и очистки дерева HTML. find_all() - лишь один из них. Мы изучим некоторые из них по мере продвижения по дороге.

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

Теперь мы собрали все, что нам нужно. А теперь перейдем к очистке всего genres. Введите следующий код:

# genres
genres = []
for genre in soup.find_all('p', class_='genre'):
  genres.append(genre.string.strip())
  • Это очень похоже, но на этот раз мы находим все <p> элементы с class_=’genre’. class - зарезервированное ключевое слово в Python, поэтому мы не можем его использовать, и поэтому мы ставим подчеркивание (_) после class.

Остальное говорит само за себя.

Теперь давайте очистим все ratings, но другим методом под названием select():

""" scraping using css_selector eg: select('span.class_name') """
# ratings, selecting all span with class="rating"
ratings = []
for rating in soup.select('span.rating'):
  ratings.append(rating.string.strip())
  • метод select() используется для поиска всех элементов с использованием синтаксиса, подобного селектору CSS. Здесь мы выбираем все span с таким рейтингом класса -- span.rating. Затем мы сохраняем их в списке Python с именем ratings.

Пришло время для небольшого упражнения. Используя метод select(), вы должны очистить все lengths (время выполнения фильма) и years (год выпуска фильма). Я могу дать вам два совета:

  • Каждый фильм length находится внутри span с классом length (span.length).
  • И каждый year находится внутри span с классом year (span.year).

Код будет очень похож на приведенный выше фрагмент кода. Вам просто нужно поменять соответствующие детали.

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

Решение:

# lengths, selecting all span with class="length"
lengths = []
for length in soup.select('span.length'):
  lengths.append(length.string.strip())
# years, selecting all span with class="year"
years = []
for year in soup.select('span.year'):
  years.append(year.string.strip())
  • Думаю, здесь не нужно никаких пояснений.

Осталось очистить budgets, grosses и img_urls. Здесь мы воспользуемся для этого добрым методом old find_all():

""" scraping by navigating through elements eg: div.span.string """
# budget
budgets = []
for budget in soup.find_all('div', class_='budget'):
  # from <div class="budget"></div>, get the span.string
  budgets.append(budget.span.string.strip())
# gross
grosses = []
for gross in soup.find_all('div', class_='gross'):
  grosses.append(gross.span.string.strip())

""" parsing all the "src" attribute's value of <img /> tag """
img_urls = []
for img in soup.find_all('img', class_='poster'):
  img_urls.append(img.get('src').strip())
  • Следует отметить, что в последних нескольких строках мы пытаемся получить атрибуты src img. Потому что там находится URL-адрес img. Чтобы получить доступ к любому из attributes элемента, мы можем использовать метод .get() после нахождения этого конкретного элемента. Beautiful Soup сохраняет attributes всех элементов в виде словаря Python во время преобразования типа байтов в тип BeautifulSoup. Вот почему мы используем метод .get() для доступа к значениям в словаре.

Вот и все, мы успешно собрали все необходимые данные.

Теперь давайте экспортируем эти данные в файл CSV.

Создание файла CSV

Для создания файлов CSV с использованием Python нам понадобится модуль с именем csv. Это встроенный модуль, поэтому устанавливать его не нужно. Вам просто нужно импортировать его в самый верх scraper.py файла.

Так что введите это в самом верху:

import csv

Теперь в самом низу файла введите следующий код:

""" writing data to CSV """
# open top25.csv file in "write" mode
with open('top25.csv', 'w') as file:
  # create a "writer" object
  writer = csv.writer(file, delimiter=',')
  # use "writer" obj to write 
  # you should give a "list"
  writer.writerow(["title", "genre", "ratings", "length", "year", "budget", "gross", "img_url"])
  for i in range(25):
    writer.writerow([
      titles[i], 
      genres[i], 
      ratings[i], 
      lengths[i], 
      years[i], 
      budgets[i], 
      grosses[i], 
      img_urls[i]
    ])
  • Сначала мы открываем файл в режиме ‘w’. ‘w’ для режима записи. Если файла с таким именем не существует, он создаст его. И если такой файл существует, он перезапишет этот файл. Здесь мы открываем / создаем новый файл с именем top25.csv.
  • Затем мы создаем объект csv.writer(), задавая file и запятую ‘,’ в качестве символа delimiter.
  • Затем, используя этот writer объект, мы write.row(). Первая строка, которую мы написали, предназначена для заголовков, вы можете думать о них как о заголовках таблиц.
  • Затем, наконец, мы выполняем цикл 25 раз, и на каждой итерации мы записываем одну строку, которая будет отдельным фильмом. Каждая строка посвящена одному фильму.

Вот и все, давайте попробуем запустить наш скрипт. Я надеюсь, что ваш терминал (в редакторе кода) уже открыт, а ваш venv активен. Теперь введите это:

python scraper.py

Если все прошло гладко, то у вас должен быть создан новый файл, а именно top25.csv в том же каталоге, и он будет содержать такие данные:

Если у вас есть какие-либо ошибки, убедитесь, что код, который вы ввели до этого момента в файле scraper.py, точно такой же, как последний код ниже ...

Окончательный код

import requests
from bs4 import BeautifulSoup
import csv
# fetch the web page
page = requests.get('https://the-coding-pie.github.io/top_movies/')
# turn page into a BeautifulSoup object
soup = BeautifulSoup(page.content, 'lxml')

""" first, scraping using find_all() method """
# scrape all the titles
titles = [] 
for h3 in soup.find_all('h3'):
  titles.append(h3.string.strip())
# genres
genres = []
for genre in soup.find_all('p', class_='genre'):
  genres.append(genre.string.strip())

""" scraping using css_selector eg: select('span.class_name') """
# ratings, selecting all span with class="rating"
ratings = []
for rating in soup.select('span.rating'):
  ratings.append(rating.string.strip())
# lengths, selecting all span with class="length"
lengths = []
for length in soup.select('span.length'):
  lengths.append(length.string.strip())
# years, selecting all span with class="year"
years = []
for year in soup.select('span.year'):
  years.append(year.string.strip())

""" scraping by navigating through elements eg: div.span.string """
# budget
budgets = []
for budget in soup.find_all('div', class_='budget'):
  # from <div class="budget"></div>, get the span.string
  budgets.append(budget.span.string.strip())
# gross
grosses = []
for gross in soup.find_all('div', class_='gross'):
  grosses.append(gross.span.string.strip())

""" parsing all the "src" attribute's value of <img /> tag """
img_urls = []
for img in soup.find_all('img', class_='poster'):
  img_urls.append(img.get('src').strip())

""" writing data to CSV """
# open top25.csv file in "write" mode
with open('top25.csv', 'w') as file:
  # create a "writer" object
  writer = csv.writer(file, delimiter=',')
  # use "writer" obj to write 
  # you should give a "list"
  writer.writerow(["title", "genre", "ratings", "length", "year", "budget", "gross", "img_url"])
  for i in range(25):
    writer.writerow([
      titles[i], 
      genres[i], 
      ratings[i], 
      lengths[i], 
      years[i], 
      budgets[i], 
      grosses[i], 
      img_urls[i]
    ])

Заключение

Надеюсь, вам понравился этот урок. В некоторых местах я намеренно пропустил пояснительную часть. Потому что эти коды были простыми и не требующими пояснений. Вот почему я предоставил вам самому расшифровать его.

Истинное обучение происходит, когда вы пробуете что-то самостоятельно. Если вы просто следуете руководству, вы не станете лучшим программистом. Вы должны использовать свой собственный мозг.

Если у вас все еще есть ошибка, сначала попробуйте расшифровать ее самостоятельно, выполнив поиск в Google.

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

И все, спасибо;)