Под источником данных в данной статье подразумевается статический объект (файл) или динамический (генерируемый скриптом), который содержит данные в формате xml(html) и доступный для чтения по протоколу http(s). Источниками, попадающими под наше определение являются:
- статические html станицы сайтов
- динамические страницы, генерирующие контент в формате xml(html)
- ленты rss новостей
- ...
Для извлечения данных из источников данных существует 2 техники
- двухпроходный парсинг. Его работа выполняется в два этапа. На первом из источника извлекаются и сохраняются в файл все ссылки, содержащие необходимые данные. На втором этапе парсер переходит по каждой ссылке, извлекает необходимые данные и сохраняет их в базу.
- циклический парсинг. Парсер через определенные промежутки времени обращается к источнику данных по одному адресу, извлекает данные и сохраняет их в базу. Особенность данного типа в том, что данные по одному адресу являются динамическими, изменяющимися с течением времени.
Отличия, особенности, алгоритмы
Двухпроходный парсинг
Главная его особенность состоит в типе ссылки на источник данных:
- каждый объект информации (в терминологии бд - запись таблицы) расположен по статическому уникальному адресу
- зачастую (не всегда) адрес на объект информации формируется по некоторому шаблону
Объект информации может представлять статическую Интернет страницу или динамический контент, содержимое которого генерируется в зависимости от параметров, указанных в ссылке на объект.
Исходя из сказанного, данная техника парсинга имеет следующий алгоритм
- Генерируется файл со списком ссылок на объекты информации. Здесь есть два пути.
1.1. Существует шаблон, определяющий правила построения ссылок на объект. В данном случае необходимо определить этот шаблон и создать генератор таких ссылок.
1.2. Шаблона не сущесвует. В этом случае создается скрипт, который, начиная с корневого источника данных собирает все ссылки на другие источники, содержащиеся по текущей ссылке, сохраняет их в файл, затем переходит по каждой из этих ссылок и повторяет дейсвие. Вот пример скрипта, соверщающего такой рекурсивный обход.
- Выполняется обращение к каждой собранной ссылке по которой извлекается объект информации
- Извлеченный объект сохраняется в БД
Циклический парсинг
Как было сказано выше, источник данных для такого вида парсинга имеет динамический контент и единственный адрес, по которому данный контент находится. Подавляющую долю источников данных такого типа составляют ленты новостей с форматом данных rss (формат описан в нотации xml). Rss формат имеет, как правило стандартный набор узлов, хранящих данные, поэтому написанный парсер подойдет для различных источников данных. Обший алгоритм парсинга такой.
- Парсер с заданной периодичностью обращается по указанному источнику, извлекает данные и сохраняет их во временный файл, в нашем случае это будет csv-файл.
- При следующем обращении парсера к источнику, перед тем, как очередная порция информации (запись бд) будет добавлена в csv-файл, может проверяться ее уникальность. Не уникальные записи игнорируются.
- Через определенный промежуток времени парсер сохраняет данные в бд и очищает csv-файл.
- Цикл повторяется.
Подробнее реализация циклического парсера описана в статье
Промежуточные задачи техник парсинга
Обе описанные техники парсинга имеют несколько общих задач, которые нехобходимо решать в процессе их реализации.
Задача исключения дубликатов объектов информации
Данная задача может быть решена и на уровне бд. Но с целью обобшения использования парсера и во избежание дополнительной нагрузки на бд проверка уникальности записи будет выполняться на уровне парсера.
Сущесвуют различные способы проверки уникальности записи (например, с использванием фильтра Блума). Но мы реализуем способ, основанный на применении хешей. Для этого установим следующие требования к функции проверки уникальности.
- Проверка уникальности может выполняться по одному или нескольким полям записи. Список полей определяется в настройках запуска парсера.
- Порядок проверки выполняется начиная от последних добавленных записей. Аргументом для данного требования является следующая эмпирическая аксиома - вероятность того, что текущая запись является дубликатом ранее сохраненной уменьшается с увеличением интервала времени между получением первой записи и ее текущего дубликата (в частности, это правило действует для rss лент новостей). В том случае, если наше утверждение верно, это позволит сократить время, затрачиваемое на проверку уникальности записи.
- Проверку уникальности можно открючить в настройках запуска парсера.
С учетом того, что хеши добавленных записей хранятся в текстовом файле, функция проверки уникальности может выглядеть примерно так
# функция обращается к файлу хешей и возвращает генератор на хеши, начиная с конца файла
def _getStringFromEndFile(filename, string_len, delimiter_line_len=1, emty_line_in_end=True):
if not os.path.exists(filename):
return
with codecs.open(filename, 'r', 'utf-8') as hk:
if emty_line_in_end:
hk.seek(0,2)
else:
hk.seek(1,2)
while hk.tell()>string_len:
hk.seek(-(string_len+delimiter_line_len),1)
yield hk.read(string_len)
hk.seek(-string_len,1)
yield None
# функция по хешу проверяет уникальность ОДНОЙ записи
def getDataFromSource(filename ,h):
hk=codecs.open(filename, 'a', 'utf-8')
for line in _getStringFromEndFile(filename, 56):
if line==h:
return 0
hk.write(h+'\n')
hk.close()
return 1
Подробнее о функции _getStringFromEndFile() можно прочитать в этой статье
При вызове _getStringFromEndFile() предполагается, что длина хена равна 56 (sha224). Если для расчета используется другой алгоритм хеша - подставить необходимую длину.
Запуск парсера в режиме демона
Восстановление структуры источника данных
В ходе работы парсер выполняет следующие действия
- подключается к указанной базе (единожды, при запуске парсера)
- извлекает данные из источника и сохраняет их в csv файл
- При следующем обращении парсер, прежде чем добавить извлеченные данные в csv файл, проверяет существование каждой записи в нем. Существующие данные повторно не добавляются
- При наступлении времени созранить извлеченные данные в базу, парсер выполняет эту операцию и затем очищает csv файл.
- цикл повторяется
В ходе работы парсер создает четыре влеменных файла
- файл логов - в нем созхраняется время обращения к источнику и количество новых записей, добавленных в csv файл
- сам csv файл
- файл ошибок - хранит время ошибки и ее описание
- файл критических ошибок. в него записываются ошибки прерывающие работу скрипта.
Настройки парсера для конкретного проета хранятся в отдельном файле params.py