Пример из реального мира парсинга веб-страниц с использованием Selenium и Beautiful Soup

5,39 млрд.

Вот сколько страниц во всемирной паутине.

Что касается данных?

Стоимость более 1200 петабайт.

Это эквивалентно 1 200 000 терабайт и 1 200 000 000 гигабайт.

Все, что я говорю, это то, что в сети очень много неструктурированных данных.

Просто сижу там ...

Жду, чтобы его почерпнули и проанализировали. 😉

В этом посте я рассмотрю отрывки кода о том, как очистить Интернет с помощью Selenium и Beautiful Soup.

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

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

Давайте начнем!

Знакомство с селеном и красивым супом

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

Селен

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

Эти тестовые сценарии могут быть созданы с использованием различных языков программирования, таких как Java, C #, PHP, Python, Ruby и Perl.

В этом посте мы будем использовать пакет Selenium Python (Selenium Web Driver) для создания тестовых скриптов.

В качестве быстрого знакомства с тем, как это работает, вот GIF-изображение, в котором я работаю с Selenium с помощью Jupyter Notebook.

В GIF я начал с импорта соответствующих пакетов и инициализации браузера Chrome. В то же время я сказал браузеру открыть «http://www.google.com».

Затем я вызвал команду для поиска «Selenium python API» в строке поиска Google перед выходом.

Если вы хотите попробовать его, запустите локальный блокнот Jupyter.

Первое, что вам нужно сделать, это установить селен.

pip install selenium

Также здесь необходимо установить хром-драйвер.

Затем пошагово запустите приведенный ниже код.

Обратите внимание на строку 13, имя класса - «gLFyf.gsfi»?

Как я узнал, что панель поиска Google использует этот тег?

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

Затем щелкните значок в верхнем левом углу. Это позволит вам выбирать элементы на странице.

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

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

elem = browser.find_element_by_class_name("gLFyf.gsfi")

Однако здесь следует сделать предостережение.

Из опыта работы с этим конкретным проектом по очистке я заметил ошибку в функции «find_element_by_class_name».

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

Я поискал в Google тонны решений. Даже если вы поместите эти точки между токенами, например «gLFyf ‹period› gsfi»

Это не всегда срабатывает…

Очень ненадежно в этом аспекте.

В частности, лучший способ отфильтровать теги классов - использовать Beautiful Soup.

Итак, если Селен причинил мне такую ​​боль ...

Почему селен?

Причина, по которой мне пришлось использовать Selenium для этого проекта, заключалась в ограничении Beautiful Soup.

Когда Beautiful Soup анализирует HTML-страницу, он не анализирует разделы страницы, которые необходимо загрузить с помощью java-скрипта.

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

Таким образом, если ваша веб-страница требует прокрутки вниз для загрузки сегментов страницы, синтаксический анализатор Beautiful Soup никогда не проанализирует полный HTML.

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

Таким образом, вы можете использовать Selenium или Beautiful Soup для запуска процесса извлечения информации.

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

Красивый суп

Beautiful Soup, созданная в 2004 году, представляет собой библиотеку Python, предназначенную для беспрепятственного анализа файлов HTML и XML.

Что я имею в виду?

Что ж, давайте рассмотрим модифицированную версию их примера из их документации.

Я изменил пример, чтобы сделать его немного понятнее.

Первое, что нужно сделать, это установить Beautiful Soup.

pip install beautifulsoup4

Вот коды:

Как видите, Beautiful Soup довольно прост и интуитивно понятен в использовании.

Я предлагаю вам попробовать их самостоятельно, если у вас есть время.

Ладно!

Теперь, когда знакомство закончилось, самое главное!

Поехали!

Настоящий проект веб-скрапинга

Если вы прошли вводные шаги выше, молодец!

Но здесь веб-парсинг может стать немного сложнее.

Весь проект парсинга заключался в том, чтобы вычистить заголовки постов от авторов TDS.

Я хотел проанализировать, о каких темах писали авторы TDS, и приблизительно оценить интерес читателей по количеству аплодисментов, полученных на каждый пост.

Я не буду останавливаться на результатах в этом посте, но они будут в другом посте.

Однако я расскажу о кодах, используемых для выполнения задачи очистки.

На это у меня ушло около 2 дней, так как я столкнулся с множеством разных ошибок.

Вот так!

Веб-парсинг с использованием Selenium и Beautiful Soup - двухэтапный процесс

Я сделал это в два этапа.

Шаг 1. Определите веб-страницы, которые необходимо очистить, и найдите общие теги, используемые на разных страницах.

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

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

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

Возьмем, к примеру, мой профиль.

Меня интересовали следующие сведения:

  • Имя издателя
  • Дата Отправки
  • Время Читать
  • Заголовок сообщения
  • Кол-во хлопков

Я искал теги «div», которые содержат информацию, которую я хочу извлечь.

Здесь я нашел тег «div», который мне был нужен, а также заметил, что в нем есть теги «class»!

Идеально!

Обратите внимание, что выше есть 7 идентичных тегов «class»?

Это потому, что каждое сообщение на странице моего профиля имеет одно и то же имя тега.

<div class = "gk gl gm gn go y c">...</div>

Это то, на что вам стоит обратить внимание.

Но вопрос в том, следуют ли другие профили пользователей одному и тому же тегу?

Давайте свяжем этот вывод с другим профилем.

Оказывается, не совсем так.

<div class = "gm gn go gp gq y c">...</div>

Но взглянув на другой профиль…

Есть закономерность.

<div class = "go gp gp gr gs y c">...</div>

Шаблон для всех тегов «div», которые мне нужны, следует шаблону регулярного выражения:

re.compile(r'(..\s){5}y c')

В английском языке будет 2 символа, за которым следует пробел, который встречается ровно 5 раз. Рядом с ним появятся символы «y c».

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

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

Веб-скрапинг - это своего рода хакерский прием и наполнен жестко запрограммированными тегами.

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

Переходим к следующему шагу.

Шаг 2. Определите логический поток, в котором будет следовать сценарий

Напомним, что цель - получить все заголовки сообщений TDS.

Из шага 1 мы знаем, что мы хотим извлечь и как это сделать.

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

Это означает загрузку профиля каждого автора и запуск процесса очистки.

Ключевое слово «каждый».

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

Это трехэтапный процесс:

  1. Сначала мы запускаем парсинг, чтобы извлечь все ссылки на профили писателей TDS.
  2. Затем используйте каждую ссылку, чтобы войти на страницу своего профиля, чтобы извлечь все заголовки сообщений, которые они написали ранее.
  3. В качестве подзадачи (2) для каждого сообщения «div» создайте новую запись для базового фрейма данных и добавьте к нему строку.

Вот как выглядит код, если применить все 3 шага.

Давайте разберемся, что вы видите во фрагменте кода выше.

Во-первых, я назвал определение «get_profile_urls (…)», которое принимает ссылку на профиль всех авторов TDS.

Как только определение очищает URL-ссылки для каждого писателя, оно возвращает их список на языке Python.

url_list =['https://medium.com/@kozyrkov','https://medium.com/@ssrosa','https://medium.com/@neha_mangal','https://medium.com/@parulnith', ...]

Затем каждая из этих ссылок проходит цикл и используется в качестве входных данных для другого определения, называемого «extract_information (…)».

По сути, это определение инициализирует браузер Selenium для каждого URL-адреса и использует Selenium для прокрутки до конца профиля, прежде чем запускать две подзадачи: «get_post (…)» и «get_writer_profile (…)».

Вот как выглядит определение extract_information (…):

Обратите внимание на строки с 21 по 37:

# Path to webdriver
    browser = webdriver.Chrome(webdriver_path)
# URL to scrape
    browser.get(pub_url)
    time.sleep(1)
# Get body
    elem = browser.find_element_by_tag_name("body")
# No. of times to scroll
    no_of_pagedowns = 100
while no_of_pagedowns:
        elem.send_keys(Keys.PAGE_DOWN)
        time.sleep(0.5)
        no_of_pagedowns-=1

Это Selenium, запускающий браузер для каждого профиля, определяющий «тело» страницы и прокручивающий страницу вниз каждые 0,5 секунды на протяжении 100 прокруток.

Да, 100 жестко запрограммированы. Я полагал, что вы дойдете до конца каждого профиля за 100 свитков.

Если нет, просто увеличьте прокрутку до 150, и это должно помочь.

Двигаясь дальше, обратите внимание, как я отслеживаю здесь количество ошибок.

Когда дело доходит до парсинга веб-страниц, может произойти много неожиданных вещей.

В этом случае я столкнулся с проблемами:

  • Посты без аплодисментов. Таким образом, я должен был это объяснить.
  • Сообщения без тегов заголовков H1.
  • Публикации Теги H4 не одинаковы для всех сообщений.
  • и более…

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

Если сообщение не соответствовало столбцам фрейма данных, это было бы ошибкой, и я зарегистрировал счетчик.

Всего я обработал 5524 заголовка сообщений с 435 ошибками.

Ой.

Но ничего страшного, я обхожусь с 5524. 😏

Давайте теперь рассмотрим, как выглядят «get_posts (…)» и «» get_writer_profiles (…) ».

Определение «get_posts (…)»

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

Кроме того, любые сообщения, не прошедшие процесс извлечения, игнорируются и сохраняется счетчик ошибок.

Вот как выглядит код:

Обратите внимание на строки 25 и 26:

ends_with_yc = re.compile(r'(..\s){5}y c')    
for row in page_content.find_all('div', class_=ends_with_yc):

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

Причина, по которой я использую Beautiful Soup, а не Selenium для извлечения, заключается в сопоставлении тега класса.

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

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

Следующее, на что я хотел бы обратить внимание, это строка 63:

dp_rt_tag = row.find_all('span')[3].find('div')

Иногда действительно полезно найти родительский тег для поиска в дочерних тегах.

В строке 63 родительский тег был «span», а дочерний тег - «div».

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

Вот пример иерархии, о которой я говорю.

На изображении выше родительским тегом может быть «section» с множеством дочерних тегов - «div», «figure», «h1», «h2» и т. Д.

Определение «get_writer_profiles (…)»

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

Это, по сути, то, что делает это определение. Довольно прямолинейно.

Я использовал селен для экстракции.

Обратите внимание на другой синтаксис.

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

match_tag = browser.find_element_by_tag_name("p")
OR
match_tag = browser.find_elements_by_tag_name("p")

Тонкая разница между словами «element» и «elements».

В Beautiful Soup эквивалентом будет:

match_tag = browser.find("p")
OR
match_tag = browser.find_all("p")

Чтобы найти класс в Selenium, вы должны написать:

match_tag = browser.find_element_by_class_name("go.gp.gp.gr.gs.y.c")

В Beautiful Soup:

match_tag = browser.find('div', class_="go gp gp gr gs y c")

Но найти теги «class» в Selenium не всегда удается.

Что странно, так это то, что это работает для некоторых тегов класса, но не работает для других. 😅

Это сработало в примере с Google во введении, не так ли?

Действительно, очень странно ...

Конечные результаты

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

Я отфильтровал TDS как издатель.

Для профиля писателя:

Подлый момент

Если вы уже читали, заметили, как я пропустил одно определение? 😉

Я ничего не упоминал об определении get_profile_url (…).

Я пропустил это определение, поэтому вы можете попробовать сами.

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

Попробуйте сами и посмотрите, как работает Selenium или Beautiful Soup!

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

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

Ссылка на этот пост на GitHub:



Конечные заметки

Ну вот и все!

В этом посте я познакомил вас с тем, как Selenium и Beautiful Soup работают с простыми упражнениями.

Затем я дал вам коды, чтобы попробовать выполнить парсинг на более реалистичном наборе данных. т.е. веб-страница TDS

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

Надеюсь, этот пост дал вам некоторое представление о парсинге веб-страниц.

Увидимся в следующем посте!

До свидания!

Профиль в LinkedIn: Тимоти Тан