Очередная поделка на Erlang: парсим выдачу Google
5 декабря 2012
В свободное время я продолжаю (см один, два и три) писать небольшие поделки на Erlang. Это занятие видится мне прекрасным способом изучения языка. Почти наверняка в этой заметке будет изрядное количество ляпов, ну да я надеюсь, что более опытные коллеги меня поправят.
Для установки сторонних библиотек в Perl используется утилита cpan, в Python — pip, в Haskell — cabal. В мире Erlang есть нечто подобное под названием Agner, но обычно все же используется rebar. В нем применяется другой подход, не такой, как в cpan или cabal. Идея заключается в том, чтобы указать ссылки на git-репозитории требуемых библиотек (кстати, не обязательно git) и перед сборкой приложения подтянуть исходники этих библиотек в каталог с кодом приложения.
Соответственно, поиск библиотек осуществляется с помощью поиска GitHub’а или какого-то другого хостинга проектов. Большинство, если не абсолютно все, Erlang-библиотеки хостятся именно на GitHub’е. По моему опыту Google ищет по GitHub’у лучше самого GitHub’а. Таким образом, библиотеки можно искать прямо в гугле по запросу site:github.com erlang ключевые слова
.
В общем, все это напоминает использование Maven из мира Java или, например, связку perlbrew, cpanm и Makefile.PL из мира Perl. Для меня этот подход не очень привычен, но отчего бы ему не иметь права на жизнь? Как минимум, Erlang-библиотеки хранятся как бы очень распределенно, а распределенность — это круто! Вон у программистов на C/C++ даже такого инструмента, кажется, нет, и ничего, живут ведь как-то. Может, менеджеры пакетов самой ОС используют?
Чтобы получить rebar, нужно собрать его из исходников:
cd rebar
make
Полученный исполняемый файл rebar можно положить в ~/bin, прописав этот путь в $PATH. Часто rebar кладут прямо в репозиторий с кодом проекта и создают Makefile-обертку вокруг него, чтобы проект можно было собрать и установить традиционным make && make install
.
Попробуем воспользоваться rebar на практике. Создадим новый Git-репозиторий.
Создадим новое приложение:
Затем добавим в репозиторий файл rebar.config следующего содержания:
{require_otp_vsn, "R14|R15"}.
% куда складывать зависимости
{lib_dirs, ["deps"]}.
% список зависимостей
{deps, [
{ibrowse, ".*",
{git, "git://github.com/cmullaparthi/ibrowse.git",
{tag, "v4.0.1"}}}
]}.
На самом деле rebar способен не только тянуть зависимости. Он также умеет прогонять модульные тесты, поддерживает хуки, плагины, и многое другое, так что rebar.config может быть намного сложнее. См например rebar.config.sample в репозитории rebar’а, а также список команд, поддерживаемых rebar’ом: rebar -c
.
Теперь создадим файл src/gsearch.erl:
-export([main/0, main/1]).
-include("deps/ibrowse/include/ibrowse.hrl").
main(List) ->
Query = lists:foldl(
fun(X, Sum) ->
Sum ++ " " ++ atom_to_list(X)
end, "", List ),
EncodedQuery = http_uri:encode(Query),
SearchUrl = "http://www.google.com/search?q=" ++ EncodedQuery,
io:format("Fetching ~s ...~n", [SearchUrl]),
ibrowse:start(),
case ibrowse:send_req(SearchUrl, [], get) of
{ ok, "200" , _Headers, Data } ->
parse_serp(Data);
Rslt ->
io:format("Request failed: ~p~n", [Rslt])
end.
main() ->
io:format("Usage: gsearch query~n").
parse_serp(Data) ->
Regex = "<a href=\"/url\\?q=(.*?)&sa=U",
case re:run(Data, Regex, [ global, {capture, [1], list}]) of
{ match, MatchList } ->
lists:foreach(
fun ([Url|_]) ->
DecodedUrl = http_uri:decode(Url),
io:format("~s~n", [DecodedUrl])
end,
MatchList );
NoMatch ->
io:format("Nothing found: ~p~n", [NoMatch])
end.
Здесь мы просто шлем HTTP-запрос Google с помощью библиотеки ibrowse и парсим выдачу регулярными выражениями. Для работы с регулярными выражениями (при помощи PCRE) используется модуль re, для работы с urlencode — модуль http_uri. Эти модули, как и lists или io, идут вместе с Erlang. Кстати, вместо ibrowse мы также могли использовать стандартный модуль httpc, но тогда не пришлось бы использовать rebar!
Тянем зависимости:
Собираем приложение:
Для удобства создадим скрипт gsearch.sh:
erl -noshell \
-pa ebin ./deps/*/ebin \
-s gsearch main $@ \
-s init stop
Проверяем, все ли работает:
./gsearch.sh записки программиста
У меня программа честно ходит в Google и выводит в консоль результаты поиска. Надеюсь, что и у вас тоже.
Вот, собственно, и все, о чем я хотел сегодня поведать. В ближайшее время я планирую осилить модульное тестирование с помощью eunit и meck. Там все выглядит довольно просто. Кстати, я насчитал как минимум три инструмента для модульного тестирования в Erlang — это EUnit, Common Test и QuickCheck. Зачем их нужно так много на данном этапе остается для меня загадкой.
Дополнение: См также заметку про распаковку gzip в ibrowse.
Метки: Erlang, Функциональное программирование.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.