Как опубликовать нелатинские данные на сайт не в формате UTF8, используя perl? - PullRequest
2 голосов
/ 03 июня 2010

Я хочу опубликовать русский текст на сайте CP1251 с помощью LWP :: UserAgent и получить следующие результаты:

# $text="Русский текст"; obtained from command line
FIELD_NAME => $text                                # result: Г?в г'В?г'В?г'В?г?вєг?вёг?в? Г'В'Г?вчг?вєг'В?г'В'
$text=Encode::decode_utf8($text);
FIELD_NAME => $text                                # result: Р с?с?с?рєрёр? С'Рчрєс?с'
FIELD_NAME => Encode::encode("cp1251", $text)     # result: Г?гіг+г+гЄгёгЏ ГІгҐгЄг+гІ
FIELD_NAME => URI::Escape::uri_escape_utf8($text) # result: D0%a0%d1%83%d1%81%d1%81%d0%ba%d0%b8%d0%b9%20%d1%82%d0%b5%d0%ba%d1%81%d1%82

Как я могу это сделать? Тип содержимого должен быть x-www-form-urlencoded. Вы можете найти похожую форму здесь , но там вы можете просто экранировать любой нелатинский символ, используя & # ...; форма, попытка экранировать ее в FIELD_NAME приводит к 10561091108910891 10901077108210891 (каждые &, # и ; извлекаются из строки) или 1056;усский текст (знаки препинания в начале строки удаляются ) в зависимости от того, что на самом деле FIELD_NAME.

ОБНОВЛЕНИЕ : Кто-нибудь знает, как преобразовать следующий код, чтобы он использовал функцию LWP :: UserAgent :: post?

my $url=shift;
my $fields=shift;
my $request=HTTP::Request->new(POST => absURL($url));
$request->content_type('application/x-www-form-urlencoded');
$request->content_encoding("UTF-8");
$ua->prepare_request($request);
my $content="";
for my $k (keys %$fields) {
    $content.="&" if($content ne "");
    my $c=$fields->{$k};
    eval {$c=Encode::decode_utf8($c)};
    $c=Encode::encode("cp1251", $c, Encode::FB_HTMLCREF);
    $content.="$k=".URI::Escape::uri_escape($c);
}
$request->content($content);
my $response=$ua->simple_request($request);

Этот код фактически решает проблему, но я не хочу добавлять третью функцию-оболочку запроса (наряду с get и post).

Ответы [ 3 ]

0 голосов
/ 04 июня 2010

Используйте WWW::Mechanize, он автоматически заботится о кодировании (как кодировке символов, так и кодировке форм) и делает все правильно, если атрибут accept-charset элемента form установлен соответствующим образом.Если он отсутствует, то по умолчанию используется значение UTF-8, и поэтому его необходимо исправить.Вы, кажется, находитесь в этой ситуации.Кстати, код вашего сайта-примера KOI8-R, а не Windows-1251.Рабочий пример:

use utf8;
use WWW::Mechanize qw();
my $message = 'Русский текст';
my $mech = WWW::Mechanize->new(
    cookie_jar => {},
    agent => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.9 SUSE/6.0.401.0-2.1 (KHTML, like Gecko)',
);
$mech->get('http://zhurnal.lib.ru/cgi-bin/comment?COMMENT=/z/zyx/index_4-1');
$mech->current_form->accept_charset(scalar $mech->response->content_type_charset);
$mech->submit_form(with_fields => { TEXT => $message });

Дамп HTTP (только основные части):

POST /cgi-bin/comment HTTP/1.1
Content-Length: 115
Content-Type: application/x-www-form-urlencoded

FILE=%2Fz%2Fzyx%2Findex_4-1&MSGID=&OPERATION=store_new&NAME=&EMAIL=&URL=&TEXT=%F2%D5%D3%D3%CB%C9%CA+%D4%C5%CB%D3%D
0 голосов
/ 09 июня 2010

Эти функции решают проблему (сначала для публикации данных application / x-www-form-urlencoded, а затем для multipart / form-data):

#{{{2 postue
sub postue($$;$) {
    my $url=shift;
    my $fields=shift;
    my $referer=shift;
    if(defined $referer and $referer eq "" and defined $fields->{"DIR"}) {
        $referer=absURL($url."?DIR=".$fields->{"DIR"}); }
    else {
        $referer=absURL($referer); }
    my $request=HTTP::Request->new(POST => absURL($url));
    $request->content_type('application/x-www-form-urlencoded');
    $request->content_encoding("UTF-8");
    $ua->prepare_request($request);
    my $content="";
    for my $k (keys %$fields) {
        $content.="&" if($content ne "");
        my $c=$fields->{$k};
        if(not ref $c) {
            $c=Encode::decode_utf8($c) unless Encode::is_utf8($c);
            $c=Encode::encode("cp1251", $c, Encode::FB_HTMLCREF);
            $c=URI::Escape::uri_escape($c);
        }
        elsif(ref $c eq "URI::URL") {
            $c=$c->canonical();
            $c=URI::Escape::uri_escape($c);
        }
        $content.="$k=$c";
    }
    $request->content($content);
    $request->referer($referer) if(defined $referer);
    my $i=0;
    print STDERR "Doing POST request to url $url".
        (($::o_verbose>2)?(" with fields:\n".
                ::YAML::dump($fields)):("\n"))
        if($::o_verbose>1);
  REQUEST:
    my $response=$ua->simple_request($request);
    $i++;
    my $code=$response->code;
    if($i<=$o_maxtries and 500<=$code and $code<600) {
        print STDERR "Failed to request $url with code $code... retrying\n"
            if($::o_verbose>2);
        sleep $o_retryafter;
        goto REQUEST;
    }
    return $response;
}
#{{{2 postfd
sub postfd($$;$) {
    my $url=absURL(shift);
    my $content=shift;
    my $referer=shift;
    $referer=absURL($referer) if(defined $referer);
    my $i=0;
    print STDERR "Doing POST request (form-data) to url $url".
        (($::o_verbose>2)?(" with fields:\n".
                ::YAML::dump($content)):("\n"))
        if($::o_verbose>1);
    my $newcontent=[];
    while(my ($f, $c)=splice @$content, 0, 2) {
        if(not ref $c) {
            $c=Encode::decode_utf8($c) unless Encode::is_utf8($c);
            $c=Encode::encode("cp1251", $c, Encode::FB_HTMLCREF);
        }
        push @$newcontent, $f, $c;
    }
  POST:
    my $response=$ua->post($url, $newcontent,
                           Content_type => "form-data",
                           ((defined $referer)?(referer => $referer):()));
    $i++;
    my $code=$response->code;
    if($i<=$o_maxtries and 500<=$code and $code<600) {
        print STDERR "Failed to download $url with code $code... retrying\n"
            if($::o_verbose>2);
        sleep $o_retryafter;
        goto POST;
    }
    return $response;
}
0 голосов
/ 03 июня 2010

Обходным путём кажется (далеко не лучшим, я думаю) использовать системную команду перекодирования, если она у вас есть. От http://const.deribin.com/files/SignChanger.pl.txt

my $boardEncoding="cp1251"; # encoding used by the board
$vals{'Post'} = `fortune $forunePath | recode utf8..$boardEncoding`;
$res = $ua->post($formURL,\%vals);

Другой подход, кажется, в http://mail2lj.nichego.net/lj.txt

my        $formdata = $1 ;
my        $hr = ljcomment_string2form($formdata) ;
my        $req = new HTTP::Request('POST' => $ljcomment_action)
        or die "new HTTP::Request(): $!\n" ;

$hr->{usertype} = 'user' ;
$hr->{encoding} = $mh->mime_attr('content-type.charset') ||
                  "cp1251" ;
$hr->{subject}  = decode_mimewords($mh->get('Subject'));
$hr->{body} = $me->bodyhandle->as_string() ;

$req->content_type('application/x-www-form-urlencoded');
$req->content(href2string($hr)) ;

my      $ljres = submit_request($req, "comment") ;

if ($ljres->{'success'} eq "OK") {
    print STDERR "journal updated successfully\n" ;
} else {
    print STDERR "error updating journal: $ljres->{errmsg}\n" ;
    send_bounce($ljres->{errmsg}, $me, $mh->mime_attr("content-type.charset")) ;
}
...