В главе 19 "Программирование CGI" основное внимание уделяется ответам на запросы броузеров и генерации документов с применением CGI. В этой главе программирование для Web рассматривается с другой стороны: вместо того чтобы общаться с броузером, вы сами пр
Упоминаемые модули находятся по следующему URL:
http://www.perl.com/CPAN/modules/by'-categoiy/1'5_World_Wide_Web_HTML_ HTTP_CGI/
Здесь хранятся модули для вычисления контрольных сумм кредитных карт, взаимодействия с API Netscape или сервера АрасПе, обработки графических карт (image maps), проверки HTML и работы с MIME. Однако самые большие и важные модули этой главы входят в компле
Модули HTTP:: и LWP:: позволяют запрашивать документы с сервера. В частности, модуль LWP::Simple обеспечивает простейший способ получения документов. Однако LWP::Simple не хватает возможности обращаться к отдельным компонентам ответов HTTP. Для работы
Имя модуля назначение
LWP::UserAgent Класс пользовательских агентов WWW
LWP::RobotUA Разработка приложений-роботов
LWP::Protocol Интерфейс для различных схем протоколов
LWP::Authen::Basic Обработка ответов 401 и 407
LWP::MediaTypes Конфигурация типов MIME (text/html и т. д.)
LWP::Debug Отладочный модуль
LWP::Simplc Простой процедурный интерфейс для часто используемых функций
LWP::UserAgent Отправка HTTP::Request и возвращение HTTP::Response
HTTP::Hcaders Заголовки стилей MIME/RFC822
HTTP::Message Сообщение в стиле HTTP
HTTP::Request Запрос HTTP
HTTP::Response Ответ HTTP
HTTP::Daemon Класс сервера HTTP
HTTP::Status Коды статуса HTTP (200 OK и т. д.)
HTTP::Date Модуль обработки даты для форматов дат HTTP
HTTP::Ncgotiate Обсуждение содержимого HTTP
URI::URL URL
WWW::RobotRulcs Анализ файлов robots.txt
File::Listing Анализ списков содержимого каталогов
Модули HTML:: находятся в близкой связи с LWP, но не распространяются в составе этого пакета. Они предназначены для анализа HTML-кода. На них основаны рецепты 20.3-20.7, программы htmlsub и hrefsub.
В рецепте 20.12 приведено регулярное выражение для декодирования полей в файлах журналов Web-сервера, а также показано, как интерпретировать эти поля. Мы используем это регулярное выражение с модулем Logfile::Apache в рецепте 20.13, чтобы продемонстри
Требуется обратиться из сценария по некоторому URL.
Решение
Воспользуйтесь функцией get модуля LWP::Simple от СРАМ, входящего в LWP.
use LWP::Simple;
$content =? get($URL);
Комментарий
Правильный выбор библиотек заметно упрощает работу. Модули LWP идеально подходят для поставленной задачи.
Функция get модуля LWP::Simple в случае ошибки возвращает undef, поэтому ошибки следует проверять так:
use LWP::Simple;
unless (defined ($content = get $URL)) { die "could not get $URL\n";
}
Однако в этом случае вы не сможете определить причину ошибки. В этой и других нетривиальных ситуациях возможностей LWP::Simple оказывается недостаточно.
В примере 20.1 приведена программа выборки документа по URL. Если попытка оказывается неудачной, программа выводит строку с кодом ошибки. В противном случае печатается название документа и количество строк в его содержимом. Мы используем четыре модуля от
LWP::UserAgent
Модуль создает виртуальный броузер. Объект, полученный при вызове конструктора new, используется для дальнейших запросов. Мы задаем для своего агента имя "Schmozilla/v9.14 Platinum", чтобы Web-мастер мучился от зависти при просмотре журнала.
HTTP:: Request
Модуль создает запрос, но не отправляет его. Мы создаем запрос GET и присваиваем фиктивный URL для запрашивающей страницы.
HTTP:: Response
Тип объекта, возвращаемый при фактическом выполнении запроса пользовательским объектом. Проверяется на предмет ошибок и для получения искомого содержимого.
URI::Heuristic
Занятный маленький модуль использует Netscape-подобные алгоритмы для расширения частичных URL. Например:
Частичный URL Предположение perl http://www.pcrl.coiu www.oreilly.com http://www.orcilly.com ftp.funet.fi ftp://ftp.funet.fi /etc/passwd filc:/etc/passwd
Хотя строки в левом столбце не являются правильными URL (их формат не отвечает спецификации URI), Netscape пытается угадать, каким URL они соответствуют. То же самое делают и многие другие броузеры.
Исходный текст программы приведен в примере 20.1.
#!/usr/bin/perl -w
# titlebytes - определение названия и размера документа
use LWP::UserAgent;
use HTTP::Request;
use HTTP: -.Response;
use URI::Heuristic;
my $raw_url = shift or die "usage: $0 url\n";
my $url = URI::Heuristic::uf_urlstr($raw_url);
$1=1; # Немедленный вывод следующей строки
printf "%s =>\n\t", $url;
my $ua = LWP::UserAgent->new();
$ua->agent("Schmozilla/v9.14 Platinum");
my $req = HTTP::Request->new(GET => $url);
$req->referer("http://wizard.yellowbrick.oz");
# Чтобы озадачить программы анализа журнала
my $response = $ua->request($req);
if ($response->is_error()) {
printf " %s\n", $response->status_line;
} else {
my $count;
my $bytes;
my $content = $response->content();
$bytes = length Scontent;
$count = ($content =~ tr/\n/\n/):
printf "%s (%d lines, %d bytes)\n", $response->title(), $count, $bytes; }
Программа выдает результаты следующего вида:
% titlebytes http://www.tpj.com/ http://www.tpj.com/ =>
The Perl Journal (109 lines, 4530 bytes) Обратите внимание: вместо правильного английского refer rer используется вариант написания ref ere г. Ошибка была допущена разработчиками стандарта при выборе имени HTTP_REFERER.
> Смотри также --------------------------------
Документация по модулю LWP::Simple с CPAN и страница руководства Iwpcook(i), прилагаемая к LWP; документация по модулям LWP::UserAgent, HTTP::Request, HTTP::Response и URI::Heuristic; рецепт 20.2.
Вы хотите передать сценарию CGI значения полей формы из своей программы.
Решение
Если значения передаются методом GET, создайте URL и закодируйте форму методом query_form:
use LWP::Simple;
use URI::uRL;
my $url = url('http://www,perl.com/cgi-bin/cpan_mod');
$url->query_form(module => 'DB_File', readme => 1);
$content = get($url);
Если вы используете метод POST, создайте собственного пользовательского агента и закодируйте содержимое:
use HTTP::Request::Common qw(POST);
use LWP::UserAgent;
$ua = LWP::UserAgent->new();
my $req = POST 'http://www.perl.com/cgi-bin/cpan_mod',
[ module => "DB_File", readme => 1 ];
$content = $ua->request($req)->as_stnng:
Комментарий
Для простых операций хватает процедурного интерфейса модуля LWP::Simple. Для менее тривиальных ситуаций модуль LWP::UserAgent предоставляет объект виртуального броузера, работа с которым осуществляется посредством вызова методов.
Строка запроса имеет следующий формат:
&ПОЛЕ1=ЗНАЧЕНИЕ1
&ПОЛЕ2=ЗНАЧЕНИЕ2
&ПОЛЕЗ=ЗНАЧЕНИЕЗ
В запросах GET информация кодируется в запрашиваемом URL:
http://www.site.com/path/to/ script.cgi?field1=value1&field2=value2&field3=value3
Служебные символы в полях должны быть соответствующим образом преобразованы, поэтому присваивание параметру а гд строки "this isn't and " выглядит так:
http://www.site.com/path/to/ script.cgi?arg=%22this+isn%27t+%3CEASY%3E+%26+%3CFUN%3E%22
Метод query_form, вызываемый для объекта URL, оформляет служебные символы формы за вас. Кроме того, можно вызвать URI: : Escape: :uri_escape или CGI:escape_html по собственной инициативе. В запросах POST строка параметров входит в тело HTML-документа, пер
Для передачи данных в запросе GET можно использовать модуль LWP::Simk', однако для запросов POST не существует аналогичного интерфейса LWP::Simple. Вместо этого функция POST модуля HTTP::Request::Common создает правильно отформатированный запрос с офо
Если запрос должен проходить через прокси-сервер, сконструируйте своего пользовательского агента и прикажите ему использовать прокси:
$ua->proxy(['http', 'ftp'] => 'http://proxy.myorg.com:8081');
Это означает, что запросы HTTP и FTP для данного пользовательского агента должны маршрутизироваться через прокси на порте 8081 по адресу proxy. myorg.com.
> Смотри также -------------------------------
Документация по модулям LWP::Simple, LWP::UserAgent, HTTP::Request::Com-mon, URI::Escape и URI::URL с CPAN; рецепт 20.1.
Воспользуйтесь модулем HTML::LinkExtor из LWP:
use HTML::LinkExtor;
$parser = HTML::LinkExtor->new(undef, $base_url);
$parser->parse_file($filename);
@links = $parser->links;
foreach $linkarray (@links) {
my @element = @$linkarray;
my $elt_type = shift @element; # Тип элемента
# Проверить, тот ли это элемент, который нас интересует
while (@element) {
# Извлечь следующий атрибут и его значение
my ($attr_name, $attr_value) = splice(@element, 0, 2);
# ... Сделать что-то ...
}
}
Воспользуйтесь простым кодирующим фильтром из примера 20.3.
Пример 20.3. text2html
#!/usr/bin/pecl -w -p00
# text2html - простейшее html-кодирование обычного текста
# -p означает, что сценарий применяется для каждой записи.
# -00 означает, что запись представляет собой абзац
use HTML: '.Entities;
$_ = encode_entities($_, "\200-\377");
if (/"W) {
# Абзацы, начинающиеся с пропусков, заключаются в PRE> s{(.*)$} {PRE>\n$1/PRE>\n}s; # Оставить отступы
} else {
s{"(>.*)} {$1BR>}gm; # quoted text
s{} {A HREF="$1">$K/A>}gs # Внутренние URL(xopoшo)
s{(http:\S+)} {A HREF="$1">$K/A>}gs; # Предполагаемые URL(nлoxo)
s{\*(\S+)\*} {STRONG>$K/STRONG>}g; # *Полужирный*
s{\b_(\S+)\_\b} {EM>$K/EM>}g; # Курсив.
s{^} {P>\n}; # Добавить тег абзаца
}
Комментарий
Задача преобразования произвольного текста в формат HTML не имеет общего решения, поскольку существует много разных, конфликтующих друг с другом способов форматирования обычного текста. Чем больше вам известно о входных данных, тем лучше вы их отформатиру
Например, если вы знаете, что исходным текстом будет почтовое сообщение, можно добавить следующий блок для форматирования почтовых заголовков:
BEGIN {
print "TABLE>";
$_ = encode_entities(scalar о);
s/\n\s+/ /g; # Строки продолжения
while ( /"(\S+?:)\s*(.*)$/gm ) { # Анализ заголовков
print "
$K/TH>
$2
\n";
}
print "/TABLE>HR>";
}
> Смотри также -------------------------------
Документация по модулю HTML::Entities от CPAN.
Требуется преобразовать HTML-файл в отформатированный ASCII-текст.
Решение
Если у вас есть внешняя программа форматирования (например, lynx), воспользуйтесь ей:
$ascii = 'lynx -dump $filename'; Если вы хотите сделать все в своей программе и не беспокоитесь о том, что HTML::TreeBuilder еще не умеет обрабатывать таблицы и фреймы:
use HTML::FormatText;
use HTML::Parse;
$html = parse_htmlfile($filename);
Sformatter = HTML: :FormatText->new(leftrnargin => 0, rightmargin => 50);
$ascii = $1:ormatter->format($html);
Комментарий
В обоих примерах предполагается, что HTML-текст находится в файле. Если он хранится в переменной, то для применения lynx необходимо записать его в файл. При работе с HTML::FormatText воспользуйтесь модулем HTML::TreeBuilder:
use HTML::TreeBuilder;
use HTML::FormatText;
$html = HTML::TreeBuilder->new();
$html->parse($document);
$formatter = HTML::FormatText->new(leftmargin => 0, rightmargin o=> 50);
$ascii = $formatter->format($html); Если вы используете Netscape, команда Save As с типом Text отлично справляется с таблицами.
> Смотри также -------------------------------
Документация по модулям HTML::Parse, HTML::TreeBuilder и HTML::Format-Text; man-страница lynx{1) вашей системы; рецепт 20.6.
Требуется удалить из строки теги HTML и оставить в ней обычный текст.
Решение
Следующее решение встречается часто, но работает неверно (за исключением простейшего HTML-кода):
($plain_text = $html_text) ^~ s/<[~>]*>//gs; #НЕВЕРНО
Правильный, но медленный и более сложный способ связан с применением модуля LWP:
use HTML::Parse;
use HTML::FormatText;
$plain_text = HTML::FormatText->new->format(parse_html($html_text));
Комментарий
Как всегда, поставленную задачу можно решить несколькими способами. Каждое решение пытается соблюдать баланс между скоростью и универсальностью. Для простейшего HTML-кода работает даже самая элементарная командная строка:
% perl -pe "s/<[">]*>//g" ФАЙЛ Однако это решение не подходит для файлов, в которых теги пересекают границы строк:
IMG SRC = "foo.gif" ALT = "Flurp!">
Поэтому иногда встречается следующее решение:
% perl -0777 -ре "s/<[">]*>//gs" ФАЙЛ или его сценарный эквивалент:
{
local $/; # Временный режим чтения всего файла
$html = ;
$html =~ s/<[">]*>//gs:
} Но даже этот вариант работает лишь для самого примитивного HTML-кода, не содержащего никаких "изюминок". В частности, он пасует перед следующими примерами допустимого HTML-кода (не говоря о многих других):
IMG SRC = "foo.gif" ALT = "А > В"> -> script if (a< b && a=c) /script> <# Просто данные #>
Проблемы возникают и в том случае, если комментарии HTML содержат другие теги:
В>Меня не видно!В> ->
Единственное надежное решение - использовать алгоритмы анализа HTML-кода из LWP. Эта методика продемонстрирована во втором фрагменте, приведенном в решении.
Чтобы сделать анализ более гибким, субклассируйте HTML::Parser от LWP и записывайте только найденные текстовые элементы:
package MyParser;
use HTML::Parser;
use HTML::Entities qw(decode_entities);
@ISA = qw(HTML::Parser):
sub text {
my($self, $text) = @_;
print decode_entities($text);
}
package main;
MyParser->new->parse_file(*F); Если вас интересуют лишь простые теги, не содержащие вложенных тегов, возможно, вам подойдет другое решение. Следующий пример извлекает название несложного HTML-документа:
($title) = ($html =~ m#\s*(.*?)\s*#is); Как говорилось выше, подход с регулярными выражениями имеет свои недостатки. В примере 20.4 показано более полное решение, в котором HTML-код обрабатывается с использованием LWP.
Пример 20.4. htitle
#!/usr/bin/perl
# htitle - Получить название HTML-документа для URL
die "usage: $0 ucl ...\n" unless @
require LWP;
foreach $url (@ARGV) {
$ua = LWP::UserAgent->new();
$res = $ua->request(HTTP::Request->new(GET => $url));
print "$url: " if OARGV > 1;
if ($res->is_success) {
print $res->title, "\n";
} else {
print $res->status_line, "\n";
}
} Приведем пример вывода:
% htitle http://www.ora.com www.oreilly.com - Welcome to O'Reilly & Associates!
% htitle http://www.perl.com/ http://www.perl.com/nullvoid http://www.perl.com/: The www.perl.com Home Page http://www.perl.com/nullvoid: 404 File Not Found
[> Смотри также -------------------------------
Документация по модулям HTML::TreeBuilder, HTML::Parser, HTML::Entities и LWP::UserAgent с CPAN; рецепт 20.5.
Требуется узнать, содержит ли документ устаревшие ссылки.
Решение
Воспользуйтесь методикой, описанной в рецепте 20.3, для получения всех ссылок я проверьте их существование функцией head модуля LWP::Simple.
Комментарий
Следующая программа является прикладным примером методики извлечения ссылок из HTML-документа. На этот раз мы не ограничиваемся простым выводом ссылок и вызываем для нее функцию head модуля LWP::Simple. Метод HEAD получает метаданные удаленного документа
Поскольку программа использует функцию get из LWP::Simple, она должна получать URL, а не имя файла. Если вы хотите поддерживать обе возможности, воспользуйтесь модулем URI::Heuristic (см. рецепт 20.1).
Пример 20.5. churl
#!/usr/bin/perl -w
# churl - проверка URL
use HTML::LinkExtor;
use LWP::Simple qw(get head);
$base_url = shift
or die "usage: $0 \n";
Sparser = HTML::LinkExtor->new(undef, $base_url);
$parser->parse(get($base_url));
@links = $parser->links;
print "$base_url: \n";
foreach $linkarray (@>links) { my @element = @$linkarray;
my $elt_type = shift @element;
while (@element) {
my ($attr_name , $attr_value) = splice(@element, 0, 2);
if ($attr_value->scheme =~ /\b(ftp|https?|file)\b/) {
print " $attr_value: ", head($attr_value)? "OK" : "BAD", "\n";
}
} }
Для программы действуют те же ограничения, что и для программы, использующей HTML::LinkExtor, из рецепта 20.3.
> Смотри также -------------------------------
Документация по модулям HTML::LinkExtor, LWP::Simple, LWP::UserAgent и HTTP::Response с CPAN; рецепт 20.8.