Учебник по веб-скрейпингу Python – как очистить данные с любого веб-сайта с помощью Python

uchebnik po veb skrejpingu python – kak ochistit dannye s lyubogo

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

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

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

Но сначала я должен предупредить вас о законности веб-скрейпинга. Хотя скрейпинг является законным, данные, которые вы можете получить, могут быть незаконными для использования. Убедитесь, что вы не возитесь с любым:

  • Содержимое, защищенное авторским правом – поскольку это чья-то интеллектуальная собственность, оно защищено законом, и вы не можете просто так его использовать.
  • Персональные данные – если собранная вами информация может быть использована для идентификации личности, она считается личными данными, а для граждан ЕС она защищена в соответствии с GDPR. Если у вас нет законной причины для хранения данных, лучше просто пропустить их совсем.

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

Что вам пригодится для вашего скребка?

Чтобы начать создание собственного веб-скребка, вам нужно сначала установить Python на вашей машине. Ubuntu 20.04 и другие версии Linux поставляются с предустановленным Python 3.

Чтобы проверить, установлен ли Python на устройстве, выполните следующую команду:

python3 -v

Если у вас установлен Python, вы должны получить следующий вывод:

Python 3.8.2

Кроме того, для нашего веб-скребка мы будем использовать пакеты Python BeautifulSoup (для выбора конкретных данных) и Selenium (для воспроизведения динамически загружаемого содержимого). Чтобы их установить, просто запустите эти команды:

pip3 install beautifulsoup4

и

pip3 install selenium

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

Как проверить страницу

Теперь, когда у вас все установлено, пора серьезно начать наш проект со скребковки.

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

Для этой статьи я решил собрать информацию о первых десяти фильмах из списка 250 лучших фильмов на IMDb:

Сначала мы получим названия, а затем погрузимся дальше, извлекая информацию со страницы каждого фильма. Некоторые данные требуют воспроизведения JavaScript.

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

e6DE3zczzQa-VSBIynK-fR4oyAjVbpx2PztpEDKbi3K0NII9_lFkFhGQmiOjc_-Y_Kg26cM3pecnSKNiPlLZGpntqVKUrcX9E4gDWaTsolWoCFz

Нажав CTRL+F и проведя поиск в структуре HTML-кода, вы увидите, что существует только один <таблиця> тег на странице. Это полезно, поскольку дает нам информацию о том, как мы можем получить доступ к данным.

HTML-селектор, который даст нам все заголовки со страницы table tbody tr td.titleColumn a. Это потому, что все заголовки находятся в привязке внутри ячейки таблицы с классом «titleColumn».

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

document.querySelectorAll("table tbody tr td.titleColumn a")[0].innerText

Вы увидите нечто подобное:

T1pgLUXJHX_s3gubDKvBjwkWeK1neZxiysoneD2Q1NU3Sj_pD8defdKorTlcsiiqShlmPDEeCu3Goo5T9CgzPKCml9dq_kCCu7KUyTx7uSrU8VN9QzJB

Теперь, когда у нас этот селектор, мы можем начать писать наш код на Python и извлекать нужную информацию.

Названия фильмов из нашего списка являются статическим содержимым. Это потому, что если вы посмотрите на источник страницы (CTRL+U на странице или щелкните правой кнопкой мыши, затем выберите Просмотр источника страницы), вы увидите, что заголовки уже там.

Статическое содержимое, как правило, легче очистить, поскольку оно не требует визуализации JavaScript. Чтобы извлечь первые десять заголовков в списке, мы будем использовать BeautifulSoup, чтобы получить содержимое, а затем распечатать его в выводе нашего скрепера.

import requests
from bs4 import BeautifulSoup
 
page = requests.get(' # Getting page HTML through request
soup = BeautifulSoup(page.content, 'html.parser') # Parsing content using beautifulsoup
 
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text) # Display the innerText of each anchor

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

Выход должен выглядеть так:

RrmEldjCrbz7V1-o4r6UsKNuWkj_yD2cWwfyuMMbdnRn7lk9cI0yhMi85PK4NrvX7L2KY0pY8047f9CmAeXo1W51HvFENMPxxh36ACqu3mhIBU_FENMPxxh36ACqu3mhNKUBNFNMHNKUBNFXH36ACqu3mhNKUBNFXH36ACqu3mhNKUBNFXH36ACqu3mhNKU2001

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

Однако это усугубляет ситуацию, поскольку HTML, полученный из простого запроса, не будет содержать динамического содержимого. К счастью, с помощью Selenium мы можем имитировать запрос в браузере и ждать, пока появится динамическое содержимое.

Как использовать Selenium для запросов

Вам нужно будет знать расположение вашего chromedriver. Следующий код идентичен коду, представленному на втором шаге, но на этот раз мы используем Selenium, чтобы сделать запрос. Мы все равно будем анализировать содержимое страницы с помощью BeautifulSoup по-прежнему.

from bs4 import BeautifulSoup
from selenium import webdriver
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
driver.get(' # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup. Notice driver.page_source instead of page.content
 
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text) # Display the innerText of each anchor

Не забудьте заменить «ВАШ ПУТЬ-К-CHROMEDRIVER» местом, где вы распаковали chromedriver. Кроме того, вы должны заметить, что вместо page.contentкогда мы создаем объект BeautifulSoup, мы сейчас используем driver.page_sourceкоторый предоставляет HTML-содержимое страницы.

Используя приведенный выше код, теперь мы можем получить доступ к каждой странице фильма, вызвав метод click на каждом из якорей.

first_link = driver.find_elements_by_css_selector('table tbody tr td.titleColumn a')[0]
first_link.click()

Это имитирует щелчок ссылки на первый фильм. Однако в этом случае я рекомендую вам продолжить использование driver.get instead. Это потому, что вы больше не сможете использовать click() метод после того, как вы перейдете на другую страницу, поскольку новая страница не содержит ссылок на девять других фильмов.

В результате, после нажатия первого заголовка из списка вам нужно будет вернуться на первую страницу, затем нажать вторую и т.д. Это пустая трата производительности и времени. Вместо этого мы просто будем использовать вытянутые ссылки и получать к ним друг за другом.

Для «The Shawshank Redemption» страница фильма будет выглядеть. Мы вытащим год и продолжительность фильма со страницы, но на этот раз мы будем использовать функции Selenium вместо BeautifulSoup в качестве примера. На практике вы можете использовать любое из них, поэтому выберите свой любимый.

Чтобы получить год и продолжительность фильма, повторите первый шаг на странице фильма.

Вы заметите, что вы можете найти всю информацию в первом элементе класса ipc-inline-list (селектор «.ipc-inline-list») и все элементы списка содержат атрибут role со значением presentation ( [role=’presentation’] селектор).

from bs4 import BeautifulSoup
from selenium import webdriver
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
page = driver.get(' # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup
 
totalScrapedInfo = [] # In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    driver.get(' + anchor['href']) # Access the movie’s page
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0] # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role="presentation"]") # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    } # Save all the scraped information in a dictionary
    totalScrapedInfo.append(scrapedInfo) # Append the dictionary to the totalScrapedInformation list
    
print(totalScrapedInfo) # Display the list with all the information we scraped

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

Если вы посмотрите на страницу с помощью inspect, вы увидите, что вы можете найти раздел как элемент с атрибутом data-testid установить в качестве firstListCardGroup-editorial. Но если вы заглянете в источник страницы, то нигде не найдете значения этого атрибута. Это потому, что раздел редакционных списков загружается IMDB динамически.

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

Для этого мы импортируем еще несколько пакетов, позволяющих дождаться загрузки нашего динамического содержимого.

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
 
option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux. 
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')
# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
 
page = driver.get(' # Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser') # Parsing content using beautifulsoup
 
totalScrapedInfo = [] # In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a") # Selecting all of the anchors with titles
first10 = links[:10] # Keep only the first 10 anchors
for anchor in first10:
    driver.get(' + anchor['href']) # Access the movie’s page 
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0] # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role="presentation"]") # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    } # Save all the scraped information in a dictionary
    WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='firstListCardGroup-editorial']")))  # We are waiting for 5 seconds for our element with the attribute data-testid set as `firstListCardGroup-editorial`
    listElements = driver.find_elements_by_css_selector("[data-testid='firstListCardGroup-editorial'] .listName") # Extracting the editorial lists elements
    listNames = [] # Creating an empty list and then appending only the elements texts
    for el in listElements:
        listNames.append(el.text)
    scrapedInfo['editorial-list'] = listNames # Adding the editorial list names to our scrapedInfo dictionary
    totalScrapedInfo.append(scrapedInfo) # Append the dictionary to the totalScrapedInformation list
    
print(totalScrapedInfo) # Display the list with all the information we scraped

Для предыдущего примера вы должны получить следующий результат:

geHhbKeeP2ATtz-OnIx9MATB3UvXcrobnO4eUNOLrzQll9ebPlq_2PqKaT_oT6e-3h7NmRkRh_9mrDuSvuW3Wbs3sRi1iuM3paCa8HBpTqWrZUS

Как сохранить загруженное содержимое

Теперь, когда у нас есть все нужные данные, мы можем сохранить их как файл .json или .csv для удобства чтения.

Для этого мы просто используем пакеты JSON и CVS от Python и запишем наше содержимое в новые файлы:

import csv
import json
 
...
        
file = open('movies.json', mode="w", encoding='utf-8')
file.write(json.dumps(totalScrapedInfo))
 
writer = csv.writer(open("movies.csv", 'w'))
for movie in totalScrapedInfo:
    writer.writerow(movie.values())

Советы и подсказки из скребка

Хотя наше руководство уже достаточно развито, чтобы позаботиться о сценариях визуализации JavaScript, у Selenium еще есть много вещей для изучения.

В этом разделе я поделюсь некоторыми советами и рекомендациями, которые могут пригодиться.

1. Время ваших запросов

Если вы за короткое время рассылаете сервер с сотнями запросов, очень вероятно, что в какой-то момент появится код captcha или ваш IP может быть даже заблокирован. К сожалению, у Python нет обходного пути, чтобы избежать этого.

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

import time
import requests
 
page = requests.get(' # Getting page HTML through request
time.sleep(30) # Wait 30 seconds
page = requests.get(') # Getting page HTML through request

2. Обработка ошибок

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

try:
    WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "your selector")))
    break
except TimeoutException:
    # If the loading took too long, print message and try again
    print("Loading took too much time!")

Синтаксис проб и ошибок может быть полезен, когда вы ожидаете элемент, вытаскиваете его или даже когда вы просто делаете запрос.

3. Сделайте скриншоты

Если вам нужно в любой момент получить снимок экрана веб-страницы, которую вы скребете, вы можете использовать:

driver.save_screenshot(‘screenshot-file-name.png’)

Это может помочь установить, когда вы работаете с динамически загруженным содержимым.

4. Прочтите документацию

И последнее, но не менее важно, не забудьте прочесть документацию от Selenium. Эта библиотека содержит информацию о том, как выполнять большинство действий, которые можно выполнять в обозревателе.

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

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

Последние мнения

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

Иногда веб-скрейлинг может оказаться очень сложным, поскольку веб-сайты начинают создавать все больше препятствий на пути разработчика. Некоторые из этих препятствий могут являться кодами Captcha, блоками IP или динамическим содержимым. Преодолеть их только с помощью Python и Selenium может быть тяжело или даже невозможно.

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

Добавить комментарий

Ваш адрес email не будет опубликован.