Время прочтения: 4 мин.

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

Зачастую для специалиста не составляет труда провести анализ того или иного массива информации, открыв его в табличном редакторе либо сформировав прямой запрос в БД по условиям выборки. Но на практике аналитики данных нередко встречают задачи по структурированию нетабличных текстовых данных, в т.ч. имеющих их табличное отображение, основанное на коде разметки веб-страниц, но не являющимися таблицами в полном смысле этого слова (html, htm и т.п.).  Именно с таким видом представления некоторых ежедневных форм нетабличных отчетных данных мы столкнулись в своей работе. О том, как мы решили подобную задачу, параллельно знакомясь с технологией парсинга, расскажем в этой статье.

Для начала предлагаем разобраться в объекте исследования — веб странице и ее коде разметки.

HTML (от англ. HyperText Markup Language — «язык гипертекстовой разметки») — стандартизированный язык разметки документов во Всемирной паутине. Большинство веб-страниц содержат описание разметки на языке HTML (или XHTML). Язык HTML интерпретируется браузерами. Полученный в результате интерпретации форматированный текст отображается на экране монитора компьютера или мобильного устройства. (из Википедии)

Рассмотрим пример таблицы в формате html:

Номер операцииДатаФИОСуммаКонтрагентКомментарии
111.02.2019Иванов И.А.1233,15ООО Ромашказа лепесток
211.02.2019Степанов А.О.15000,00 ЗАО Котелокза дрова
311.02.2019Лебедева А.Ш.7500,50 ООО Ромашка
411.03.2019Вавилов Н.З.13,44ПАО Связькопытавозврат

Код гипертекстовой разметки данной таблицы будет выглядеть так:

<!DOCTYPE html>
<html>
<head>
<title>Отчет по платежам</title>
</head>
<body>
<p   style="text-align: center;">Отчет по платежам</p>
<table>
  <tbody>
    <tr>
        <td>Номер операции</td>
        <td>Дата</td>
        <td>ФИО</td>
        <td>Сумма</td>
        <td>Контрагент</td>
        <td>Комментарии</td>
    </tr>
    <tr>
        <td>1</td>
        <td>11.02.2019</td>
        <td>Иванов И.А.</td>
        <td>1233,15</td>
        <td>ООО Ромашка</td>
        <td>за лепесток</td>
    </tr>
    <tr>
        <td>2</td>
        <td>11.02.2019</td>
        <td>Степанов А.О.</td>
        <td>15000,00</td>
        <td>ЗАО Котелок</td>
        <td>за дрова</td>
    </tr>
    <tr>
        <td>3</td>
        <td>13.02.2019</td>
        <td>Лебедева А.Ш.</td>
        <td>7500,50</td>
        <td>ООО Ромашка</td>
        <td>&nbsp;</td>
    </tr>
    <tr>
        <td>4</td>
        <td>17.03.2019</td>
        <td>Вавилов Н.З.</td>
        <td>13,44</td>
        <td>ПАО Связьтранскопыта</td>
        <td>возврат</td>
    </tr>
  </tbody>
</table>
</body>
</html>

Из кода видно, что за структуру таблицы в HTML отвечают так называемые тэги:

  1. <table></table> — ограничивающие собой саму таблицу;
  2. <tr></tr> — ограничивающие собой строки таблицы;
  3. <td></td> — ограничивающие собой ячейки строк.

Теперь перед нами возникает задача: как привести этот закодированный текст в удобный нам табличный вид? И как это сделать если таких файлов формируется десятки (сотни) в день, а анализировать нам надо месяцы (или даже годы)? Здесь нам на помощь приходит Python и его модуль парсера “BS4 (beautifulsoup4)”.

Парсер (англ. parser; от parse — анализ, разбор), или синтаксический анализатор, — часть программы, преобразующей входные данные (как правило, текст) в структурированный формат.

Для успешного решения этой задачи нам потребуется передать для анализа в модуль BS4 Python таблицу ограниченную тегами <table>, предварительно внедрив в код сам модуль:

from bs4 import BeautifulSoup
import csv                                # модуль для итогового экспорта данных в csv формат

Сначала следует часть типового решения Python по работе с файлом: интересующий нас файл открывается Python, “зачитывается” полностью в переменную и передается модулю bs4.

filename = # путь до файла
infile = io.open(filename, ‘r’)      # открывается файл в режиме чтения
data = infile.read()                       # “чтение” данных из файла в переменную
infile.close()                                 # закрытие файла
soup = BeautifulSoup(‘’.join(data), ‘lxml’)  # преобразование данных файла в формат BS4 с учетом предустановок ‘lxml’, ‘html’ и т.п. и передача их в переменную soup

Следующим этапом следует определение самой таблицы в общей массе кода по тегу <Table> и сведение всех ячеек в один список.

rows = [ ]
table_data = soup.find(‘table’)     # находим массив данных ограниченных <table>
for td in tabledata.find_all(‘td’):
    rows.append(td.text.strip())  # формируем список из значений каждой пары <td></td>, т.е. всех ячеек

Сформированный общий список значений всех ячеек нам надо вернуть по строкам

rows_upd = {} # создаем словарь, ключом которого будет номер строки, значение - список из значений ячеек первой строки 
i = 0
while len(rows) > 0:
    i += 1
    rows_upd[i] = rows[:6] # формируем строки в словаре
    del rows[:6] # удаляем переданные в словарь строки из общей массы

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

path = # путь к файлу экспорта формата csv
with open(path, ‘a’, newline=’’) as csvfile: # открытие для записи файла csv
    writer = csv.writer(csvfile, delimiter=’;’) # определение формата файла, разделителя столбцов 
    for key in rows_upd.keys():
        writer.writerow(rows_upd[key]) # последовательная запись каждой строки словаря согласно ранее сформированного ключа

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

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