Сегодня поговорим о веб скрапинге. Допустим, стоит задача выгрузить информацию с сайта или нескольких и представить ее в таблице. Для примера я возьму techcrunch и mashable. На выходе хочу получить таблицу с колонками [текст], [краткое описание], [ссылка], чтобы не тратить время на бесконечное зависание на сайтах, ну вы знаете как это бывает=)

Библиотеки

Начнем с импорта. Для работы с http лучший выбор это requests, а для парсинга html будем классически работать с Beautiful Soup. Ну и pandas для удобной работы с данными и выгрузки в csv.

1
2
3
4
import requests
from bs4 import BeautifulSoup
from numpy import nan as NaN
import pandas as pd

Нахождение нужных нам элементов

Напишем скрипт, выполняющий get-запрос, записав его в переменную, затем преобразуем полученный текст страницы в объект BeautifulSoup.
В качестве аргумента get-запроса передадим ссылку на раздел startups, там все веселье.

1
2
r1 = requests.get('https://techcrunch.com/startups/')
soup = BeautifulSoup(r1.text, 'html.parser')

Здесь мы получим на выходе кусок html, который выглядит примерно так(для примера приведен только отрывок).

1
2
3
4
5
6
7
<ul class="subnav-channel" data-omni-sm-delegate="gbl_mainnav"><li class="menu-item menu-item-type-taxonomy menu-item-object-category current-menu-item menu-item-899745" id="menu-item-899745"><a href="https://techcrunch.com/startups/">Startups</a></li>
<li class="menu-item menu-item-type-taxonomy menu-item-object-category current-post-ancestor current-menu-parent current-post-parent menu-item-899746" id="menu-item-899746"><a href="https://techcrunch.com/mobile/">Mobile</a></li>
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-899747" id="menu-item-899747"><a href="https://techcrunch.com/gadgets/">Gadgets</a></li>
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-899748" id="menu-item-899748"><a href="https://techcrunch.com/enterprise/">Enterprise</a></li>
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-899749" id="menu-item-899749"><a href="https://techcrunch.com/social/">Social</a></li>
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-899750" id="menu-item-899750"><a href="https://techcrunch.com/europe/">Europe</a></li>
<li class="menu-item menu-item-type-custom menu-item-object-custom menu-item-901944" id="menu-item-901944"><a href="/asia">Asia</a></li>

Открываем нашу ссылку в браузере через F12 и находим нужные нам элементы - тег с названием(будем искать по title), описанием(description) и url.

В случае с techcrunch все нужные нам ссылки находятся в “диве” block-content, в котором находится каждый пост.

block-content

Для подобного сценария в BS есть метод find_all(), который находит все возможные вхождения тега с заданными параметрами.
Затем мы создаем простой for луп и пустой список, куда записываем нужные нам данные, которые также ищем по тегам внутри block-content.

1
2
3
4
5
6
7
resultstech = soup.find_all('div', 'block-content',)
recordstech = []
for result in resultstech:
title = result.find('h2').text.strip()
desc = result.find('p', 'excerpt').text[:-10].strip()
link = result.find('a')['href']
recordstech.append((title, desc, link))

Для удобства обрезаем пробелы с помощью метода strip(), и последние десять символов в desc, обратившись к ним по индексу[:-10], т.к. они представляют собой фразу “Read More” для каждого поста.

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

Проделаем тоже самое с mashable

1
2
3
4
5
6
7
8
9
r2 = requests.get('http://mashable.com/tech/')
soup = BeautifulSoup(r2.text, 'html.parser')
resultsmash = soup.find_all('article')
recordsmash = []
for result in resultsmash:
title = result.find('h1').text
link = result.find('a')['href']
recordsmash.append((title, link))

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

Обработка с помощью pandas

Создадим два датафрейма pandas для того, чтобы красиво соединить наши данные. Для визуального разделения данных сделаем пустой ряд - empty row с помощью вставки not a number - “NaN” из numpy, после каждого набора данных.

1
2
3
4
5
6
7
dftech = pd.DataFrame(recordstech, columns=['title', 'desc', 'link'])
empty_row3 = pd.Series([NaN, NaN, NaN], index=['title', 'desc', 'link'])
dftech_empty_row = dftech.append(empty_row3, ignore_index=True)
dfmash = pd.DataFrame(recordsmash, columns=['title', 'link'])
empty_row2 = pd.Series([NaN, NaN], index=['title', 'link'])
dfmash_empty_row = dfmash.append(empty_row2, ignore_index=True)

После этого соединим наши датафреймы с помощью метода concat()

1
2
frames = [dftech_empty_row, dfmash_empty_row]
results = pd.concat(frames)

Открываем таблицу

Запишем результаты в csv

1
results.to_csv('parsing.csv'), index=False, encoding='utf-8')
  • откроем csv файл с помощью текстового редактора и зададим разделение sep=, в самом начале если нужно открыть в экселе
  • либо откроем в google sheets

Картинка ниже как именно выглядит результат

result

Готово. Можете применить подобную операцию ко всем сайтам в интересной вам индустрии, и на выходе получить сводку всех новостей в одной таблице, определив что вам полезно, а что нет. И не тратить время на бесконечный скроллинг веба=)

Предварительно ознакомьтесь с правилами сайта, для этого прибавьте к корневому url адресу /robots.txt и узнайте, можно ли его “скрейпить”.

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

Ссылки