Мини заметки — выпуск 4

1 сентября 2011

Пришло время для очередного выпуска мини-заметок. Вас ждут парочка полезных фильтров для WordPress, несколько Perl-скрипов и другие приятные мелочи. Предыдущие выпуски мини-заметок можно найти здесь (#3) и там (#2).

1. Работа с UTF-8 в Perl

Perl хранит строки в виде массивов целых чисел. Чтобы работать с данными в кодировке UTF-8, сначала нужно преобразовать их во внутреннее представление интерпретатора. Делается это очень просто:

use strict;
use utf8;

my $str;
# ... $str := данные в кодировке utf8 ...

# декодируем строку из utf8 во внутреннее представление
utf8::decode($str);

# теперь $str можно обрабатывать

Прежде, чем вывести данные в консоль или файл, их нужно преобразовать из внутреннего представления обратно в кодировку UTF-8:

# кодируем строку в utf8
utf8::encode($str);

# выводим куда-нибудь

Если все время кодировать-декодировать данные не хочется, можно возложить эту работу на интерпретатор:

#!/usr/bin/perl

use strict;
use utf8;

binmode STDIN, ":utf8";
binmode STDOUT, ":utf8";

print "Ваше имя: ";
chomp(my $name = <>);
print "Привет, $name!\n";

Более короткий вариант:

#!/usr/bin/perl

use strict;
use utf8;
use open qw/:std :utf8/;

print "Ваше имя: ";
chomp(my $name = <>);
print "Привет, $name!\n";

По-моему, все просто и логично. Соответствующая инфа прекрасно гуглится (я же ее как-то нашел в свое время). Однако почему-то этот вопрос довольно часто всплывает на форумах и в IRC.

Следует учесть, что описанный выше рецепт позволяет «нормально» работать даже с кодами, еще не используемыми в Unicode. В некоторых случаях (например, если вы пишите свой веб-фреймворк) вам может потребоваться более строгая работа с UTF-8. Подробности можно узнать в статье Know the difference between utf8 and UTF-8.

Подробнее о том, как Perl работает с различными кодировками, можно прочитать в этой статье.

2. Скрипт для скачивания видео с Vimeo.com

Как выяснилось, Vimeo легко ковыряется с помощью Firebug.

#!/usr/bin/env perl

# vimeo.com downloader v 0.1
# (c) Alexandr A Alexeev 2011 | http://eax.me/

use strict;

my $url = shift;
my $vid;
unless(($vid) = $url =~ m#^http://(?:www\.)?vimeo\.com/(\d+)$#i) {
  die "Usage: $0 http://vimeo.com/1234567\n";
}

my $data = `wget '$url' -O -`;
if($?) {
  die "Download failed: wget returns $?";
}

my $regex = qr#"signature":"([0-9a-f]{32})","timestamp":([0-9]+),[^\}]+\},"video":\{"id":(\d+)#i;
my ($sign, $time, $clip_id);
unless(($sign, $time, $clip_id) = $data =~ $regex) {
  die "Failed to parse html code";
}

my $url =
  "http://player.vimeo.com/play_redirect".
  "?clip_id=$clip_id&sig=$sign&time=$time".
  "&quality=hd,sd,mobile&codecs=H264,VP8,VP6";
`wget '$url' -O $vid.mp4`;
if($?) {
  print "Download failed: wget returns $?";
} else {
  print "Video saved to $vid.mp4\n";
}

Также в этом блоге вы можете найти аналогичные скрипты для YouTube и RuTube.

Дополнение: См также CPAN-модули WWW::YouTube::Download и WWW::Vimeo::Download.

3. Скрипт для скачивания файлов с DepositFiles

Загружать на DepositFiles мы уже умеем, а вот доунлоадер еще не писали:

#!/usr/bin/env perl

# quick-deposit.pl script v 0.1
# (c) Alexandr A Alexeev 2011 | http://eax.me/

use strict;
use File::Temp;

# проверяем наличие всех необходимых утилит
{
   my @depends = qw/wget/;
   my $not_found;
   for(@depends) {
     print "ERROR: $_ not found" and ++$not_found
       if(system("which $_ > /dev/null"));
   }
   exit 1 if($not_found);
}

my $url = shift;
die "Usage: $0 <url>\n"
  unless($url && $url !~ /'/);

# валидация url
if($url !~ m#depositfiles\.com/(.*)$#i) {
  die "Step 0: invalid url!\n";
}
$url = $1;
if($url !~ m#^[a-z]{2}/#i) {
  $url = "ru/$url"; # обязательно нужен двубуквенный префикс!
}
$url = "http://depositfiles.com/$url";


my $cookies = tmpnam();
my $data;

STEP1:
  $data = `wget -q '$url' --post-data='gateway_result=1' --save-cookies=$cookies -O -`;
  die "Step 1: wget returns $?\n" if($?);

  if($data =~ m#<span class="html_download_api-limit_parallel">#i) {
    print "Parallel limit, sleeping 2 min...\n";
    sleep 120;
    goto STEP1;
  }

  if($data =~ m#<span class="html_download_api-limit_interval">(\d+)</span>#i) {
    my $sleep = $1;
    print "limit_interval = $sleep (~ ".int($sleep/60)." min)\n";
    sleep 60; # $sleep;
    goto STEP1;
  }

my $get_file_url;
if($data !~ m#(get_file\.php\?fid=[0-9a-f]+)#i) {
  die "Step 1: get_file url not found!\n";
}
$get_file_url = "http://depositfiles.com/$1";

print "Sleeping 60 seconds...\n";
sleep 61;

STEP2:
  $data = `wget -q '$get_file_url' --load-cookies=$cookies -O -`;
  die "Step 2: wget returns $?\n" if($?);

# тут _теоретически_ может возникнуть ситуация, когда кто-то нас опередил
# но на практике такое не происходит

my $download_url;
if($data !~ m#<form action="(http://fileshare[^"]+)"#i) {
  if($data =~ m#Please wait \d{1,2} sec#i) {
    # Please wait 60 sec or use GOLD account to download with no time limits
    print "Sleeping 5 seconds...\n";
    sleep 5;
    goto STEP2;
  }
  die "Step 2: failed to get download url!\n";
}
$download_url = $1;

die "Invalid download url: $download_url\n"
  if($download_url =~ /'/);
print "Downloading $download_url\n";

system("wget '$download_url' --load-cookies=$cookies");

Просто скармливаем скрипту ссылку и занимаемся дальше своими делами. Он сам подождет 60 или сколько-там-надо секунд, после чего бережно скачает требуемый файл на диск.

4. Запрещаем комментаторам блога оставлять ссылки

Чтобы меньше беспокоиться по поводу ручного спама в ваших WordPress-блогах, пропишите следующий код в functions.php:

// удаляем все ссылки в тексте комментариев (даже в RSS)
function fltr_get_comment_text($str) {
  $str = preg_replace(
    '/<a href=["\']{1}([^"\']+)["\']{1}[^>]*>([^<]+)<\/a>/i',
    '$2 [$1]',
    $str
  );
  $str = str_replace('tp://', '&#116;p://', $str);
  return $str;
}

add_filter('get_comment_text', 'fltr_get_comment_text');

Теперь все ссылки в комментариях будут заменяться на последовательность «текст_ссылки [url_простым_текстом]». Таким образом мы избавимся от всех гиперссылок в комментах. Пусть комментаторы ссылаются, на что хотят :)

5. Скрытие ссылок средствами JavaScript

А вот такой прием можно использовать в агрегаторах новостей:

function js_urlencode($str) {
    $str = mb_convert_encoding($str, 'UTF-16', 'UTF-8');
    $out = '';
    for($i = 0; $i < mb_strlen($str, 'UTF-16'); $i++)
        $out .= '%u'.bin2hex(mb_substr($str, $i, 1, 'UTF-16'));
    return $out;
}

function hide_link($matches) {
    $url = $matches[1];
    $str = $matches[2];
    $id = md5("SECRET123456".$url);
    return
      "<span id='$id'>".(($str === $url) ? $url : "$str [$url]").
      "</span><script type='text/javascript'><!--\n".
      'document.getElementById("'.$id.'").innerHTML = '.
      'unescape("'.js_urlencode('<a href="'.$url.'">'.$str.'</a>').
      "\");\n//--></script>";
}

// вывести архив с зашифрованными ссылками
function js_the_content($str) {
  $str = preg_replace_callback(
    '#<a href="([^"]+)">([^<]+)</a>#i',
    "hide_link",
    $str
  );
  return $str;
}

add_filter('the_content', 'js_the_content');

Теперь поисковые системы не увидят ни одной ссылки, а реальные пользователи (с включенным JavaScript) не заметят никакой разницы.

6. Определение оператора вывода для контейнеров в C++

Этот пример я просто стырил с Винграда:

#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <list>
#include <deque>
template<
  typename T, typename A,
  template <class, class> class C
> std::ostream& operator<< (std::ostream& os, const C<T, A>& o)
{
  std::copy(
    o.begin(), o.end(),
    std::ostream_iterator<T>(std::cout, " ")
  );
  return os << std::flush;
}
int main() {
    std::vector<std::string> myVector;
    std::list<std::string> myList;
    std::deque<std::string> myDeque;

    myVector.push_back("aaa");
    myVector.push_back("bbb");

    std::cout << myVector;
    std::cout << myList;
    std::cout << myDeque;
}

Авось когда-нибудь пригодится.

7. Как доказать, что у пользователя отключены кукисы

К сожалению, иногда приходится заниматься такой ерундой:

<script language="javascript"><!--
 var d = new Date();
 var ref = escape(document.referrer);
 document.write(
   '<a href="<%= url_for '/' %>">' +
   '<img src="<%= url_for '/logo.png' %>' +
   // **** DEBUG ****
     '?' + d.getTime() +
     ';' + escape(document.cookie) +
     ';' + ref.replace(/\//g, '_') +
   // **************
   '" style="border: 0;" /></a>'
 );
//--></script>

Допустим, пользователь не может залогиниться на сайт, а симптомы напоминают отключенные кукисы. Чтобы не переться в бухгалтерию и не рыться в настройках браузера (нарушив тем самым должностную инструкцию), добавляем в шаблон приведенный выше код (предполагается, что сайт работает на Mojolicious) и просим пользователя попробовать еще разок. Затем открываем /var/log/httpd-access.log и тыкаем пальчиком.

8. Как удалить скрипт, поставленный с помощью setup.py

Говорим:

python setup.py install --record flist.txt

Внимательно просматриваем список файлов, после чего удаляем их:

cat flist.txt | xargs rm -rf

Так-то!

9. Список модулей, используемых проектом на Perl

С помощью такого однострочника можно получить список модулей, используемых в проекте:

find ./ | grep -iE '\.(pl|pm)$' | xargs cat | \
  perl -lne 'if(/^use(?: base)? ([^ ;]+)/){print $1;}' | \
  grep -v strict | sort -u

Список, к примеру, можно скопировать в README.TXT. Для упрощения деплоймента проектов на Perl Ярослав Коршак посоветовал мне использовать Module::Starter. К сожалению, на момент написания этих строк, руки до указанного модуля у меня не дошли.

Подробнее про однострочники на Perl можно прочитать на Хабре.

10. Как получить основной текст веб-страницы

Чтобы отделить основной текст страницы от заголовка, подвала и меню, можно использовать примерно такую последовательность шагов:

wget http://eax.me/ -O index.html
wget http://eax.me/perl5-oop/ -O page.html
diff index.html page.html | perl -lne 'if(/^>(.*)$/){print $1;}'

Дальше можно избавиться от лишнего HTML-кода (каких-нибудь div и span), отыскать самую длинную измененную часть страницы и тп. Подозреваю, что поисковые системы используют похожий метод.

Как всегда, любые замечания и дополнения приветствуются. Да пребудет с вами Великая Сила!

Дополнение: Мини заметки — выпуск 5

Метки: .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.