ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
20.8. Поиск свежих ссылокПроблемаИмеется список URL. Вы хотите узнать, какие из них изменялись позже других.РешениеПрограмма из примера 20.6 читает URL из стандартного ввода, упорядочивает их по времени последней модификации и выводит в стандартный вывод с префиксами времени. Пример 20.6. surl#!/usr/bin/pecl -w # surl - сортировка URL по времени последней модификации use LWP::UserAgent; use HTTP::Request; use URI::URL qw(url); my($url, %Date): my $ua = LWP::UserAgent->new(), while ( Surl = url(scalar <>) ) { my($req, $ans); next unless $url->scheme =~ /"(file|https?)$/, Sans = $ua->request(HTTP::Request->new("HEAD", $url)); if ($ans->is_success) { $Date{$url} = $ans->last_modified || 0; # unknown } else { print STDERR "$url: Error [", $ans->code, "] ", $ans->message, "!\n"; } } foreach Surl ( sort { $Date{$b} <=> $Date{$a} } keys %Date ) { printf "%-25s %s\n", $Date{$url} ? (scalar localtime $Date{$url}) : " $url; } КомментарийСценарий surl больше похож на традиционную программу-фильтр. Он построчно читает URL из стандартного ввода (па самом деле данные читаются из%xurl http://www.perl.com/ | surl | head Моп Арг 20 06:16:02 1998 http://electriclichen.com/linux/sroni.html Fri Apr 17 13:38:51 1998 http://www.oreilly.com/ Fri Mar 13 12:16:47 1998 http://www2.binevolve.com/ Sun Mar 8 21:01:27 1998 http://www.perl.org/ Tue Nov 18 13:41:32 1997 http://www.perl.com/universal/header.map Wed Oct 1 12:55:13 1997 http://www.songline.com/ Sun Aug 17 21:43:51 1997 http://www.perl.com/graphics/perlhome.header.jpg Sun Aug 17 21:43:47 1997 http://www.perl.com/graphics/perl.id.313c.gif Sun Aug 17 21:43:46 1997 http://www.perl.com/graphics/ora.logo.gif Sun Aug 17 21:43:44 1997 http://www.perl.com/graphics/header-nav.gif Маленькие программы, которые выполняют свою узкую задачу и могут объединяться в более мощные конструкции, - верный признак хорошего программирования. Более того, можно было бы заставить xurl работать с файлами и организовать фактическую выборку содержимого URL в Web другой программой, которая бы передавала свои результаты xurl, churl или surl. Вероятно, эту программу следовало бы назвать gurl, но программа с таким именем уже существует: в комплекс модулей LWP входит программа Iwp-request с синонимами HEAD, GET и POST для выполнения этих операций в сценариях командного интерпретатора. > Смотри также -------------------------------
20.9. Создание шаблонов HTMLПроблемаВы хотите сохранить параметризованный шаблон во внешнем файле, прочитать его в сценарий CGI и подставить собственные переменные вместо заполнителей, находящихся в тексте. Это позволяет отделить программу от статических частей документа.РешениеЕсли вы ограничиваетесь заменой ссылок на переменные, используйте функциюtemplate: sub template { my ($filename, $fillings) = @_; my $text; local $/; # Режим поглощающего ввода (undef) local *F; # Создать локальный манипулятор open(F, "< $filename") [| return; $text = close(F); # Игнорировать код возврата # Заменить конструкции %%...%% значениями из хэша %$#ilings $text =~ s{ %%(.*?)%%} { exists( $fillings->{$1} ) ? $fillings->{$1} }gsex; return $text; } В этом случае используемые данные выглядят так: BODY> H1>Report for %%username%% /H1> %%username%% logged in %%count%% times, for a total of %%total%% minutes. Для расширения полноценных выражений используйте модуль Text::Template с CPAN, если вы можете гарантировать защиту данных от постороннего вмешательства. Файл данных для Text::Template выглядит так: !- fancy.template for Text::Template -> HTMLXHEAD>TITLE>Report for {$user}/TITLEx/HEAD> BODY>H1>Report for {$user}/H1> { lcfirst($user) } logged in {$count} times, for a total of { int($seconds / 60) } minutes. КомментарийПараметризованный ввод в сценариях CGI хорош по многим причинам. Отделение программы от данных дает возможность другим людям (например, дизайнерам) изменять код HTML, не трогая программы. Еще лучше то, что две программы могут работать с одним шаблоном, поэтому стилевые изменения шаблона немедленно отразятся на обеих программах.Предположим, вы сохранили в файле первый шаблон из решения. Ваша программа CGI содержит определение функции template (см. выше) и соответствующим образом задает значения переменных $whats_his_name, $login_count и $minute_used. Шаблон заполняется просто: %fields = ( username => $whats_his_name, count => $login_count, total => $minute_used, ); print templateC'/home/httpd/templates/simple.template", \%fields); Файл шаблона содержит ключевые слова, окруженные двойными символами % (%%КЛЮЧЕВОЕ-СЛОВО%%). Ключевые слова ищутся в хэше %$fillings, ссылка на который передается template в качестве второго аргумента. В примере 20.7 приведен более близкий к реальности пример, использующий базу данных SQL. Пример 20.7. userrepi #!/usr/bin/perl -w # userrepi - вывод данных о продолжительности работы пользователей # с применением базы данных SOL use DBI; use CGI qw(:standard); # Функция template() определена в решении (см. выше) $user = param("username") or die "No username"; $dbh = DBI->connect("dbi:mysql:connections:mysql.domain.com:3306", "connections", "seekritpassword") or die "Couldn't connect\n"; $sth = $dbh->prepare(""END_OF_SELECT") or die "Couldn't prepare SQL"; SELECT COUNT(duration),SUM(duration) FROM logins WHERE username='$user' ENO_OF_SELECT if (@row = $sth->fetchrow()) { ($count, $seconds) = @row; } else { ($count, $seconds) = (0,0); } $sth->finish(); $dbh->disconnect; print header(); print templateC'report.tpl", { 'username' => $user, 'count' => $count, 'total' => $total }); Если вам потребуется более изощренное и гибкое решение, рассмотрите второй шаблон решения, основанный на модуле Text::Template с CPAN. Содержимое пар фигурных скобок, обнаруженных в файле шаблона, вычисляется как код Perl. Как правило, расширение сводится к простой подстановке переменных: You owe: {$total} но в фигурных скобках также могут находиться полноценные выражения: The average was {$count ? ($total/$count) : 0}. Возможное применение этого шаблона продемонстрировано в примере 20.8. Пример 20.8. userrep2 #!/usr/bin/perl -w # userrep2 - вывод данных о продолжительности работы пользователей # с применением базы данных SQL use Text::Template; use DBI; use CGI qw(:standard); $tmpl = "/home/httpd/templates/fancy.template"; $template = Text::Template->new(-type => "file", -source => $tmpl); $user = param("username") or die "No username"; $dbh = DBI->connect("dbi:mysql:connections:mysql.domain.com:3306", "connections", "secret passwd") or die "Couldn't db connect\n"; $sth = $dbh->prepare(""END_OF_SELECT") or die "Couldn't prepare SQL"; SELECT COUNT(duration),SUM(duration) FROM logins WHERE username='$user' END_OF_SELECT $sth->execute() or die "Couldn't execute SQL"; if (Orow = $sth->fetchrow()) { ($count, $total) = @irow; } else { $count = $total = 0; } $sth->finish(); $dbh->disconnect; print header(); print $template->fill_in(); При более широких возможностях этого подхода возникают определенные проблемы безопасности. Любой, кому разрешена запись в файл шаблона, сможет вставить в него код, выполняемый вашей программой. В рецепте 8.17 рассказано о том, как снизить этот риск. > Смотри также -------------------------------
20.10. Зеркальное копирование Web-страницПроблемаВы хотите поддерживать локальную копию Web-страницы.РешениеВоспользуйтесь функцией mirror модуля LWP::Simple:use LWP::Simple; mirror($URL, $local_filename); КомментарийНесмотря на тесную связь с функцией get, описанной в рецепте 20.1, функция mirror не выполняет безусловной загрузки файла. В создаваемый ей запрос GET включается заголовок If-Modified-Since, чтобы сервер передавал лишь недавно обновленные файлы. Функция mirror копирует только одну страницу, а не целое дерево. Для копирования набора страниц следует использовать ее в сочетании с рецептом 20.3. Хороший вариант зеркального копирования целого удаленного дерева приведен в программе w3mir, также находящейся на CPAN. Будьте осторожны! Можно (и даже просто) написать программу, которая сходит с ума и начинает перекачивать все Web-страницы подряд. Это не только дурной тон, но и бесконечный труд, поскольку некоторые страницы генерируются динамически. Кроме того, у вас могут возникнуть неприятности с теми, кто не желает, чтобы их страницы загружались en masse.> Смотри также -------------------------------
20.11. Создание роботаПроблемаТребуется написать сценарий, который самостоятельно работает в Web (то есть робота). При этом желательно уважать правила работы удаленных узлов.РешениеВместо модуля LWP::UserAgent используйте в роботе модуль LWP::RobotUA:use LWP::RobotUA; $ua = LWP::RobotUA->new('websnuffler/0.1', 'me@wherever.com'): КомментарийЧтобы жадные роботы не перегружали серверы, на узлах рекомендуется создавать файл с правилами доступа robots.txt. Если ваш сценарий получает лишь один документ, ничего страшного, но при получении множества документов с одного сервера вы легко перекроете пропускную способность узла.Создавая собственные сценарии для работы в Web, важно помнить о правилах хорошего тона. Во-первых, не следует слишком часто запрашивать документы с одного сервера. Во-вторых, соблюдайте правила, описанные в файле robots.txt. Самый простой выход заключается в создании агентов с применением модуля LWP::RobotUA вместо LWP::UserAgent. Этот агент автоматически "снижает обороты" при многократных обращениях к одному серверу. Кроме того, он просматривает файл robots.txt каждого узла и проверяет, не пытаетесь ли вы принять файл, размер которого превышает максимально допустимый. В этом случае возвращается ответ вида: 403 (Forbidden) Forbidden by robots.txt Следующий пример файла robots.txt получен программой GET, входящей в комплекс модулей LWP: % GET http://www.webtechniques.com/robots.txt User-agent: * Disallow: /stats Disallow: /db Disallow: /logs Disallow: /store Disallow: /forms Disallow: /gifs Disallow: /wais-src Disallow: /scripts Disallow: /config Более интересный и содержательный пример находится по адресу http:// www.cnn.com/robots.txt. Этот файл настолько велик, что его даже держат под контролем RCS! % GET http://www.cnn.com/robots.txt | head # robots, scram # $1 d : robots.txt,v 1.2 1998/03/10 18:27:01 mreed Exp $ User-agent: * Disallow: / | User-agent: Mozilla/3.01 (hotwired-test/0.1) Disallow: /cgi-bin Disallow: /TRANSCRIPTS Disallow: /development [> Смотри также -------------------------------
20.12. Анализ файла журнала Web-сервераПроблемаВы хотите извлечь из файла журнала Web-сервера лишь интересующую вас информацию.РешениеРазберите содержимое файла журнала следующим образом:while ( my ($client, $identuser, $authuser, $date, $time, $tz, $method, $url, $protocol, $status, $bytes) = /"(\S+) (\S+) (\S+) \[([-:]+):(\d+:\d+:\d+) ([-\]]+) "(\S+) (.*?) (\S+)" (\S+) (\S+)$/, # . . . } КомментарийПриведенное выше регулярное выражение разбирает записи формата Common Log Format - неформального стандарта, которого придерживается большинство Web-серверов. Поля имеют следующий смысл:client IP-адрес или имя домена для броузера. identuser Результаты команды IDENT (RFC 1413), если она использовалась. authuser Имя пользователя при аутентификации по схеме "имя/пароль". date Дата поступления запроса (0 I/Mar/I 997). time Время поступления запроса (12:55:36). tz Часовой пояс (-0700). method Метод запроса: GET, POST, PUT. uri Запрашиваемый URL (/-user/index.html). protocol HTTP/1.0 или HTTP/I.I. status Возвращаемый статус (200 - все в порядке, 500 - ошибка сервера). bytes Количество возвращаемых байт (может быть равно "-" для ошибок, перенаправлений и операций, не сопровождаемых пересылкой документа). В другие форматы также включаются данные о внешней ссылке и агенте. Ценой минимальных изменений можно заставить этот шаблон работать с другим форматом журнала. Обратите внимание: пробелы в URL не оформляются служебными символами. Это означает, что для извлечения URL нельзя использовать \8* - . * заставит регулярное выражение совпасть с целой строкой, а затем возвращаться до тех пор, пока не будет найдено соответствие для остатка шаблона. Мы используем . *? и фиксируем шаблон в конце строки с помощью $, чтобы механизм поиска не устанавливал совпадения и последовательно добавлял символы до тех пор, пока не будет найдено совпадение для всего шаблона. > Смотри также
20.13. Обработка серверных журналовПроблемаТребуется обобщить данные в серверном журнале, но у вас нет специальной программы с возможностью настройки параметров.РешениеАнализируйте журнал с помощью регулярных выражений или воспользуйтесь модулями Logfile с CPAN.КомментарийВ примере 20.9 приведен образец генератора отчетов для журнала Apache. Пример 20.9. sumwww#!/usr/bin/perl -w # sumwww - обобщение данных об операциях Web-сервера $lastdate = ""; daily_logs(); summary(); exit; # Читать файлы CLF и запоминать обращения с хоста и на URL sub daily_logs { while (<>) { ($type, $what) = /"(GET|POST)\s+(\S+?) \S+7 or next; ($host, undef, undef, $datetime) = split; ($bytes) = /\s(\d+)\s*$/ .or next; ($date) = ($datetime =- /\[([~:]*)/); $posts += ($type eq POST); $home++ if m, / ,; if ($date ne $lastdate) { if ($lastdate) { write_report() } else { $lastdate = $date } } $count++; $hosts{$host}++; $what{$what}++; $bytesum += $bytes; } write_report() if $count; } # Ускорить копирование за-счет создания синонимов # глобальных переменных вида *typeglob sub summary { $lastdate = "Grand Total"; *count = *sumcount; *bytesum = *bytesumsum *hosts = *allhosts; *posts = *allposts; *what = *allwhat; *home = *allhome; write: } # Вывести сведения по хостам и URL с применением специального формата sub wrlte_report { write; # add to summary data $lastdate = $date; $sumcount += $count: $bytesumsum += $bytesum; $allposts += $posts: $allhome += $home; # Сбросить данные за день $posts = $count = $bytesum = $home = 0; @allwhat{keys %what} = keys %what; @allhosts{keys %hosts} = keys %hosts; %hosts = %what = (); } format STDOUT_TOP = @|||||||||| @|||||| @||||||| @||||||| @|||||| @|||||| @||||||||||||| "Date","Hosts", "Accesses"'Unidocs", "POST", "Home", "Bytes' format STDOUT = @>>>>>>>>>> @>>>>>> @>>>>>>> @>>>>>>> @>>>>>> @>>>>>> @>>>>>>>>>>>>> $lastdate, scalar(keys %hosts), $count, scalar(keys %what), $posts, $home, $bytesum Bytes 16058246 61879643 64613798 52437374 55623059 250612120 Пример вывода выглядит I.IK: Date Hosts Accesses Unidocs POST Home -lQ/May/1998 353 6447 3074 352 51 20/May/1998 1938 23868 4288 972 350 21/May/1998 1775 27872 6596 1064 376 22/May/1998 1680 21402 4467 735 285 23/May/1998 1128 21260 4944 592 186 Grand Total 6050 100849 10090 3715 1248 Модуль Logfile::Apache c CPAN (см. пример 20.10) позволяет написать аналогичную, но менее специализированную программу. Этот модуль распространяется вместе с другими модулями Logfile в единой поставке Logtile (на момент написания книги - Logfile-O.im.tar.gz). Пример 20.10. aprept #!/usr/bin/perl -w # aprept - отчет по журналам Apache use Logfile::Apache; $1 = Logfile::Apache->new( File => oo-", # STDIN Group => [ Domain, File ]); $l->report(Group => Domain, Sort => Records); $l->report(Group => File, List => [Bytes,Records]); Конструктор new читает файл журнала и строит индексы. В параметре File передается имя файла, а в параметре Group - индексируемые поля. Возможные значения - Date (дата), Hour (время получения запроса), File (запрашиваемый файл), U or (имя пользователя, извлеченное из запроса), Host (имя хоста, запросившего документ) и Domain (Host, преобразованный в строку типа "France", "Germany" и т. д.). Вывод отчета в STDOUT осуществляется методом report. В параметре Group передается используемый индекс, а также дополнительно - способ сортировки (Records - по количеству обращений, Bytes - по количеству переданных байт) и способ дальнейшей группировки данных (по количеству байт или количеству обращений). Приведем примеры вывода: Domain US Commercial 222 38.47Х US Educational 115 19.93X Network Unresolved Australia Canada Mexico 93 16.12X
/cgi-bin/MxScreen
> Смотри также - Документация по модулю Logfile::Apache с СРАN;perlform(1). 20.14. Программа: htmlsubСледующая программа выполняет подстановку в HTML-файле так, что изменения происходят только в обычном текст. Предположим, у вас имеется файл index.html следующего содержания:H1>Welcome to Scooby World!I have A HREF="pictures.html">pictures/A>
of the crazy dog
IMG SRC="scooby.Jpg" ALT="Good doggy!" P>
P.S. I am deathly ill. A HREF="shergold.html">Please
send
IMG SRC="scooby.jpg" ALT="Good doggy! "xP>
P.S. I am deathly ill. A HREF="shergold.html">Please send
IMG SRC="scooby.jpg" ALT="Good doggy!">
BLINK>He's my hero! /BLINK> I would like to meet him some day,
P.S. I am deathly ill. a href="cards.html">Please send
|