Читаем цитаты с bash.org.ru своей программой
Автор: Вадим К
I. Вступление
Интернет всё больше и больше входит в нашу жизнь. Мы часто сидим и нажимаем кнопку "обновить", ожидая новых данных на заветной страничке. Но ведь надо и работать когда-то, поэтому задачу мониторинга странички с новостями мы поручим Delphi, а сами будем работать (или заниматься другим полезным делом – жизнь ведь прекрасна). Если что – всё будет скачано и представлено в удобном для нас виде.
В качестве "подопытного кролика" я выбрал многим известный цитатник рунета – bash.org.ru. Программа будет забирать с главной странички цитаты, и складывать их в базу. А с цитатами в базе можно делать что угодно.
Эта статья отличается от моих других тем, что я даю сразу готовое приложение и комментирую интересные моменты.
II. Общие замечания
Внешний интерфейс программы не претендует на награду. Я над ним особенно не задумывался, так как основная задача в другом. Интерфейс сделаете сами, если он вам нужен.
Это приложение построено по модульно-объектной технологии (мне больше нравится название "Разделяй и властвуй"). То-есть, для каждой сущности есть объект, и эти объекты взаимодействуют между собой. Так как связи и зависимости между ними минимальные, то можно легко их заменять и модифицировать.
Чтобы легче было искать место в коде, на которое я ссылаюсь, я использую специальные маркеры. В тексте статьи они выглядят так {текст}. Чтобы найти код, который соответствует ему, нужно сделать следующее. Перейти в Delphi, выбрать в меню View » ToDo List. Найти там соответствующий текст и кликнуть дважды. А по тексту программы вы увидите их как { TODO -oVadim -cView : текст }.
III. Класс базы данных (БД)
Обзор класса
Начнём разбор с класса, который обслуживает базу данных. Только объекты этого класса имеют доступ к базе данных, только они знают как с ней работать. Остальные классы не должны ничего знать о базе данных.
В самом верху юнита BBasa объявлен абстрактный класс TBBasaCustom {B1}. Этот класс имеет только один метод для добавления цитат в базу, да и тот абстрактный. Рабочий класс будет наследоваться от него. Это сделано специально, чтобы из того объекта, который будет добавлять данные, нельзя было бы обратиться к другим методам, и ему было всё равно, какие ещё методы есть у класса. Они ему не нужны. Это выглядит дивно, но позже станет понятно.
В этом же юните объявлен класс TBBasa {B2}. Этот класс наследуется от нашего класса и реализовывает всю нужную функциональность, а именно:
Открывает БД при своем создании. Если файл базы отсутствует – он его создаст. {B6}
Умеет добавлять цитату, так как реализовывает абстрактный метод Add. {B3}
Умеет найти в базе цитату по номеру – метод GetQuotes. {B4}
Посмотрите внимательно на public методы. Ни один из них не расскажет о том, как и где хранятся данные. Поэтому, никто не мешает переписать методы и хранить цитаты в текстовом файле или нескольких файлах. Остальной код этого никак не почувствует. Ощущаете силу такого подхода к программированию?
В качестве базы данных я использую SQLite.
Рассмотрим некоторые методы детальнее.
Метод Add
Этот метод {B3} получает два параметра – Number и Text – соответственно номер цитаты и её текст. Перед добавлением вначале проверяет, а нет ли уже цитаты в базе. Для этого он делает запрос на выборку цитат с заданным номером. Если цитата не найдена (количество найденных строк равно нулю), то добавляем в базу. Конечно, теоретически может быть, что в базе будет несколько записей с одним номером (программная ошибка, к примеру), но я не усложняю. В конце метода есть вызов SendCommand. Это оповещение интерфейса о том, что есть новые цитаты. Как это работает – будет рассказано ниже.
Метод GetQuotes
Этот метод {B4} по заданному номеру цитаты находит её и возвращает, предварительно удалив спецсимволы. Если цитата не найдена – возвращает пустую строку (оно логично). Интересно также то, что этот метод обращается к другому, который запрашивает "сырой текст" из базы.
IV. Класс потока
Обзор
Данный класс занимается основной работой – он скачивает главную страничку, парсит, и отдаёт поштучно цитаты объекту БД. Вся реализация находится в юните BTread.
Как видно из кода, у этого класса только три публичных метода – конструктор, деструктор и запрос на обновление. А больше ему и не надо.
Рассмотрим как всё это работает. Через конструктор {T1} при создании класса мы передаём ему ссылку на объект БД, чтобы потом иметь возможность добавлять цитаты.
После того, как поток будет запущен, начинает выполняться метод Execute {T2}. Он предельно прост – пока нас не завершили, повторять: скачать, распарсить и подождать. Все действия специально разделены, чтобы быть независимыми.
Скачивание страницы
Метод скачать страничку GetText {T3} просто возвращает в виде строки содержимое страницы. Если вам по каким-то идеологическим причинам не нравятся компоненты Indy, просто замените здесь на свой код, и всё. Сам метод хорош ещё тем, что показывает, как в RunTime создавать компонент и назначать методы.
Парсинг
Вытягивать цитаты {T4} наиболее просто с помощью регулярных выражений. Откуда я взял регулярное выражение – не спрашивайте. Учитесь сами его составлять. Кстати, на сайте есть статья, посвященная им. Больше в этом методе ничего интересного.
Сон
Между скачиванием цитат надо делать паузу. Так как мы находимся в потоке, то свободно можем писать Sleep. Никакого "подвисания интерфеса" не будет. Но эта функция плоха тем, что поток сложно пробудить из такого сна. И если мы захотим в этот момент закрыть программу, то нам придётся ждать, пока функция не отработает.
Именно по этой причине я использую немного другой метод {T5}. Я использую функцию WaitForSingleObject, которая, в отличие от Sleep, может также завершиться, если "сработал объект-событие", который ей передали. Сработал – это перешёл с SetEvent.
Поэтому, если нужно обновить раньше времени или завершить работу потока, мы "включаем событие" и метод Sleep завершается досрочно.
V. Основное приложение
Обзор
Теперь, когда мы рассмотрели все вспомогательные объекты, пора их объединить и заставить работать.
Так как основная функциональность у нас уже реализована, то код предельно прост. Первым делом мы создаём два объекта – один для БД, другой для потока. Делаем это в OnCreate формы {M2}. В OnDestroy соответственно уничтожаем. Обратите внимание на последовательность создания и удаления. Она важна.
Кнопка "обновить" тоже не должна вызвать осложнений. Она просто вызывает метод Update у объекта потока.
Обратная связь
Помните вызов SendCommand у объекта БД? Эта функция находится в юните BMessage. Сама она очень проста – она просто вызывает WinAPI функцию SendMessage для посылки сообщения главной форме, а хендл главной формы берётся из Application.MainForm.Handle. Главная форма принимает эти сообщения, и уже самостоятельно обращается к нужным объектам.
Рассмотрим для примера передачу сообщения о новой цитате. После того, как цитата была сохранена, посылается сообщение MY_NEW_QUOTES и в качестве параметра передаётся номер цитаты. Второй параметр нам не нужен, и мы пишем туда 0.
В класса формы объявлен метод {M7} для перехвата этого сообщения. Получив его, мы обращаемся к объекту БД, по номеру получаем само сообщение и добавляем в Memo.
VI. Выводы
Конечно, это очень сырой пример, и на выставку он не годится. Но он хорош тем, что является заготовкой для различных программ подобного типа – собирать новости, выкачивать галереи картинок, собирать данные.
И помните, в некоторых случаях подобные программы могут не дружить с законом! Будьте внимательны, и не нарушайте.
Автор: Вадим К
Статья добавлена: 4 июня 2008
Следующая статья: Выравнивание компонентов »
Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.
Статьи, похожие по тематике
Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:
Ссылка для форумов (BBCode):
Быстрая вставка ссылки на статью в сообщениях на сайте:
{{a:47}} (буква a — латинская) — только адрес статьи (URL);
{{статья:47}} — полноценная HTML-ссылка на статью (текст ссылки — название статьи).
Поделитесь ссылкой в социальных сетях:
Комментарии читателей к данной статье
Пока нет комментариев к данной статье. Оставьте свой и он будет первым.
Оставлять комментарии к статьям могут только зарегистрированные пользователи.