Робот для Хедхантера

logoДобрый день уважаемые читатели.

На повестке моего дня стоит поиск работы, и для этого я традиционно пользуюсь сайтом www.hh.ru .

На данном сайте размещаются резюме людей, ищущих работу. Сайт очень популярен в среде IT специалистов и можно смело сказать, что в другие места ходить не придется — все работодатели уже здесь.

Сайт устроен по принципу работы с обеих сторон. Если работнику очень нравится вакансия в определенной компании, то он может откликнуться на неё, если работодателю понравилось резюме работника, то он может откликнуться на его резюме.

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

Для этого существует специальная кнопка «Обновить дату»

Обновлять резюме можно каждые 4 часа, т.е. 6 раз за сутки.

У сайта есть свой мобильный клиент для Андроида, но скачав его, я не нашел функции, из-за которой я его устанавливал, а именно — «Обновить дату».

Если компания по своим причинам (авто-обновление платное) не делает такой функционал для меня, тогда я сам сделаю его.

В данной статье я хочу поделиться опытом создания программы для сайта www.hh.ru .

Разрабатывать данную программу мы будем на языке Delphi в среде XE5.

Для начала нам нужно провести анализ поведения сайта: что он принимает-отправляет во время нашей с ним работы. Для этих целей в Firefox есть встроенный инструмент Веб-консоль (Инструменты — Веб разработка — Веб-консоль). Методом научного тыка (реверс инженеринга), совместно с товарищем [AltaVista] было выяснено, что браузер посылает POST-запрос с ID резюме по адресу http://hh.ru/applicant/resumes/touch , причем в куках у него должно обязательно лежать 3 параметра :

1) hhtoken — дается при авторизации;

2) hhuid — какое-то ID (по идеи ID человека, разместившего резюме);

3) referrer _id страничка, с которой перешли.

Эти параметры можно увидеть в Веб-консоли после нажатия кнопки «Обновить дату».

Выделяем наш POST запрос.

Переходим во вкладку Куки.

Три параметра мы получили, теперь найдем ID нужного нам резюме.

Оно находится в адресной строке страницы http://hh.ru/resume/6d000db0ff00d00e000000ed1f700000000f51, т.е. нужное нам значение —

ID = 6d000db0ff00d00e000000ed1f700000000f51

Теперь осталось только написать программу, которая с периодичностью 1 раз в 4 часа отправляла бы POST запрос на разведанный адрес.

Для разработки возьмем старый добрый Delphi в его последнем издании XE5.

Давайте опишем создание программы по пунктам:

  1. Создайте новый проект: File-New-VCL Forms Application
  2. Измените в объектном инспекторе свойство формы Caption на «HH bot v2», Color на «clWhite», Position на «poDesktopCenter», BorderStyle на «bsSingle».

  3. Сохраните файл как Main.pas и проект как hh_bot_v2.dproj, последовательность действий File->Save Project As-> создать отдельную папку для проекта->Unit1.pas заменить на Main.pas-> Project1.dpr заменить на hh_bot_v2.dproj.
  4. Добавьте на форму компоненты ApplicationEvents,Timer, PopupMenu, TrayIcon, IdHTTP, label-4шт., Edit -4шт, Button, BitButton-2 шт.

Настройте все визуально, чтобы в итоге получилось как на картинке :

  1. Компоненты Edit последовательно переименуйте в ID_Edit ,hhtoken_Edit, hhuid_Edit.Text, referrer_id_Edit
  2. У компонента TrayIcon1 установите свойство PopupMenu на «PopupMenu1», в свойство Hint напишите «Обновление резюме работает», добавьте 2 пункта в это меню :

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

    Напишите обработчики кликов данных пунктов:
    [cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
    procedure TForm1.N1Click(Sender: TObject);

    begin

    About_Form.Showmodal;

    end;

procedure TForm1.N2Click(Sender: TObject);

begin

Close;

end;
[/cc]

Создайте новую форму File-New-Form, назовите её About_Form и сохраните как About_frm. Pas
  • Приведите форму к подобному виду:

В нашей программе будет возможность сохранять настройки. Для этого мы будем использовать ini файл.

Изучив документацию, для этих целей выберем не привычный класс TiniFile, а TMemIniFile , потому как Ембаркадеро советует :

«On Windows, a related object, TMemIniFile, works the same way as TIniFile, but the buffer writes in memory to minimize disk access.»

При старте программы сохраненные настройки резюме будут заполнять поля формы. В случае, если настройки изменились, нужно нажать кнопку «Сохранить настройки» и настройки в ini файле обновятся.

Вот заполнение настройками полей во время создания формы:
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.FormCreate(Sender: TObject);

begin

IniFile := TMemIniFile.Create(ChangeFileExt(Application.Exename, ‘.ini’));

{ Вызываем конструктор, в который передаем путь к нашему INI файлу,

в данном случае он будет находится там же где и программа и будет иметь тоже имя файла }

try

ID_Edit.Text := IniFile.ReadString(‘settings’, ‘ID’, »);

hhtoken_Edit.Text := IniFile.ReadString(‘settings’, ‘hhtoken’, »);

hhuid_Edit.Text := IniFile.ReadString(‘settings’, ‘hhuid’, »);

referer_id_Edit.Text := IniFile.ReadString(‘settings’, ‘referer_id’, »);

finally

end;

end;
[/cc]

Вот процедура Сохранения настроек:
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.Save_BitBtnClick(Sender: TObject);

begin

try

IniFile.WriteString(‘settings’, ‘ID’, ID_Edit.Text);

IniFile.WriteString(‘settings’, ‘hhtoken’, hhtoken_Edit.Text);

IniFile.WriteString(‘settings’, ‘hhuid’, hhuid_Edit.Text);

IniFile.WriteString(‘settings’, ‘referer_id’, referer_id_Edit.Text);

finally

IniFile.UpdateFile; { Обновляем значения в INI файле }

end;

end;
[/cc]

При закрытии программы очищаем созданный класс TMemIniFile
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

try

IniFile.Free; { Освобождаем память }

finally

end;

end;
[/cc]
Теперь напишем процедуру отправки POST-запроса :
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.Refresh_hh(sHHTOKEN: string; sHHUID: string; sREFERID: string;

sResumeID: string);

var

PD: TStringList;

begin

IdHTTP1.Request.ContentType := ‘application/x-www-form-urlencoded’;

IdHTTP1.Request.CustomHeaders.Clear;

IdHTTP1.Request.CustomHeaders.Add(‘Cookie:’ + ‘hhtoken=’ + sHHTOKEN + ‘; ‘ +

‘hhuid=’ + sHHUID + ‘; ‘ + ‘referer_id=’ + sREFERID + ‘; ‘ +

‘dont_redirect=1;’);

PD := TStringList.Create;

PD.Add(‘resume=’ + sResumeID);

PD.Add(‘undirectable=true’);

try

IdHTTP1.Post(‘http://hh.ru/applicant/resumes/touch’, PD);

finally

FreeAndNil(PD);

end;

end;
[/cc]

У неё 4 параметра, которые мы уже разузнали ранее.

В процедуре мы укладываем нужные кукисы в компонент IdHTTP1, а позже отправляем из него запрос с ID резюме по адресу http://hh.ru/applicant/resumes/touch .

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

Кнопку однократного обновления запрограммируем следующим образом :
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.Button1Click(Sender: TObject);

begin

Refresh_hh(hhtoken_Edit.Text, hhuid_Edit.Text, referer_id_edit.Text, ID_Edit.Text);

end;
[/cc]

Для автоматизации этого процесса установите свойство компонента Timer1 Interval равное 14 400 000 (4*60*60*1000)

И у события onTime пропишите:
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.Timer1Timer(Sender: TObject);

begin

Refresh_hh(hhtoken_Edit.Text, hhuid_Edit.Text, referer_id_edit.Text, ID_Edit.Text);

end;
[/cc]

Теперь давайте настроим включение автообновления. Для этого запрограммируйте кнопку «Включить таймер» следующим образом :
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.Timer_BitBtnClick(Sender: TObject);

begin

if not(Timer1.Enabled) then

begin

BitBtn1.Caption := ‘Выключить таймер’;

BitBtn1.font.Color := clred;

Timer1.Enabled := true;

end

else

begin

BitBtn1.Caption := ‘Включить таймер’;

BitBtn1.font.Color := clgreen;

Timer1.Enabled := false;

end;

end;
[/cc]

Если таймер включен, то программу можно будет свернуть и она появится в системном трее. Для этого у компонента ApplicationEvents1

напишем обработчик события OnMinimize :
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]
procedure TForm1.ApplicationEvents1Minimize(Sender: TObject);

begin

if (Timer1.Enabled) then

begin

Hide();

WindowState := wsMinimized;

TrayIcon1.visible := true;

end;

end;
[/cc]

При одиночном клике на иконку в трее, форма программы восстановится. Для этого у компонента TrayIcon1 напишем обработчик события OnClick :
[cc lang=»delphi» tab_size=»2″ line_numbers=»false» no_links=»false»]

procedure TForm1.TrayIcon1Click(Sender: TObject);

begin

TrayIcon1.visible := false;

Show();

WindowState := wsNormal;

Application.BringToFront();

end;
[/cc]

При клике правой кнопкой мыши, появится меню из двух пунктов, при наведении мыши, всплывет сообщение «Обновление резюме работает».

Вот и вся программа. Надеюсь, она поможет вам найти хорошую работу.

Нашли ошибку в тексте?

Робот для Хедхантера: 3 комментария

  1. Виктор Морозов

    Заметил в приведенном коде несколько конструкций вида:
    try
    IniFile.Free; { Освобождаем память }
    finally

    end;

    Причем в finally — ничего не делается. Вы не могли бы прояснить смысл блока try..finally с пустым finally?

    1. blog Автор записи

      Прояснить могу, программа собиралась из нескольких блоков и это кусок кода из другой программы. Тут и блок try..finally применен не правильно и скорее всего он не нужен вообще, т.к. при IniFile.Free ошибки быть не должно. Если я ошибаюсь, то прошу поправить. Как будет свободное время, я код перепишу на более верный.

      1. Виктор Морозов

        > Тут и блок try..finally применен не правильно и скорее всего он не нужен вообще
        Вот скорее всего — да.
        > т.к. при IniFile.Free ошибки быть не должно
        А если она и будет — не очень представляю, что можно было бы осмысленного в таком случае написать в finally.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *