NewTechAudit
809

Web Parsing. Основы на Python

Рассмотрим еще один практический кейс парсинга сайтов с помощью библиотеки BeautifulSoup: что делать, если на сайте нет готовой выгрузки с данными и нет API для удобной работы, а страниц для ручного копирования очень много?

В закладки

Недавно мне понадобилось получить данные с одного сайта. Готовой выгрузки с информацией на сайте нет. Данные я вижу, вот они передо мной, но не могу их выгрузить и обработать. Возник вопрос: как их получить? Немного «погуглив», я понял, что придется засучить рукава и самостоятельно парсить страницу (HTML). Какой тогда инструмент выбрать? На каком языке писать, чтобы с ним не возникло проблем? Языков программирования для данной задачи большой набор, выбор пал на Python за его большое разнообразие готовых библиотек.

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

Задачу можно разбить на три этапа:

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

Инструменты

Для начала нам необходимо отправлять HTTP-запросы на выбранный сайт. У Python для отправки запросов библиотек большое количество, но самые распространённые urllib/urllib2 и Requests. На мой взгляд, Requests — удобнее, примеры буду показывать на ней.

А теперь сам процесс. Были мысли пойти по тяжелому пути и анализировать страницу на предмет объектов, содержащих нужную информацию, проводить ручной поиск и разбирать каждый объект по частям для получения необходимого результата. Но немного походив по просторам интернета, я получил ответ: BeautifulSoup – одна из наиболее популярных библиотек для парсинга. Библиотеки найдены, приступаем к самому интересному: к получению данных.

Загрузка и обработка данных

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

import requests from bs4 import BeautifulSoup bank_id = 1771062 #ID банка на сайте banki.ru url = 'https://www.banki.ru/services/questions-answers/?id=%d&p=1' % (bank_id) # url страницы r = requests.get(url) with open('test.html', 'w') as output_file: output_file.write(r.text)

После исполнения данного скрипта получится файл text.html.

Открываем данный файл и видим, что необходимые данные получили без проблем.

Теперь очередь для разбора страницы на нужные фрагменты. Обрабатывать будем последние 10 страниц плюс добавим модуль Pandas для сохранения результата в Excel.

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

import requests from bs4 import BeautifulSoup import pandas as pd bank_id = 1771062 #ID банка на сайте banki.ru page=1 max_page=10 url = 'https://www.banki.ru/services/questions-answers/?id=%d&p=%d' % (bank_id, page) # url страницы

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

Подробно покопавшись во внутренностях страницы, мы увидим, что необходимые данные «вопросы-ответы» находятся в блоках <table class:qaBlock >, соответственно, сколько этих блоков на странице, столько и вопросов.

result = pd.DataFrame() r = requests.get(url) #отправляем HTTP запрос и получаем результат soup = BeautifulSoup(r.text) #Отправляем полученную страницу в библиотеку для парсинга tables=soup.find_all('table', {'class': 'qaBlock'}) #Получаем все таблицы с вопросами for item in tables: res=parse_table(item)

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

  • Find(‘table’) – проводит поиск по странице и возвращает первый найденный объект типа ‘table’. Вы можете искать и ссылки find(‘table’) и рисунки find(‘img’). В общем, все элементы, которые изображены на странице;
  • find_all(‘table’) – проводит поиск по странице и возвращает все найденные объекты в виде списка

У каждой из этих функций есть методы. Я расскажу от тех, которые использовал:

  • find(‘table’).text – этот метод вернет текст, находящийся в объекте;
  • find(‘a’).get(‘href’) – этот метод вернет значение ссылки

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

def parse_table(table):#Функция разбора таблицы с вопросом res = pd.DataFrame() id_question=0 link_question='' date_question='' question='' who_asked='' who_asked_id='' who_asked_link='' who_asked_city='' answer='' question_tr=table.find('tr',{'class': 'question'}) #Получаем сам вопрос question=question_tr.find_all('td')[1].find('div').text.replace('<br />','\n').strip() widget_info=question_tr.find_all('div', {'class':'widget__info'}) #Получаем ссылку на сам вопрос link_question='https://www.banki.ru'+widget_info[0].find('a').get('href').strip() #Получаем уникальным номер вопроса id_question=link_question.split('=')[1] #Получаем того кто задал вопрос who_asked=widget_info[1].find('a').text.strip() #Получаем ссылку на профиль who_asked_link='https://www.banki.ru'+widget_info[1].find('a').get('href').strip() #Получаем уникальный номер профиля who_asked_id=widget_info[1].find('a').get('href').strip().split('=')[1] #Получаем из какого города вопрос who_asked_city=widget_info[1].text.split('(')[1].split(')')[0].strip() #Получаем дату вопроса date_question=widget_info[1].text.split('(')[1].split(')')[1].strip() #Получаем ответ если он есть сохраняем answer_tr=table.find('tr',{'class': 'answer'}) if(answer_tr!=None): answer=answer_tr.find_all('td')[1].find('div').text.replace('<br />','\n').strip() #Пишем в таблицу и возвращаем res=res.append(pd.DataFrame([[id_question,link_question,question,date_question,who_asked,who_asked_id,who_asked_link,who_asked_city,answer]], columns = ['id_question','link_question','question','date_question','who_asked','who_asked_id','who_asked_city','who_asked_link','answer']), ignore_index=True) #print(res) return(res)

Функция возвращает DataFrame, который можно накапливать и экспортировать в EXCEL.

result = pd.DataFrame() r = requests.get(url) #отправляем HTTP запрос и получаем результат soup = BeautifulSoup(r.text) #Отправляем полученную страницу в библиотеку для парсинга tables=soup.find_all('table', {'class': 'qaBlock'}) #Получаем все таблицы с вопросами for item in tables: res=parse_table(item) result=result.append(res, ignore_index=True) result.to_excel('result.xlsx')

Резюме

В результате мы научились парсить web-сайты, познакомились с библиотеками Requests, BeautifulSoup, а также получили пригодные для дальнейшего анализа данные об отзывах с сайта banki.ru. А вот и сама результирующая таблица.

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

Я надеюсь, моя статья была полезна. Спасибо за внимание.

{ "author_name": "NewTechAudit", "author_type": "editor", "tags": ["\u0444\u0443\u043d\u043a\u0446\u0438\u044f","\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c","\u043f\u0438\u0448\u0435\u043c","\u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c","print","id"], "comments": 2, "likes": 3, "favorites": 6, "is_advertisement": false, "subsite_label": "newtechaudit", "id": 109368, "is_wide": false, "is_ugc": false, "date": "Thu, 27 Feb 2020 18:13:19 +0300", "is_special": false }
Создать объявление на vc.ru
Трибуна
Онлайн-бар DrinkTalk.me
Всем привет, меня зовут Дмитрий, я из Минска, и мы делаем онлайн-бар.
0
2 комментария
Популярные
По порядку
4

Парсер можно писать на чем угодно. Да и библиотеки не всегда нужны, использовал раньше только ssl для https. В некоторых случаях проще писать свой код. Единственное, что хорошо бы иметь - параллельные потоки и прокси. Писал давно универсальный парсер, столкнулся с неприятным моментом ошибок в коде доноров, отсутствии закрывающих тегов или генерации страниц скриптами. Решил гибкой системой настроек паттернов, в приложении добавил редактор данных в таблице, куда заносятся паттерны для поочередной обработки данных. Для увеличения скорости добавил стоп-паттерн (та уникальная часть кода страницы, после которой нет нужных данных), после которого не нужно ждать окончания загрузки, а можно сразу работать с кодом страницы. И для ускорения именно разбора добавил старт-позицию до которой не нужно анализировать данные. Ну ещё счетчик страниц, автосохранение данных и настроек и т.п.

Ответить
0

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

Ответить

Прямой эфир