← На главную

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

Пришло время для очередного выпуска мини-заметок. Вас ждут парочка полезных фильтров для 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 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 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