Как известно, на «Авито» периодически появляются товары очень хорошего качества и при этом очень дешевые. Но появляются они редко, висят там мало и исчезают быстро.

Поэтому возникла у меня идея: а не поискать ли сервис, который раз в несколько минут проверяет объявления, и если появилось что-то интересное для меня — оповещает об этом? При этом оповещать лучше всего по смс, а то почту я не всегда проверяю оперативно.

Гуглинг выдал несколько таких сервисов, «всего» от 3 руб за смс или от 4 руб в сутки.

В итоге, я решил написать такой сервис самостоятельно, но об этом дальше…

Для интереса я зарегистрировался на одном из сервисов. Вчера он проверял ссылки каждые 15 минут, и если что изменилось, слал уведомления на почту. Про смс же у них на сайте было вскользь упомянуто, что почта mail.ru умеет отправлять смски. По факту оказалось, что mail.ru умеет отправлять только на мегафон, а у меня совсем даже не он… А если надо билайн-мтс, то пожалуйста, сервис с удовольствием поможет, за отдельную денежку.

Замечу также, что я давно являюсь пользователем очень удобного и бесплатного сервиса, о котором давным-давно писали тут же на хабре, и который позволяет отправить письмо с определенной темой на определенный ящик, и содержание письма придет мне в виде смс. Хотел указать свой_ящик@sms.ru для писем сервиса, но не понял как поменять тему письма, без чего смс не получишь.

Кроме того, сегодня демо-период глисы закончился, и периодичность проверки стала 720 минут.

В общем, подумав что платить за, прошу прощения, «сервис» такого уровня это все равно что платить за винду воздух, я решил что проще всего потратить часа 3 своего ценного времени и соорудить подобный сервис самому, благо парсинг страницы «Авито» тривиальный и, как следует ниже, занял у меня ровно 1 строчку кода.

Я для этого скрипта использовал VPS-хостинг. Подойдет также WEB-хостинг, при условии наличия на нем перла, доступа «наружу» и планировщика. В крайнем случае, подойдет любой включенный в инет комп. Думаю, у многих есть что-то подобное.

На чем написан скрипт


Писать его я решил на перле, и хотя знаю я перл довольно посредственно, для скриптов такого плана он подходит лучше всего. Там, где разбираться с перлом было лень, я не особо напрягаясь, вызывал команды шелла через system. Тем не менее, получилось, на мой взгляд, довольно прилично и даже не постесняюсь показать свое творение общественности.

Логика работы, кратко


— Запускаем скрипт каждые xxx минут;
— Скачиваем страничку с помощью wget;
— Храним скачанную в прошлый раз страничку, сравнивая ее с вновь скачанной, если какие-то объявления изменились/появились новые — отправляем смс об этом.

Вытаскиваемая из объявлений инфа — это:

1. URL объявления (который я использую как уникальный идентификатор объявления);
2. Название;
3. Цена.

При этом предусмотрено: если вдруг произойдет сбой при одном из скачиваний страницы, то старый список останется, а страница просто скачается в следующий раз, тогда же и придет смс об изменениях, если они произошли.

Детальнее


Перед использованием проверить пути и имена для mailer и wget, убедиться что они у вас есть и работают. В частности, у меня в centos мейлер называется mutt, чаще встречается mail или sendmail с тем же синтаксисом. Может быть, вам надо заменить wget на /usr/local/bin/wget и т.п.

Также следует задать свой ящик и телефон, на который хотите получать уведомления.

Запускать скрипт командой: ./avito.pl урл_страницы_с_объявлениями.

Замечу, что URL страницы должен быть в виде «списком с фото». Иначе говоря, в урле не должно быть никаких &view=list или &view=gallery.

Пример урла: www.avito.ru/moskva?q=%D1%80%D0%B5%D0%B7%D0%B8%D0%BD%D0%BE%D0%B2%D1%8B%D0%B9+%D1%81%D0%BB%D0%BE%D0%BD

Страница скачивается в файл с именем полученным из урла, с заменой всех левых символов на подчеркивания, наподобие такого:

https___www.avito.ru_moskva_q__D1_80_D0_B5_D0_B7_D0_B8_D0_BD_D0_BE_D0_B2_D1_8B_D0_B9__D1_81_D0_BB_D0_BE_D0_BD

Оно должно быть уникальным, поддерживаться и в линуксе, и в винде и при этом быть достаточно читаемым.

Если такой файл уже есть, скрипт пытается вытащить из него объявления. Если объявлений в файле не найдено, скрипт вызывает wget, при этом перезаписывая файл. Если объявления найдены, файл сохраняется с суффиксом -1:

https___www.avito.ru_moskva_q__D1_80_D0_B5_D0_B7_D0_B8_D0_BD_D0_BE_D0_B2_D1_8B_D0_B9__D1_81_D0_BB_D0_BE_D0_BD-1

Далее страница скачивается заново, в ней проверяются следующие ситуации:

1. Если объявления в новой скачанной странице не найдены, скрипт просто завершается — старая страница остается с суффиксом -1. Это на тот случай, если вдруг сеть пропала или подвисла — прошлый список объявлений не потеряется.
2. Если скрипт запущен в первый раз (не найдена ранее скачанная страница), то инфа придет просто о количестве имеющихся объявлений:
Found 25 items, page www.avito.ru/moskva?q=%D1%80%D0%B5%D0%B7%D0%B8%D0%BD%D0%BE%D0%B2%D1%8B%D0%B9+%D1%81%D0%BB%D0%BE%D0%BD monitoring started

Если это сообщение пришло, значит система запустилась, это главным образом проверка того, что все заработало.

Поскольку смс должно быть чем короче, тем лучше, то все сообщения очень лаконичны.

3. Если появилось новое объявление, то инфа об этом добавится в текст будущей смс. Потом по всем объявлениям инфа придет в виде одной смс.
4. Если изменилась цена или наименование товара, то инфа придет в виде: старая_цена -> новая_цена наименование ссылка. Или новое_название ссылка.

Не знаю, может ли измениться название, но лишнюю проверку сделать было не жалко.

5. В консоль выводится отдельным текстом список того, что нашлось. Это сделано больше для отладки, потому как сегодня парсер работает, а завтра, когда они поменяют разметку, перестанет. Придется менять парсинг.

О парсинге и нюансах


Собственно, весь парсинг — в этой строчке:

while($text=~/<div class=\"description\"> <h3 class=\"title\"> <a href=\"(.*?)\".*?>\n(.*?)\n.*?<div class=\"about\">\n\s*(\S*)/gs)

Хотя, еще цена содержит пробел в виде nbsp, который я вырезаю другим regexp-ом:

$price=~s/&nbsp;//g

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

g — модификатор глобального поиска, который позволяет засунуть поиск внутрь условия while, выдавая каждый раз следующее объявление;
s — позволяет внутри одного регекспа проводить поиск в нескольких строчках (на «Авито» URL, наименование и цена располагаются на 4 строках, но это сейчас, пока они верстку не поменяли).

Также замечу, что для многострочного чтения файла в начале скрипта присваивается:

undef $/;

Это чтобы my $text=; прочитал в себя весь файл целиком.

Еще нюанс: я во все смски вставляю кликабельные урлы. У меня нормальный смартфон, который позволяет ткнуть в урл внутри смс и попасть на нужную страницу, очень удобно. Так вот, почему-то sms.ru портит такой невинный символ, как подчеркивание. Заменяя его на %C2%A7. Повлиять я на это не могу, зато могу заменить его на код подчеркивания, который доходит нормально, при этом урл становится кликабельным для sms.ru, оставаясь таким и для обычной почты: $text=~s/_/%5F/g;

Добавляем задание в планировщик


#crontab -e
*/20    *       *       *       *       cd /scripts/avito && ./avito.pl 'https://www.avito.ru/moskva?q=%D1%80%D0%B5%D0%B7%D0%B8%D0%BD%D0%BE%D0%B2%D1%8B%D0%B9+%D1%81%D0%BB%D0%BE%D0%BD'

Каждые 20 минут вызывать скрипт, проверяя страницу. Не забыть экранировать URL одинарными кавычками.

Таких заданий можно задать сколько угодно, они все будут работать независимо друг от друга.

Что я еще не сделал для промышленного варианта и что было бы легко доделать


1. Веб-морду для добавления/удаления юзеров и заданий. Хранение урлов, периодичности, ящика и телефона юзеров на sms.ru в базе mysql. Скрипт бы вызывался каждую минуту, проверял, по какому урлу выполняться и слал бы смс не на мой жестко забитый номер, а на тот, который задан пользователем.

Тогда можно было бы сдирать с юзеров по 8 рублей в день или что-то типа того. Может, заняться? Есть желающие за такую штуку денег заплатить?

2. Фильтр цен. Игнорировать цену выше или ниже заданной. Делается элементарно, еще одним if: next if($page_new{"price"}{$uri}>$max_price or $page_new{"price"}{$uri}<$min_price). Просто не нужно было.

3. По аналогии с «Авито», добавить авто.ру, irr и т.п. сайты.

Тоже элементарно, просто за тем while(...){...} дописать еще несколько while — каждому сайту по одному. Главное, чтобы внутри них заполнялись $page{"name"}{$uri} и $page{"price"}{$uri}.

По каждому сайту будет срабатывать свой while, остальные просто возвращать пустой результат.

Ну и собственно код скрипта


#!/usr/bin/perl

use strict;
undef $/;

my $url=$ARGV[0];
my $mailer="mutt";
my $wget="wget";

if($url eq ""){
    print "Usage: avito.pl <https://www.avito.ru/...url>";
    exit;
}

my $filename=$url;
$filename=~s#[^A-Za-z0-9\.]#_#g;
$url=~m#(^.*?://.*?)/#;
my $site=$1;
print "site:".$site."\n";

sub sendsms {
    my $text=shift;
    $text=~s/_/%5F/g;
    $text=~s/&/%26/g;
    system("echo '$text' | $mailer -s 79xxxxxxxxx xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\@sms.ru");
}

sub parse_page {
    open(MYFILE,"<".shift);
    my $text=<MYFILE>;
    close(MYFILE);
    my %page;

    while($text=~/<div class=\"description\"> <h3 class=\"title\"> <a href=\"(.*?)\".*?>\n(.*?)\n.*?<div class=\"about\">\n\s*(\S*)/gs)
    {
        my $uri=$1;
        my $name=$2;
        my $price=$3;
        $uri=~s/^\s+|\s+$//g;
        $name=~s/^\s+|\s+$//g;
        $price=~s/^\s+|\s+$//g;
        $price=~s/&nbsp;//g;

        $page{"name"}{$uri}=$name;
        $page{"price"}{$uri}=$price;
    }
    return %page;
}

my %page_old=parse_page($filename);

if(scalar keys %{$page_old{"name"}}>0){
    system("cp $filename ${filename}-1");
}
else{
    %page_old=parse_page("${filename}-1");
}
system("$wget '$url' -O $filename");
my %page_new=parse_page($filename);

if(scalar keys %{$page_old{"name"}}>0){ # already have previous successful search
    if(scalar keys %{$page_new{"name"}}>0){ # both searches have been successful
        my $smstext="";
        foreach my $uri(keys %{$page_new{"name"}})
        {
            if(!defined($page_old{"price"}{$uri})){
                $smstext.="New: ".$page_new{"price"}{$uri}." ".$page_new{"name"}{$uri}." $site$uri\n ";
            }
            elsif($page_new{"price"}{$uri} ne $page_old{"price"}{$uri}){
                $smstext.="Price ".$page_old{"price"}{$uri}." -> ".$page_new{"price"}{$uri}." ".$page_new{"name"}{$uri}." $site$uri\n";
            }
            if(!defined($page_old{"name"}{$uri})){
                # already done for price
            }
            elsif($page_new{"name"}{$uri} ne $page_old{"name"}{$uri}){
                $smstext.="Name changed from ".$page_old{"name"}{$uri}." to ".$page_new{"name"}{$uri}." for $site$uri\n";
            }
        }
        if($smstext ne ""){
            sendsms($smstext);
        }
    }
    else{ # previous search is successful, but current one is failed
        # do nothing, probably a temporary problem
    }
}
else{ # is new search
    if(scalar keys %{$page_new{"name"}}<=0){ # both this and previous have been failed
        sendsms("Error, nothing found for page '$url'");
    }
    else{ # successful search and items found
        sendsms("Found ".(scalar keys %{$page_new{"name"}})." items, page '$url' monitoring started");
    }
}

foreach my $uri(keys %{$page_new{"name"}})
{
    print "uri: $uri, name: ".$page_new{"name"}{$uri}.", price: ".$page_new{"price"}{$uri}."\n";
    if($page_new{"price"}{$uri} eq $page_old{"price"}{$uri}){print "old price the same\n";}
    else{print "old price = ".$page_old{"price"}{$uri}."\n";}
    if($page_new{"name"}{$uri} eq $page_old{"name"}{$uri}){print "old name the same\n";}
    else{print "old name = ".$page_old{"name"}{$uri}."\n";}

}

Комментарии (66)


  1. evilbot
    15.10.2015 10:39
    -2

    А почему wget, а не curl?


    1. Anisotropic
      15.10.2015 12:28
      +8

      Тут вообще много почему, начиная с perl`а.


      1. jrip
        15.10.2015 13:20
        +5

        Странные у вас почему, а почему curl а не wget?
        Чем перл не нравится, на чем предложите писать?


        1. AleksRT
          15.10.2015 13:34
          -8

          на python?


          1. jrip
            15.10.2015 15:05
            +2

            И чем он тут обосновано больше подходит?


    1. agent_0007
      15.10.2015 17:00
      +1

      а как же LWP::Simple?
      зачем все так сложно wget, curl?


      1. parserpro
        20.10.2015 13:41

        вот именно.


  1. priv8v
    15.10.2015 13:01
    +1

    На sms.ru можно себе отправлять смску простым гет-запросом, где одним из параметров будет тупо текст смски, если правильно помню — там больше 20 смс в день можно на свой номер слать. бесплатно.


    1. Newbilius
      16.10.2015 08:03

      Спасибо за ссылку, но увы, не 20…

      На собственный номер — для программистов, которые отправляют себе оповещения из своих программ *

      *Сообщения на собственный номер бесплатны до 5 СМС в день при условии, что каждое сообщение помещается в 1 СМС (до 70 русских / 160 латинских символов). При превышении этих лимитов, сообщения оплачиваются согласно тарифу.


      1. priv8v
        19.10.2015 11:06

        Раньше было больше (


  1. denv
    15.10.2015 13:08
    +1

    С Mojolicious (perl framework) не знакомы? Можно гораздо проще сделать на нем, регулярные выражения и внешний wget не потребуется. А еще есть телеграмм бот готовый под ваши нужды, ссылку оставлять не буду, отвечу в личке.


    1. kloppspb
      15.10.2015 14:13
      +1

      Угу. Куда проще:

      my $ua = Mojo::UserAgent->new();
      my $content = $ua->get( $URL );
      my $dom = Mojo::DOM->new( $content->res->body );
      


    1. flaresun
      16.10.2015 10:48
      +1

      В моджо вникать дольше и сложнее, чем в регулярки и вызов system :)
      Но почему wget, а не LWP мне всё равно не понятно.


      1. iltmpz
        16.10.2015 11:51

        LWP я не использовал по единственной причине: ничего о нем не знал до сего момента…
        А wget успешно пользуюсь уже лет 15, еще с диалап-времен.

        На самом деле согласен, LWP гораздо удобнее для того чтобы просто прочитать страничку в переменную. Если соберусь переделывать свой скрипт — перепишу на LWP.

        Еще вопросы в ту же тему: а почему system(«mutt...») вместо какой-нибудь перловской библиотечки, почему даже system(«cp ...»), разве перл не умеет копировать файлы?.. Ответ тот же: мое не очень хорошее знание перл…
        Конечно, не в ущерб читаемости и объему кода: чтобы не получилась замена одной строки с system на страницу кода инициализации какого-нибудь фреймворка.


        1. flaresun
          16.10.2015 12:45

          Я ни в коем случае не в претензию :) Просто по запросу perl http get в первых пяти результатах речь как раз про LWP, с примерами :), т.е. довольно распространённая и простая библиотека.


  1. BupycNet
    15.10.2015 13:44
    +1

    «Поэтому возникла у меня идея: а не поискать ли сервис, который раз в несколько минут проверяет объявления, и если появилось что-то интересное для меня — оповещает об этом? При этом оповещать лучше всего по смс, а то почту я не всегда проверяю оперативно.»
    PushAll дает бесплатные push-уведомления на смартфон и компьютер.


    1. iltmpz
      16.10.2015 11:55

      Спасибо, глянул, интересная альтернатива, а то как раз вчера не пришла пара смсок от sms.ru, это периодически случается…


  1. mihmig
    15.10.2015 13:44
    +4

    Пользовались sms.ru пару лет, потом пришлось отказаться — техподдержка никакая (не отвечает), СМС тупо не приходят. Перешли на одного из многих операторов. Хотя сейчас думаем отказаться от «понтового» FIRMNAME в имени отправителя. Сотовые операторы «просекли фишку» и планомерно повышают ценник. Отправлять можно через 3G/4G-модем или смарт на Android, благо есть операторы с безлимитными/адекватными СМС-тарифами.


    1. iltmpz
      16.10.2015 12:03

      Мы пользуемся обычным GSM-модемом для мониторинга оборудования. Он надежнее для тех случаев, когда например сеть упала, соответственно, ни на какую почту ничего не придет.
      А sms.ru — бесплатная альтернатива, которая дублирует GSM-модем, и заодно используется для всяких не сильно критичных уведомлений, за которые не хочется платить.


  1. wildvampir
    15.10.2015 13:50

    Было бы хорошо, дополнительно возможность отправлять в мессенджеры или например бота для телеграм)


  1. kloppspb
    15.10.2015 14:00
    +1

    По парсингу HTML регекспами не проходился только ленивый, поэтому не буду повторять :) А вот длинное «wget в файл» легко и непринуждённо заменяется чем-то попроще, например, LWP::Simple::get…


    1. jrip
      15.10.2015 15:11
      +2

      А автора похоже олдскульный админский подход, т.е. накидать на перле по быстрому, используя стандартные средства, так чтобы работало.
      В нем нет ничего плохого, всякие изыски из серии LWP::Simple::get или монструозного фреймворка для разбора html — оно как бы необязательные изыски, ведь задачу написанный код решает, при этом он даже понятен.


      1. kloppspb
        15.10.2015 15:28
        +1

        Это HTML::Tree или HTML::DOM монструозные, Mojo::DOM — порхающая невесомая бабочка по сравнению с ними :) А wget, кстати, да и curl, моложе LWP на несколько лет.

        Но в общем да, можно и так, конечно: корявенько, но работает…


        1. jrip
          15.10.2015 15:33

          >корявенько, но работает…
          Ну кстати тут даже не корявенько, а довольно добротно написано )
          Так то на практике обычно бывает, что админы обмениваются и передают по наследству ацкие однострочники, как-то даже приходилось это дело расшифровывать.


          1. kloppspb
            15.10.2015 16:39

            Всё так. Но там речь идёт о расширении базы сайтов, фактически о продакшн-варианте… А когда сайтов станет больше — больше станет и селекторов, и правил, и много чего ещё. Такой код будет уже не очень. Знаю о чём говорю, ибо у меня подобные парсеры перемалывают десятки тысяч страниц, с парой сотен наборов правил извлечений :-)


            1. iltmpz
              16.10.2015 12:21

              Это верно, сейчас все-таки скрипт получился уровня «сейчас отмониторить пару объявлений».
              Когда речь идет о сопровождении программного продукта с гибко меняющимися входными условиями, несколькими разными сайтами объявлений, все это эволюционирует по запросам клиентов, то этот код скорее всего придется переписать под ноль. И скорее всего уже не на перле. Я бы выбрал ПХП.
              Пока я правда парсеры «с парой сотен наборов правил извлечений» еще не делал :)


      1. iltmpz
        16.10.2015 12:14

        Именно, это я еще этот скрипт писал, планируя выложить его на хабр, поэтому старался сделать более-менее красиво, обычно терпения на это не хватает. Правда потом за это сам же и расплачиваюсь, когда спустя полгода надо его чуть переделать.

        Но 1 строчка старого доброго system(«шелловская команда») зачастую понятнее многим, чем инициализация хитрого фреймворка, который еще не у всех установлен, либо не той версии, из-за чего сперва провозишься с полчаса, пытаясь его установить, плюнешь, да и перепишешь скрипт сам.

        Кстати, я честно пытался вместо регекспа применить HTML::Parser. Разбирался с ним, разбирался, потом понял, что мне в данном случае важнее не содержимое полей в тегах, которые он вытаскивает, а именно определенная подстрока в виде шаблона. Ну и считаю, что регексп, который у меня занял ровно 1 строчку — оптимальное решение среди доступных инструментов.
        Конечно, эту строчку придется периодически переписывать по мере смены верстки авито.


        1. kloppspb
          16.10.2015 12:19

          >Именно, это я еще этот скрипт писал, планируя выложить его на хабр

          Э… А вот это зря было сказано. Если бы это было сиюминутное откровение — тогда понятно. Но зачем выкладывать специально написанный в наихудшем стиле прошлого века велосипед с квадратными колёсами (да ещё и абсолютно стандартный, который любой перловик изобретал в своей жизни не один раз и уже не один раз отказавшийся от подобных костылей в пользу фреймворков) — непонятно совсем.


          1. iltmpz
            16.10.2015 12:27

            Может быть, для новичков? Показать некоторые типовые решения, которые мне пришлось применить. Ну и как я справился с некоторыми трудностями, в данном случае тоже типовыми.
            Сам давно читаю хабр, и в подобных решениях нахожу для себя иногда что-нибудь интересное.
            Ну и я не говорю что писал скрипт специально для хабра, я его писал для себя, просто стараясь сделать его более-менее читаемым для других.


            1. kloppspb
              16.10.2015 12:40

              Учить такому новичков — очень и очень плохо. И разбор косяков можно начинать уже со строчки 'undef $/', за которую нужно руки отрубать сразу же.


              1. iltmpz
                16.10.2015 13:00

                А что в ней плохого?
                Как мне еще одной командой читать весь файл в переменную?
                Я даже искал какие-нибудь «правила хорошего тона» для перла — не нашел, может, плохо искал?

                Во всех книжках/доках по перлу написано про подобные конструкции $/ — нигде не упоминается о том что они плохи.


                1. kloppspb
                  16.10.2015 13:15
                  +1

                  Плохого в ней:

                  1) Неудобопонятность. Я, имея немалый перлостаж, помня наизусть perldoc perlvar и не путая $/, $\ и $!, всё равно предпочту написать вместо них $INPUT_RECORD_SEPARATOR, $OUTPUT_RECORD_SEPARATOR и $OS_ERROR.

                  2) Переопределение глобальных переменных в глобальном контексте плохо и небезопасно всегда. Даже если код крошечный и ты клянёшься об это помнить — плохая привычка, хорошая — только в локальном.

                  3) Велосипед с кучей лишней ручной работы (use File::Slurp, например).

                  >Во всех книжках/доках

                  Поправка: во всех ДРЕВНИХ книжках. Современных правил хорошего тона навалом, начиная с Modern Perl Book:

                  http://modernperlbooks.com/books/modern_perl/chapter_00.html
                  https://github.com/vlet/modern_perl_book/wiki

                  и заканчивая Perl Best Practices с perlcritic:
                  http://www.oreilly.com/catalog/perlbp/


  1. dmx102
    15.10.2015 15:31
    +2

    Следующим этапом будет борьба с блокировками по ip.


    1. BupycNet
      15.10.2015 15:32
      +1

      Авито не будет блокировать. если он будет раз в 10 минут сканировать.


      1. jrip
        15.10.2015 15:35

        deleted


  1. mtp
    15.10.2015 17:00
    +1

    Идея интересная. Я только не совсем понял, какой смысл присылать оповещения обо вообще всех новых поступлениях? Хотя бы минимальную фильрацию по имени/содержимому бы.


  1. ValdikSS
    15.10.2015 18:56
    +1

    Я себе похожим образом искал Haswell-процессор, только без оповещений, просто вывел на отдельный монитор и раз в полчасика просматривал.

    Парсить регэкспом, это, конечно, приемлемо, но не особо удобно-то.

    gist.github.com/ValdikSS/5801c0c8a0e279d33466


    1. iltmpz
      16.10.2015 12:40

      Да, Питон — мощная штука, если он так умеет 1 строкой вытаскивать «div.description».
      Ну и остальное тоже достойно.
      Знал бы питон — писал бы на нем


      1. ValdikSS
        16.10.2015 19:36

        Не в питоне дело, это обычный dom-parser, их и для perl предостаточно.


  1. JC_Piligrim
    15.10.2015 20:17
    -1

    А почему sms, а не telegram-бот какой?


  1. ingumsky
    15.10.2015 22:06
    +3

    По поводу конкретики запроса — много резиновых слонов вам встретилось в результатах?

    Скрытый текст
    Понимаю, что вы в качестве примера использовали не ту фразу, по которой действительно ищете для себя товары, но ведь по какой-то причине вы выбрали именно её, а не другие.


  1. rewse
    16.10.2015 02:44

    WWW::Mechanize еще никто не упомянул. По мне так удобнее всего перечисленного.


    1. kloppspb
      16.10.2015 04:32

      Не упомянули потому что он для другого — для имитации взаимодействия пользователя с сайтом, а не для полноценной работы с DOM. Общая схема может быть такая: с помощью WWW::Mechanize мы выполняем пользовательские сценарии (например, логинимся, заполняем какие-то формы и т.д.). А вот структуру страниц после этого разбираем нормальным парсером.

      Вот здесь, например, можно посмотреть пример такой связки: http://habrahabr.ru/post/236519/. В качестве парсера там используется HTML::TokeParser, но можно и любой другой, это не принципиально. А принципиально — разделение труда :)


  1. Obramko
    16.10.2015 08:34

    Кстати, а для Perl нет чего-то вроде Scrapy? Как-то игрался, оставил очень положительные впечатления.


  1. Cord
    16.10.2015 10:42

    Великий и могучий Perl. Когда-то он был основным языком веб-разработки, уступив затем место PHP. Ностальгия…


    1. flaresun
      16.10.2015 11:28

      * … уступив затем зачем-то место … :)


      1. evilbot
        16.10.2015 12:19

        Просто людей которые писали программы руководствуюсь правилу:«Напиши первые 40 символов программы, затем нажми ввод и остальную программу напиши в следующей строчке» надо было заменить на других. А с ними у ушел Перл.


        1. flaresun
          16.10.2015 12:50

          :)
          Не, на perl'е можно писать просто чтобы писать на perl'е, можно писать неподдерживаемый код, можно писать просто странный код, а можно писать хорошо читаемый и поддерживаемый код. Он многое позволяет, нужно просто правильно использовать и по ситуации. Вон, python вполне не плох, порог вхождения не высокий, есть рекомендации как писать правильно, но он так же позволяет написать откровенно странный и сложно поддерживаемый код.


          1. iltmpz
            16.10.2015 12:57

            Почему-то именно перл меня все время провоцирует писать как можно более неподдерживаемый код.
            Можно написать программу в стиле ПХП, но гораздо проще нарисовать крючочков-закорючек, и все это будет будет быстро и будет работать. Правда потом при попытке переделать — часто проще написать заново.
            Хотя, я очень стараюсь писать понятно, как минимум для себя, а по возможности и для других.


            1. flaresun
              16.10.2015 13:00

              )) Ну это уже не должно быть проблемой языка ;)


    1. jrip
      16.10.2015 13:17
      +1

      Из наблюдений того времени

      perl прогеры строгие и немногословные, на все вопросы отвечали, если отвечали: «man {непонятное слово}»
      если учесть, что даже вывод hello world сопровождался приколами с разницей в переводах строк на window и linux, до джуниора доживали немногие. Если ты доживал до джуниора ты скорее всего умел собирать из исходников чего угодно что угодно, походу познакомившись с разработчиком этого чего угодно и прислав ему пару патчей.

      php прогеры, по большей части даже не прогеры, а так, желающие запилить что-нибудь. Любой вопрос, даже банальный, вызывал флейм на неделю, обязательные советы убить себя и заканчивался предложениями «а давай те уже чего-нить запилим нормальное, например cms, ведь cms это круто». Пара месяцев разработки на пхп в то время давали понимание, как что угодно собрать из говна и палок.

      А вот ведь даже не верится, что было время, когда было совершенно неясно что же выбрать, пхп или perl.


  1. return
    16.10.2015 11:25

    можно было бы сделать проще — есть же подписка на результаты поиска и когда в подписке появляется что-то новое, у нее слева (на странице подписок) появляется синий кружок.


  1. flaresun
    16.10.2015 11:50
    +1

    s — позволяет внутри одного регекспа проводить поиск в нескольких строчках

    Немного не так. Модификатор просто позволяет точке («.» — любой символ) соответствовать вообще любым символам, переносу строки в том числе. ;) Используется обычно вместе с m. А без этого модификатора регулярка всё равно работает со всем содержимым переданной переменной, просто переносы строки придётся указывать явно… Т.е. при указании этого модификатора все \n можно/лучше заменить на «точки», иначе пропадет смысл использование модификатора.

    У Вас регулярка указана не строкой в кавычках, поэтому внутри кавычки экранировать не нужно.
    Ленивые квантификаторы, это правильно, ну чуть быстрее будет обрабатываться если их заменить на что-то вроде [^"]+, например (в нужном месте). В данном конкретном случае это почти ничего не изменит, но вырабатывать правильные привычки — правильно :)


    1. iltmpz
      16.10.2015 12:52
      +1

      Спасибо, ценные замечания.
      Действительно, я сначала пытался использовать модификатор m для многострочного поиска, удивлялся почему не работает поиск по точке.

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

      Про [^"]+ вместо .?* — не подумал, действительно, должно быть быстрее, стараюсь писать правильно по возможности, чего и всем желаю. :)


  1. ange007
    16.10.2015 11:51
    -1

    Немного не по теме. Сервис по уведомлению о интересующем товаре на аукционах.
    Писал в основном для себя (с надеждой на развитие) — сервис по уведомлениям за появлением интересующих товаров на аукционах (правда только по email на данный момент): auctia.net
    Правда сейчас проект в небольшой заморозке (и не актуален для России из-за закрытия Молотка), а так-же в ожидании переезда на новый сервер.
    Но возможно кого-то заинтересует.


  1. iltmpz
    16.10.2015 18:57

    Нашел еще глюк, кстати вызванный подходом с моими system-костылями:
    Если урл и соотв. $text содержит знак &, то угадайте, что происходит по команде:
    system(«echo '$text' | $mailer -s 79xxxxxxxxx xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\@sms.ru»);

    Правильно, & срабатывает как спецсимвол, echo перенаправляется туда не знаю куда, и письмо приходит пустое, а смс вообще не приходит.
    Сейчас вылечил костылем в виде $text=~s/&/%26/g;
    Ну и урлы типа sometext>/etc/passwd оно «еще лучше» обработает.
    В общем, сейчас я сам задаю урлы, но такой подход применять нельзя — надо как-то по-другому организовать вывод.


    1. kloppspb
      16.10.2015 19:00
      +1

      use Mime::Lite же!
      (мог бы напомнить про URI::Escape, но в принципе неправильный подход: использовать Perl как враппер для системных программ)


      1. iltmpz
        16.10.2015 19:21

        Да, теперь соглашусь, получился очень наглядный пример, как не надо писать скрипты.
        Исправил:
        use MIME::Lite;
        и

        sub sendsms {
            my $text=shift;
            $text=~s/_/%5F/g;
            my $msg = MIME::Lite->new(  From    => 'foo@test.com',
                                        To      => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@sms.ru',
                                        Subject => '79xxxxxxxxx',
                                        Type    => 'text/plain; charset=UTF-8',
                                        Data    => "$text" );
            $msg->send();
        }
        

        Ну и, как водится, CentOS ругнулся на MIME::Lite, поэтому:
        yum install perl-MIME-Lite


  1. AlienZzzz
    16.10.2015 21:49

    как быстро вас забанило авито с таким парсингом?


    1. jaiprakash
      17.10.2015 20:16

      Не забанит. В cron выставил 20 мин или ещё реже и всё.


      1. AlienZzzz
        17.10.2015 20:36

        тогда вы пропустите объявления =)


        1. jaiprakash
          17.10.2015 20:39

          Если вам нужно что-то типа высокочастотного трейдинга, то рекомендую подкупить модератора авито — тогда самые вкусные объявления даже не появятся на доске ))


          1. AlienZzzz
            17.10.2015 20:44

            да не, я как бы намекаю, что вгетом с 20 минутными паузами вы много пропустите — либо второй вариант — парсить 4-5 страниц за раз


            1. jaiprakash
              17.10.2015 20:48

              Почему много? Запрос-то конкретный + фильтры.


  1. mihmig
    20.10.2015 16:14
    +1

    Авито внедрил «сохранённый поиск»…


  1. Renatk
    21.10.2015 13:40

    Никто не попросил список (или фото) покупок в студию. И впечатления (стоило ли ради этого) все это затевать?