Шаблон для инет-скриптов
|
|
|
Vladislav 06.01.11 20:30 #1
|
Ранее уже выкладывал этот шаблон в скриптах, но по каким-то причинам был удалён, и вот новая версия вполне работоспособна.
Типа инструкция Сейчас мы сделаем скрипт pritcha.tcl который будет выводить притчи на канал: 1. открываем template.tcl, редактируем шапку скрипта. 2. заменяем везде template на pritcha 3. в variable pub:pritcha "$unamespace шаблон" заменяем шаблон на притча 4. в variable fetchurl "http://сайт" указываем ссылку http://pritchi.ru/id_ 5. заменяем строку: Код lput puthelp "Формат: $::gprefix(1)pritcha - описание." $prefix ; return на: Код lput puthelp "Формат: $::gprefix(1)притча - выводит случайную притчу." $prefix ; return 6. в данном случае для того чтобы получать случайную притчу нам нужно открывать страничку со случайным числом на конце, для этого строку: заменяем на: Код set aurl [expr [rand 6000]+1] 7. парсер. Указываем ту часть кода странички которая нас интересует Код if {[regexp -nocase -- { к о д } $str garb text]} { заменяем на: Код if {[regexp -nocase -- {<h1>(.*?)</h1>.*?<p>(.*?)<div class="additional">} $str garb text text2]} { (.*?) - это название притчи (text) (.*?) - это сама притча (text2)
8. вставляем ниже: Код regsub -all -nocase -- {<p>|<br />} $text2 " " text2 regsub -all -nocase -- {<i>|</i>} $text2 "\002" text2 Это своего рода замена html кода на IRC, мы <p> <br /> <i> </i> заменили на пробел и выделение жирным.
9. изменяем вывод информации, а именно: Код put putserv "[sspace [sconv $text]]" $prefix заменим на: Код lput putserv "$::gcolor(5):: [sspace [sconv $text]]" $prefix lput putserv "[sspace [sconv $text2]]" $prefix Получим в одной строке название притчи, а с новой строки саму притчу. sspace - убирает лишние пробелы, sconv - заменяет различные html-символы, ниже, в самой процедуре sconv их можно дополнить.
P.S: если нужно текст свыше 2 строк отправлять не на канал, а в приват, дабы не флудить, надо раскоментировать: Код #if {[string length $str] > 800 && $uchan ne $unick} {set prefix [subst -noc $msgsend]} P.S.S: если кодировка сайта не cp1251 то указывает ту кодировку в: Код if {[info exists ::sp_version]} {set str [encoding convertfrom cp1251 $lbody]} {set str $lbody]]}
Пример работы скрипта pritcha.tcl: Цитата <Vladislav> !притча <Windrop> :: Кто больше? <Windrop> Вечером над лесом поднялась полная Луна. — Посмотрите, какая я большая! — сказала Луна. — Я больше Солнца, потому что прогнала его с небосвода. — Да, это так! — подтвердила одноглазая Ворона, сидевшая на дереве у болота. К полуночи Луна отразилась в маленькой Лужице. Тогда болотная Лужица обрадовано воскликнула: — Смотрите, а я, оказывается, больше Луны! Луна полностью уместилась в моих берегах, да ещё осталось достаточно <Windrop> места для звёзд. — Да, это верно, — согласилась одноглазая Ворона и принялась размышлять. — Если Луна, отразившись в тебе, уместилась в твоих берегах, и осталось ещё место для звёзд, ты больше неё. Но мой глаз больше тебя. — Это каким образом? — спросила болотное Лужица. — Очень просто, — ответила одноглазая Ворона. — Ты, Лужица, умещаешься в моём глазу вместе с Луной и звёздами, да ещё остается место, чтобы уместить <Windrop> всё болото. Бельмо, сидевшее на левом глазу Вороны, важно сказало: — Самое большое в мире — это я. Стоит мне пересесть на твой правый зрячий глаз, Ворона, и я закрою не только Лужицу с Луной, но и весь мир. — Да, это правда, — ещё раз согласилась Ворона. — Ты, Бельмо, самое большое в мире. <Vladislav> !притча -Windrop- пожалуйста повторите попытку позже. Сервис будет доступен для использования через 6 сек.
Цитата <Vladislav> !притча #windrop 5 -Windrop- Включен автоматический постинг в канал #windrop (раз в 5 мин.)
Если бот перезагружался, то автоматический вывод начнётся после команды !притча.
Цитата <Vladislav> !притча #windrop 0 -Windrop- Выключен автоматический постинг в канал #windrop
|
|
|
|
Vladislav 12.07.11 11:45 #2
|
Ещё один вариант более простой и менее функциональный, однако может чему-то научить.
В качестве примера будем забирать статистику форума - http://city.is74.ru/forum/forum.php
Цитата Тем: 78,887 Сообщений: 3,300,923 Пользователи: 31,608 Активные участники: 10,125
Начнем с бинда команды которая будет вызывать процедуру парсинга:
Код bind pub - !статфорум parsing
Ну и сама процедура:
Код proc parsing {nick uhost hand chan text} { ....Далее мы будем сюда записывать код.... }
Следующей строкой в процедуре указываем юзерагент с которым бот будет конектиться к сайту. В ::http::config может быть много параметров, но пока нам это не надо.
Код ::http::config -useragent "Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)"
Далее указываем адрес сайта и таймаут соединения, чтобы бот не вис при обращении к недоступному сайту. Тут тоже кроме адреса и таймаута можно указать другие параметры но и они нам тоже пока не пригодятся.
Код set parsing_tok [::http::geturl "http://city.is74.ru/forum/forum.php" -timeout 20000]
Тут в переменную data мы получаем содержимое нашей страницы без переноса строк и прочей ненужности:
Код set data [regsub -all -- {\n|\r|\t|\v} [::http::data $parsing_tok] ""]
И удаляем переменные более не используемые в токене:
Код ::http::cleanup $parsing_tok
На этом этапе стоит обратить своё внимание на кодировку сайта (добавим строку "return $data" для того чтоб увидеть сайт так как видет его бот), если выводит криво, то указываем кодировку, например:
Код set data [encoding convertfrom cp1251 $data]
В нашем случае сайт попался не вредный и вот тот участок кода который нам и нужен:
И вот тут начинается самая вкусная, интересная и очень важная операция от которой зависит на сколько наш скрипт будет устойчив к изменениям на сайте. Во первых мы сделаем проверку на нужный нам код, видим, что интересующий нас текст заключён в тегах "<dl></dl>" поэтому и оставляем только его:
Код if {[regexp -nocase -- {<dl>(.*?)</dl>} $data -> data]} { ....Далее мы будем сюда записывать код.... } {putserv "NOTICE $nick :Ошибка парсинга."}
Добавим строку "return $data" и посмотрим то ли мы взяли:
То что нужно! Теперь надо обработать код и можно выводить результат, но тут есть у нас два варианта: вырезать лишнее либо взять только нужное. Второй вариант будет более прихотлив к коду и чаще будет "ломаться" скрипт. Рассмотрим оба способа, но для итога возьмём первый. И так начнём:
Убираем из кода теги <dt> и </dt>:
Код regsub -all -- {<dt>|</dt>} $data "" data
Заменяем <dd> и </dd> на пробел и убираем лишние пробелы:
Код regsub -all -- {<dd>|</dd>} $data " " data regsub -all -- {\s+} $data " " data
На этом первый способ закончен переходим к рассмотрению второго:
Убираем лишние пробелы:
Код regsub -all -- {\s+} $data " " data
Добавляем проверку с присвоением переменной нужным нам данным. Первая (.*?) = tdata, вторая (.*?) = sdata и тд:
Код if {[regexp -nocase -- {<dt>Тем</dt><dd>(.*?)</dd><dt>Сообщений</dt><dd>(.*?)</dd><dt>Пользователи</dt><dd>(.*?)</dd> <dt>Активные участники</dt> <dd>(.*?)</dd>} $data -> tdata sdata pdata adata]} { ....Далее мы будем сюда записывать код.... } {putserv "NOTICE $nick :Ошибка парсинга (2)."}
Соберём в одну переменную то, что у нас получилось:
Код set data "Тем $tdata Сообщений $sdata Пользователи $pdata Активные участники $adata"
На этом второй способ заканчиваем. Добавим строку "return $data" и посмотрим что у нас получилось:
Цитата Тем 78,889 Сообщений 3,300,866 Пользователи 31,610 Активные участники 10,144
Замечательно, в обоих случаях у нас один результат. Не забываем убирать после просмотра return $data.
Выводим результат на канал, на всякий случай обрежем пробелы по краям:
Код putserv "PRIVMSG $chan :[string trim $data]"
Вот что у нас получилось:
Код bind pub - !статфорум parsing
proc parsing {nick uhost hand chan text} { ::http::config -useragent "Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)" set parsing_tok [::http::geturl "http://city.is74.ru/forum/forum.php" -timeout 20000] set data [regsub -all -- {\n|\r|\t|\v} [::http::data $parsing_tok] ""] ::http::cleanup $parsing_tok
if {[regexp -nocase -- {<dl>(.*?)</dl>} $data -> data]} { regsub -all -- {<dt>|</dt>} $data "" data regsub -all -- {<dd>|</dd>} $data " " data regsub -all -- {\s+} $data " " data putserv "PRIVMSG $chan :[string trim $data]" } {putserv "NOTICE $nick :Ошибка парсинга."}
}
Проверить можно данный скрипт (немного урезан для того чтоб уместился в строке) прямо на канале:
Цитата <Vladislav> !tcl ::http::config -useragent "Mozilla/4.0"; set parsing_tok [::http::geturl "http://city.is74.ru/forum/forum.php" -timeout 20000]; set data [regsub -all -- {\n|\r|\t|\v} [::http::data $parsing_tok] ""]; ::http::cleanup $parsing_tok; regexp -nocase -- {<dl>(.*?)</dl>} $data -> data; regsub -all -- {<dt>|</dt>} $data "" data; regsub -all -- {<dd>|</dd>} $data " " data; regsub -all -- {\s+} $data " " data; return $data
<Windrop> [RETURN 1588.153ms] Тем 78,889 Сообщений 3,300,820 Пользователи 31,610 Активные участники 10,144
|
|
|
|
Vladislav 12.07.11 13:20 #3
|
Давайте рассмотрим несколько примеров регулярных выражений которые помогут нам избавиться от мусора в коде при парсинге:
Данным примером мы вырезаем все теги:
Код regsub -all -- {<.*?>} $t {} t
Цитата <Vladislav> !tcl set t {</table><a class="forumBar" href="http://windrop.clan.su/forum/12">Мастерская</a><br>}; regsub -all -- {<.*?>} $t {} t; return $t
<Windrop> [RETURN 0.047ms] Мастерская
А тут мы выносим результат для редактирования:
Код regsub -all -nocase -- {<.*?>(.*?)</.*?>} $t "\\1" t
Цитата <Vladislav> !tcl set t {<a class="forumBar" href="http://windrop.clan.su/forum/12">Мастерская</a>}; regsub -all -nocase -- {<.*?>(.*?)</.*?>} $t "~\\1~" t; return $t
<Windrop> [RETURN 0.037ms] ~Мастерская~
Заменяем теги на то, что нам нужно:
Код regsub -all -nocase -- {<br.*?>} $t { } t regsub -all -nocase -- {<b>|</b>} $t "\002" t
Цитата <Vladislav> !tcl set t {Бот<br><b>Мастерская</b><br />Немного о IRC}; regsub -all -nocase -- {<br.*?>} $t { } t; regsub -all -nocase -- {<b>|</b>} $t "\002" t; return $t
<Windrop> [RETURN 0.046ms] Бот Мастерская Немного о IRC
Если нам не нужно заменять коды на спец символы, то мы их можем всем классом вырезать:
Код regsub -all -nocase -- {([0-9]{1,5};);} $t {} t
Цитата <Vladislav> !tcl set t {З9;МастерскаяЗ9;}; regsub -all -nocase -- {([0-9]{1,5};);} $t {} t; return $t
<Windrop> [RETURN 0.101ms] Мастерская
Пример обрезки в начале и в конце (тоже самое что string trim если целью являются пробелы):
Код regsub -all {^\s+} $t "" t regsub -all {\s+$} $t "" t
Цитата <Vladislav> !tcl set t { Мастерская }; regsub -all {^\s+} $t "" t; regsub -all {\s+$} $t "" t; return $t
<Windrop> [RETURN 0.029ms] Мастерская
Бывает так, что в коде то есть ковычки то их нет:
Код regsub -all -nocase -- {<div class="?edited"?>(.*?)</div>} $t "\\1" t
Цитата <Vladislav> !tcl set t {<div class="edited">Мастерская</div>}; regsub -all -nocase -- {<div class="?edited"?>(.*?)</div>} $t "\\1" t; return $t <Windrop> [RETURN 0.063ms] Мастерская
<Vladislav> !tcl set t {<div class=edited>Мастерская</div>}; regsub -all -nocase -- {<div class="?edited"?>(.*?)</div>} $t "\\1" t; return $t <Windrop> [RETURN 0.071ms] Мастерская
Ну и здесь же рассмотрим string map (не копировать коды, для отображения их здесь заменены буквы):
Код set t [string map -nocase {{&mdаsh;} {-} {&quоt;} {"}} $t]
Цитата <Vladislav> !tcl set t {&quоt;Мастерская &mdаsh; Vladislav&quоt;}; set t [string map -nocase {{&mdаsh;} {-} {&quоt;} {"}} $t]; return $t
<Windrop> [RETURN 0.035ms] "Мастерская - Vladislav"
|
|
|
|
Vladislav 31.07.11 14:36 #4
|
Эта процедура поможет избежать многократного переписывания однотипных строк. Загрузите ее отдельным скриптом и обращайтесь к ней:
Код set data [web2data $url $nick]
В случае каких-либо ошибок процедура вернет 0 и напишет об ошибке указанному нику, если ошибок нет, то вернется HTML код страницы.
Код proc web2data {url nick} { # Указываем юзерагент с которым бот будет конектиться к сайту: ::http::config -useragent "Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)" # Отправляем http запрос, указываем таймаут и используем catch для того, чтобы избежать остановки выполнения процедуры в случае проблем связи с сайтом или ошибок: if {[catch {set token [::http::geturl $url -timeout 10000]} error]} {putserv "NOTICE $nick :Ошибка: $error"; return 0 # Если указанный адрес не существует: } elseif {[::http::ncode $token] == "404"} {putserv "NOTICE $nick :Ошибка: [::http::code $token]" # Проверяем статус запроса, если все хорошо, то в переменной data оказывается код страницы: } elseif {[::http::status $token] == "ok"} {set data [regsub -all -- {\n|\r|\t|\v} [::http::data $token] ""] # Если время соединения превышено, то пишем об этом: } elseif {[::http::status $token] == "timeout"} {putserv "NOTICE $nick :Ошибка: Таймаут соединения." # Тоже самое с остальными ошибками: } elseif {[::http::status $token] == "error"} {putserv "NOTICE $nick :Ошибка: [::http::error $token]"} # И наконец, освобождаем переменные токена для последующих операций: if {[info exists data]} {::http::cleanup $token; return [regsub -all -- {\s+} $data " "]} else {::http::cleanup $token; return 0} }
|
|
|
|
Vladislav 10.01.14 04:15 #5
|
Максимально короткий пример получения данных с сайта:
Код !tcl regexp -- {id="info32".*?Version: (.*?). Released: (.*?)</p>.*?<a href="(.*?)"} [::ccs::get_httpdata http://steelseries.com/support/downloads] -> v d u; return "\037[join [lreverse [split $d -]] .]\037-> версия: $v \002@\002 $u"
06.11.2013-> версия: 2.8.0171 @ http://cdn-co.steelseries.com/downloads/drivers/engine/SteelSeriesEngine_2.8.0171.exe
В данном примере мы ещё поработали с датой, изначально она была: 2013-11-06
|
|
|
|