Парсер для форума XenForo

Author Автор: Роман Чернышов    Опубликовано: 22 декабря 2020

Парсер XenForo XenForo — движок, набирающий все большую популярность, среди сайтов-форумов, вместе с этим растет и его сообщество, пишутся плагины, патчи и хаки, и ко мне все чаще обращаются клиенты с различными задачами по доработке данного движка. Среди многообразия задач и решений, выделается интерес к моей разработке парсера, который в том числе я адаптировал под работу с движком форума XenForo. Это значит, что парсер может получать данные с любых сайтов и сохранять их в базу данных XenForo, тем самым наполняя сайт: разделами, форумами, темами, сообщениями, пользователями(в том числе их публично-доступными данными включая аватар) и т.д. Далее я расскажу подробнее о логике работы парсера и о технической части касаемо XenForo.

Логика работы парсера

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

  • Первичная настройка парсера на сайт донора, с указанием где и какие данные ему собирать, обучить «пониманию» структуры HTML страниц донора (задача не сложная, например прописать путь в xPath до заголовка страницы H1);
  • Обход главной страницы форума, сбор ссылок на разделы и их названия;
  • Обход разделов, сбор в них ссылок на форумы и их названия;
  • Обход форумов, сбор в них ссылок на топики и их названия;
  • Обход топиков, сбор в них всех сообщений и ссылок на их авторов(на профили пользователей);
  • Обход профилей пользователей и сбор всех доступных данных по ним;
  • Отдельно происходит сбор ссылок на постраничную навигацию, для последующего сбора информации на других страницах списков форумов и топиков.

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

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

Одним из важных критериев, является возможность парсинга сайтов, которые доступны только после регистрации, имеют защиту от парсинга или защиту множественного открытия страниц в течении определенного промежутка времени. Для решения этой задачи, можно воспользоваться сторонними сервисами, помогающими обойти такую защиту, как например Google reCaptcha и т.д. Но, можно пойти и другим путем, перенести основную часть парсера на JavaScript и запустить парсер в почти автономном режиме в своем же браузере через UserScript(дополнение Violentmonkey для Chrome) в таком случае вы сможете воочию наблюдать за работой парсера, а также вмешиваться в неё если потребуется, помогать ему, в том числе для прохождения теста что вы не бот, например капчи от Google reCaptcha. Из недостатков это пожалуй то, что парсер будет работать на вашем ПК прямо в вашем браузере, впрочем и это решаемо, перенос парсера на «безголовый» браузер по типу PhantomJS и запуск его на сервере хостинга, уже полностью в автономной режиме работы без вашего участия(позаботившись о списках прокси и о сервисах обхода капчи, конечно же).

Сохраняем данные в базу XenForo

Пришедшие данные на каждом этапе, сохраняются в базу данных XenForo. Далее я расскажу в какие таблицы сохраняются те или иные типы данных, а также назначение их(таблиц) полей.

Сохраняем разделы

Таблица: xf_node

Запрос:

INSERT INTO `xf_node` (`node_id`, `title`, `description`, `node_name`, `node_type_id`, `parent_node_id`, `display_order`, `display_in_list`, `lft`, `rgt`, `depth`, `style_id`, `effective_style_id`, `breadcrumb_data`, `navigation_id`, `effective_navigation_id`, `xfa_nit_type`, `xfa_nit_params`) 
VALUES
(1,	'Название раздела',	'',	NULL,	UNHEX(''),	0,	300,	1,	51,	128,	0,	0,	0,	'a:0:{}',	UNHEX(''),	UNHEX(''),	0,	'');

В данном запросе стоит обратить внимания на значение полей:
node_id — ID раздела;
title — Название раздела;
parent_node_id — Если значение равно нулю значит данная запись является разделом, в ином случае форумом. Разделы и форумы хранятся в одной таблице.

Сохраняем форумы

Таблица: xf_forum

Запрос:

INSERT INTO `xf_forum` 
(`node_id`, `discussion_count`, `message_count`, `last_post_id`, `last_post_date`, `last_post_user_id`, `last_post_username`, `last_thread_title`, `last_thread_prefix_id`, `moderate_threads`, `moderate_replies`, `allow_posting`, `allow_poll`, `count_messages`, `find_new`, `field_cache`, `prefix_cache`, `prompt_cache`, `default_prefix_id`, `default_sort_order`, `default_sort_direction`, `list_date_limit_days`, `require_prefix`, `allowed_watch_notifications`, `min_tags`, `seo`) VALUES
(2,	1,	1,	1,	1608562826,	999,	'ИмяПользователя',	'Название форума',	46,	0,	0,	1,	1,	1,	1,	'a:0:{}',	'[]',	'',	0,	'last_post_date',	'desc',	0,	0,	'all',	0,	'');

В данном запросе стоит обратить внимания на значение полей:
node_id — ID форума;
discussion_count — Количество топиков;
message_count — Количество сообщений;
last_post_id — ID последнего сообщения;
last_post_date — Дата последнего сообщения (Unix time);
last_post_user_id — ID пользователя оставившего последнее сообщение;
last_post_username — Имя пользователя оставившего последнее сообщение;
last_thread_title — Название последнего топика;
last_thread_prefix_id — ID префикса(из таблицы прифексов xf_thread_prefix) последнего топика;
count_messages — Количество сообщений;

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

Таблица (та же, что и для разделов): xf_node

Запрос:

INSERT INTO `xf_node` (`node_id`, `title`, `description`, `node_name`, `node_type_id`, `parent_node_id`, `display_order`, `display_in_list`, `lft`, `rgt`, `depth`, `style_id`, `effective_style_id`, `breadcrumb_data`, `navigation_id`, `effective_navigation_id`, `xfa_nit_type`, `xfa_nit_params`) 
VALUES
(2,	'Название раздела',	'',	NULL,	UNHEX(''),	1,	300,	1,	51,	128,	0,	0,	0,	'a:0:{}',	UNHEX(''),	UNHEX(''),	0,	'');

В данном запросе стоит обратить внимания на значение полей:
node_id — ID форума;
title — Название форума;
parent_node_id — ID раздела к которому относится данный форум.

Сохраняем топики

Таблица: xf_thread

Запрос:

INSERT INTO `xf_thread` (`thread_id`, `node_id`, `title`, `reply_count`, `view_count`, `user_id`, `username`, `post_date`, 
`sticky`, `discussion_state`, `discussion_open`, `discussion_type`, `first_post_id`, `first_post_likes`, `last_post_date`, 
`last_post_id`, `last_post_user_id`, `last_post_username`, `prefix_id`, `tags`, `custom_fields`, `brms_promote_date`, 
`is_sticked`, `seo`, `rate`, `rate_count`) 
VALUES
(1,	2,	'Название форума',	0,	1,	999,	'ИмяПользователя',	'1608562826',	0,	'',	0,	'',	0,	0,	'1608562826',	0,	999,	'ИмяПользователя', 46,	'a:0:{}',	'a:0:{}',	0,	0,	'a:0:{}',	0.0,	0);

В данном запросе стоит обратить внимания на значение полей:
thread_id — ID топика;
node_id — ID форума к которому относится топик;
title — Название топика;
post_date — Дата создания топика (Unix time);
prefix_id — ID префикса из таблицы xf_thread_prefix, префикс это метка вначале названия топика: важно, новинка, информация, и т.д.

Сохраняем сообщения в топиках

Таблица: xf_post

Запрос:

INSERT INTO `xf_post` (`post_id`, `thread_id`, `user_id`, `username`, `post_date`, `message`, `ip_id`, `message_state`, `attach_count`, `position`, `likes`, `like_users`, `warning_id`, `warning_message`, `last_edit_date`, `last_edit_user_id`, 
`edit_count`, `embed_metadata`) 
VALUES
(1,	1,	999,	'ИмяПользователя',	'1608562826',	'Текст сообщения топика',	0,	'visible',	1,	0,	0,	'',	0,	'',	"1608562826',	1,	1,	'');

В данном запросе стоит обратить внимания на значение полей:
post_id — ID сообщения;
thread_id — ID топика;
user_id — ID пользователя;
username — Имя пользователя;
post_date — Дата создания сообщения (Unix time);

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

Обновляем топик, указывая ID последнего сообщения и дату:

UPDATE `xf_thread` SET 
`first_post_id` = '1',
`last_post_id` = '1',
`discussion_state` = 'visible',
`post_date` = '1608562826',
`last_post_date` = '1608562826'
WHERE `thread_id` = '1'

Обновляем информацию о последнем активном топике и сообщении в таблице форума xf_forum:

UPDATE `xf_forum` SET 
`last_thread_title` = 'Название форума',
`last_post_id` = '1',
`last_post_user_id` = '999',
`last_post_date` = '1608562826',
`last_post_username` = 'ИмяПользователя',
`discussion_count` = `discussion_count` + 1,
`message_count` = `message_count` + 1
WHERE `node_id` = '2'";

Технические характеристики

Парсер состоит из двух частей, как я уже ранее сказал, это — головная часть на JavaScript запускаемая через UserScript или PhantomJS который непосредственно парсит данные с сайта донора и принимающий данные скрипт inputParseData написанный на PHP 7, который сохраняет данные в MySQL базу данных XenForo. Скорость парсинга можно наращивать путем параллельного запуска нескольких копий(вкладок в браузере) головной части. Настройка парсера на сайт донора осуществляется указанием путей xPath до нужных HTML элементов страницы.

Заключение

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

Обращайтесь буду рад поработать с вами!

Оставить комментарий

Автор блога
Роман Чернышов
Веб-разработчик,
Full Stack
Senior, Architect
PHP, JavaScript, Node.JS, Python, HTML 5, CSS 3, MySQL, Bash, Linux Admin
Заказать работу
предложить оффер

Моя книга
Книга. Веб-разработчик. Легкий вход в профессию
Печатная книга
Веб-разработчик.
Легкий вход в профессию
Оформить предзаказ
Последние вопросы
Список вопросов
Последние комментарии
Меню

Archive

Мои проекты
Insurance CMS Love Crm CMS Совместные покупки Мой PHP Framework Хостинг для моих клиентов Лицензии на мой софт и поддержка