Со временем в материалах на моём сайте под управлением Joomla накопился "мусор" — старые теги вида {jd_file file==4}
, оставшиеся от какого-то плагина для загрузки файлов. Они больше не работали и только портили вид контента. Вручную чистить сотни статей было не вариант, поэтому я решил написать скрипт на Python, чтобы автоматизировать процесс. Вот как это было.
Проблема
В текстах материалов (в полях introtext
и fulltext
) встречались конструкции вроде:
{jd_file file==4}
{jd_file file==123}
{jd_file file==7}
Эти теги были частью старой системы, которая уже не используется, и их нужно было просто удалить из всех материалов, чтобы привести контент в порядок.
Решение
Я написал небольшой скрипт на Python, который подключается к базе данных Joomla, ищет эти теги с помощью регулярных выражений и убирает их. Вот как это выглядело шаг за шагом.
Шаг 1: Подключение к базе данных
Для начала я настроил подключение к базе Joomla через pymysql
. Параметры подключения хранятся в файле .env
:
import pymysql import os import re from dotenv import load_dotenv import logging load_dotenv() logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s') jedig_db_config = { 'host': os.getenv('JEDIG_MYSQL_HOST'), 'user': os.getenv('JEDIG_MYSQL_USER'), 'password': os.getenv('JEDIG_MYSQL_PASSWORD'), 'db': os.getenv('JEDIG_MYSQL_DATABASE'), 'charset': 'utf8mb4', 'cursorclass': pymysql.cursors.DictCursor } jedig_table_prefix = 'dyutb_'
Префикс dyutb_
соответствует моей базе на сайте jedig.ru
.
Шаг 2: Определение паттерна
Чтобы найти все варианты тегов {jd_file file==<цифра>}
, я использовал регулярное выражение:
jd_file_pattern = re.compile(r'\{jd_file file==\d+\}')
Здесь \d+
означает одну или более цифр, так что паттерн ловит, например, {jd_file file==4}
или {jd_file file==123}
.
Шаг 3: Основная функция
Вот код, который выполняет очистку:
def remove_jd_file_tags(): jedig_connection = pymysql.connect(**jedig_db_config) try: with jedig_connection.cursor() as jedig_cursor: jedig_cursor.execute(""" SELECT `id`, `title`, `introtext`, `fulltext` FROM `dyutb_content` """) items = jedig_cursor.fetchall() for item in items: updated = False item_id = item['id'] title = item['title'] introtext = item['introtext'] or "" if introtext and jd_file_pattern.search(introtext): updated_introtext = jd_file_pattern.sub('', introtext) jedig_cursor.execute(""" UPDATE `dyutb_content` SET `introtext` = %s WHERE `id` = %s """, (updated_introtext, item_id)) updated = True logging.info(f"Удалены теги из introtext материала ID: {item_id}, Заголовок: {title}") fulltext = item['fulltext'] or "" if fulltext and jd_file_pattern.search(fulltext): updated_fulltext = jd_file_pattern.sub('', fulltext) jedig_cursor.execute(""" UPDATE `dyutb_content` SET `fulltext` = %s WHERE `id` = %s """, (updated_fulltext, item_id)) updated = True logging.info(f"Удалены теги из fulltext материала ID: {item_id}, Заголовок: {title}") if updated: logging.info(f"Материал ID: {item_id} обновлён.") jedig_connection.commit() logging.info("Очистка завершена.") except Exception as e: logging.error(f"Ошибка: {e}") finally: jedig_connection.close() remove_jd_file_tags()
Что делает этот код:
- Выбирает все материалы из таблицы
dyutb_content
. - Для каждого материала проверяет
introtext
иfulltext
на наличие тегов. - Если теги найдены, заменяет их на пустую строку и обновляет запись в базе.
- Логирует каждое изменение и фиксирует результат.
Шаг 4: Запуск
Я сохранил скрипт в файл clean_jd_file.py
и запустил его:
python clean_jd_file.py
Логи показали, где были изменения, например:
2025-03-11 12:34:56,123:INFO:Удалены теги из fulltext материала ID: 45, Заголовок: Land Rover Maintenance 2025-03-11 12:34:56,456:INFO:Очистка завершена.
Результат
После запуска скрипта все теги {jd_file file==<цифра>}
исчезли из материалов. Например, если раньше было:
Текст статьи {jd_file file==4} и ещё немного текста.
Теперь стало:
Текст статьи и ещё немного текста.
Контент стал чище и аккуратнее, без старого "мусора".
Вывод
Python снова выручил меня, когда нужно было быстро привести в порядок старые материалы. Регулярные выражения и прямой доступ к базе данных Joomla сделали задачу простой и эффективной. Если в будущем появится другой "мусор", я просто скорректирую паттерн в скрипте и повторю процесс.
Полный код:
import pymysql
import os
import re
from dotenv import load_dotenv
import logging
load_dotenv()
logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')
jedig_db_config = {
'host': os.getenv('JEDIG_MYSQL_HOST'),
'user': os.getenv('JEDIG_MYSQL_USER'),
'password': os.getenv('JEDIG_MYSQL_PASSWORD'),
'db': os.getenv('JEDIG_MYSQL_DATABASE'),
'charset': 'utf8mb4',
'cursorclass': pymysql.cursors.DictCursor
}
jedig_table_prefix = 'dyutb_'
# Регулярное выражение для поиска конструкций {jd_file file==<цифра>}
jd_file_pattern = re.compile(r'\{jd_file file==\d+\}')
def remove_jd_file_tags():
jedig_connection = None
try:
jedig_connection = pymysql.connect(**jedig_db_config)
with jedig_connection.cursor() as jedig_cursor:
# Выбираем все материалы с полями introtext и fulltext
jedig_cursor.execute(f"""
SELECT `id`, `title`, `introtext`, `fulltext`
FROM `{jedig_table_prefix}content`
""")
items = jedig_cursor.fetchall()
for item in items:
updated = False
item_id = item['id']
title = item['title']
# Обработка introtext
introtext = item['introtext'] or ""
if introtext and jd_file_pattern.search(introtext):
updated_introtext = jd_file_pattern.sub('', introtext)
jedig_cursor.execute(f"""
UPDATE `{jedig_table_prefix}content`
SET `introtext` = %s
WHERE `id` = %s
""", (updated_introtext, item_id))
updated = True
logging.info(f"Удалены конструкции jd_file из introtext материала ID: {item_id}, Заголовок: {title}")
# Обработка fulltext
fulltext = item['fulltext'] or ""
if fulltext and jd_file_pattern.search(fulltext):
updated_fulltext = jd_file_pattern.sub('', fulltext)
jedig_cursor.execute(f"""
UPDATE `{jedig_table_prefix}content`
SET `fulltext` = %s
WHERE `id` = %s
""", (updated_fulltext, item_id))
updated = True
logging.info(f"Удалены конструкции jd_file из fulltext материала ID: {item_id}, Заголовок: {title}")
if updated:
logging.info(f"Материал ID: {item_id} обновлён.")
jedig_connection.commit()
logging.info("Обработка завершена.")
except Exception as e:
logging.error(f"Ошибка при выполнении скрипта: {e}")
finally:
if jedig_connection:
jedig_connection.close()
if __name__ == "__main__":
remove_jd_file_tags()