Наконец-то появилось немного времени, чтобы выложить вторую часть статьи... Прошу не ругать, но вторая часть — это скорее теория, чем практика. Сначала хотел сделать полноценную простую программу для заливки новостей на сайт, но когда понял, что это получится вторая версия NoNaMe Post Editor-а, да еще и кастрированная, решил, что делать этого не стоит.
Поэтому предлагаю Вам самим сделать для себя такой редактор, какой Вам удобен и необходим. А я попробую Вам помочь. Итак, продолжаем...
Ну что же, продолжим... Так как API для NoNaMe так и нет, придется отправлять все данные на сайт через web-страницу. А для этого необходимо знать куда, что и как отправлять. В этой статье я как раз и попробую объяснить, как производить разбор страниц, как отправлять и т.д. Начинаем…
А начнем мы вот с чего. Открываем в браузере страницу добавления новости и смотрим исходный код. Нам необходимо найти форму добавления новости. Вот она:
<form method="post" action="/addnew" name="addnew">
<div class="np-block" id="np-edit">
<div id="np-panel">
<input type="text" id="np-name" name="np-name" value="Название новости" maxlength="70" />
<ul class="bbtag">
<li class="start"></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('cut',2, this);"><img src="http://www.nnm.ru/img/default/bb/cut.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('doc',1, this);"><img src="http://www.nnm.ru/img/default/bb/doc.gif" width="21" height="20" /></a></li>
<li class="end"></li>
</ul>
<ul class="bbtag">
<li class="start"></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('url',3, this);"><img title="Вставить ссылку" src="http://www.nnm.ru/img/default/bb/link.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('mail',3, this);"><img title="Вставить ссылку на Email" src="http://www.nnm.ru/img/default/bb/mail.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="window.open('http://www.sharing.ru/upload/partner.php?id=1', 'uploadfile', 'width=500, height=275, top=200, left=200');"><img title="Вставить файл" src="http://www.nnm.ru/img/default/bb/file.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('image',3, this);"><img title="Вставить картинку" src="http://www.nnm.ru/img/default/bb/image.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('video',3, this);"><img title="Вставить видео" src="http://www.nnm.ru/img/default/bb/video.gif" width="21" height="20" /></a></li>
<li class="end"></li>
</ul>
<ul class="bbtag">
<li class="start"></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('b',1, this);"><img title="Выделить текст жирным" src="http://www.nnm.ru/img/default/bb/bold.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('i',1, this);"><img title="Выделить текст курсивом" src="http://www.nnm.ru/img/default/bb/italic.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('u',1, this);"><img title="Подчеркнуть текст" src="http://www.nnm.ru/img/default/bb/underline.gif" width="21" height="20" /></a></li>
<li><a href="javascript:void(0);" onclick="javascript:insert('s',1, this);"><img title="Зачеркнуть текст" src="http://www.nnm.ru/img/default/bb/strike.gif" width="21" height="20" /></a></li>
<li class="end"></li>
</ul>
</div>
<textarea name="np-text" id="np-text">Текст новости</textarea>
<br /><br />Теги [через запятую, пример: <font color="blue">зима, фото, лыжи, я</font>]<input class="np-ist" type="text" name="tags" value="" id="np-tags" style="margin-bottom:2px;" />
<input class="np-ist" type="text" name="np-ist" value="http://Источник" id="np-ist" />
Отправить в раздел <input type="checkbox" name="n_dalee" value="1" onclick="if(!document.forms['addnew']['n_dalee'].checked){document.forms['addnew']['nra'].setAttribute('disabled', 'disabled');}else{document.forms['addnew']['nra'].removeAttribute('disabled');}" /> <select name="nra" disabled="disabled"><option value="0">[ выберите раздел ]</option><option value="1">newz</option><option value="2">filez</option><option value="3">mobilz</option><option value="9">muzic</option><option value="10">palmz</option><option value="12">gamez</option><option value="13">ironz</option><option value="14">webz</option><option value="15">creative</option><option value="16">video</option><option value="17">humor</option><option value="18">other</option></select><br /><br />
<b>Добавить голосовалку</b> <input type="checkbox" name="golos_check" onclick="showgolos();">
<table cellspacing="0" cellpadding="1" id="golos_inv"style="display:none;">
<tr><td width="100" align="right"><small><b>Вопрос</b>: </td><td align="right"><input name="vopr" type="text" value=""></td></tr>
<tr><td align="right"><small>Ответ #1: </td><td align="right"><input type="text" name="otv1" value=""></td></tr>
<tr><td align="right"><small>Ответ #2: </td><td align="right"><input type="text" name="otv2" value=""></td></tr>
<tr><td align="right"><small>Ответ #3: </td><td align="right"><input type="text" name="otv3" value=""></td></tr>
<tr><td align="right"><small>Ответ #4: </td><td align="right"><input type="text" name="otv4" value=""></td></tr>
<tr><td align="right"><small>Ответ #5: </td><td align="right"><input type="text" name="otv5" value=""></td></tr>
<tr><td align="right"><small>Ответ #6: </td><td align="right"><input type="text" name="otv6" value=""></td></tr>
<tr><td align="right"><small>Ответ #7: </td><td align="right"><input type="text" name="otv7" value=""></td></tr>
<tr><td align="right"><small>Ответ #8: </td><td align="right"><input type="text" name="otv8" value=""></td></tr>
<tr><td align="right"><small>Ответ #9: </td><td align="right"><input type="text" name="otv9" value=""></td></tr>
</table>
<br /><br />
<center><input type="submit" name="pre" value="Предпросмотр новости" /> </center>
</div>
<input type="hidden" name="doc" value="18641" />
</form><form method="post" action="/addnew" name="addnew">
<input type="text" id="np-name" name="np-name" value="Название новости" maxlength="70" />
<textarea name="np-text" id="np-text">Текст новости</textarea>
Пока хватит =))). Вот это уже можно разобрать построчно. Давайте попробую:
<form method="post" action="/addnew" name="addnew"> — из этой строки нам необходимо получить два параметра method, который отвечает за способ отправки новости на сайт, и action, который показывает куда отправляется новость. Больше здесь ничего интересного для нас нету…
<input type="text" id="np-name" name="np-name" value="Название новости" maxlength="70" /> — из этой строки нам необходимо название поля (name="np-name"), ну и как бонус – получаем максимальную длину заголовка (70 символов ) =).
<textarea name="np-text" id="np-text">Текст новости</textarea> — здесь нам также необходимо получить название поля (name="np-text")…
Так-с… С формой добавления новости разобрались. Теперь займемся авторизацией, т.к. без этого добавить новость не получится. Открываем страницу http://nnm.ruhttp://nnm.ru (если вы авторизованы – нажмите Выкл). Теперь снова смотрим исходный код страницы и выдираем необходимую форму:
<form method="post" name="auth" action="/">
<ul class="ulMenu" id="m-auth">
<li class="title">account</li>
<li><input type="text" name="login" value="Введите логин" onblur="if(this.value==''){this.value='Введите логин';}" onfocus="if(this.value=='Введите логин'){this.value='';}" /></li>
<li><input type="password" name="passwd" value="" /></li>
<li><a href="#" onclick="document.forms['auth'].submit();">вкл.</a>/выкл.</li>
<li class="right"><a href="http://www.nnm.ru/register">регистрация</a></li>
<li class="right"><a href="http://www.nnm.ru/retrive">забыл пароль?</a></li>
</ul>
</form>
Думаю здесь Вы уже разберетесь сами =). Если нет – спрашиваем в комментах, объясню.
По секрету скажу, что нам необходимо будет еще кое-что, но не сейчас. Поэтому я пока не буду грузить Вас лишним…
Ну что же, все необходимые данные для отправки новости у нас есть.
Вот тут необходимо остановиться и подумать, что будем использовать для работы с инетом. Тут есть множество вариантов: WinAPI, Indy, ICS, SwinHTTP и т.д. Когда-то, я перепробовал большинство из этих вариантов и остановился на 3-х.
1. Indy – использую когда необходимо быстро сделать какое-либо приложение, не задумываясь о куках, заголовках, формированиях запросов. Хорошая библиотека, но не лишена своих недостатков. Среди них – небольшие ошибки и, относительно, большой размер. Если с первым бороться еще можно (ошибок мало и они довольно специфичные, т.ч. мало кто их заметит), то со вторым трудновато. Размер библиотеки – это, в данном случае, показатель возможностей этой библиотеки. А возможности у нее большие…
2. ICS – практически тоже самое, что и Indy. Чуть меньше возможностей, меньший размер и другой подход. Если Indy использует синхронный подход, то ICS – асинхронный. Что это значит. Синхронный – это когда Вы даете запрос и получаете ответ на него. При этом приложение приостанавливается, пока не выполниться запрос (если не использовать AntiFreeze), а асинхронный – это когда вы посылаете запрос, а ответ получаете в определенном событии. И тот и другой подход имеют свои плюсы и минусы…
3. SwinHTTP – это также асинхронный компонент. В этом компоненте реализованы только минимальные функции. Именно его я и использую в последнее время. Из минусов – в нем кроме транспортных функций нет ничего. Ни работы с куками, никаких дополнительных функций. Из плюсов – размер и простота работы.
Долго думал, на каком компоненте показывать, но решил остановиться на Indy, хотя SwinHTTP мне более приятен. Что-ж, поехали (далее будут приведены фрагменты из NoNaMe Post Creator, поэтому не стоит просто копировать их =))):
function TInetThread.DoLogin(const ALogin, APassword: string; AIsProxy: Boolean;
const AProxyAdress: string; AProxyPort: Integer; const AProxyLogin, AProxyPassword: string): Integer;
var
Data: TStringList;
ResStr: string;
begin
Result := -1;
with FHTTP do
if AIsProxy then
begin
ProxyParams.ProxyServer := AProxyAdress;
ProxyParams.ProxyPort := AProxyPort;
ProxyParams.ProxyUsername := AProxyLogin;
ProxyParams.ProxyPassword := AProxyPassword;
end else
begin
ProxyParams.ProxyServer := '';
ProxyParams.ProxyPort := 80;
ProxyParams.ProxyUsername := '';
ProxyParams.ProxyPassword := '';
end;
if ATimeOut <= 0 then ATimeOut := 30;
Data := TStringList.Create;
try
Data.Text := C_Login + '=' + ALogin + '&' + C_Password + '=' + APassword;
try
ResStr := FHTTP.Post(pg_NNM, Data);
if (ResStr = '') or (Pos(C_UnLogin, ResStr) > 0) then Result := 1;
except
Result := 6;
end;
finally
Data.Free;
end;
end;
Это функция авторизации на nnm.ru. Надеюсь тут ничего объяснять не стоит. Идем дальше…
function TInetThread.GetDoc(const AUrl: string; out ADoc: Integer): Integer;
var
sVal, ResStr: string;
Idx: Integer;
lUrl: string;
Data: TStringList;
begin
Result := -1;
Data := TStringList.Create;
try
ADoc := 0;
lUrl := AUrl;
if lUrl[Length(lUrl)] <> '/' then lUrl := lUrl + '/';
lUrl := lUrl + C_AddNew;
ResStr := FHTTP.Get(lUrl);
if (ResStr = '') or (Pos(C_Doc, ResStr) <= 0) or (Pos(C_AddForm, ResStr) <= 0) then Result := 2;
if Result < 0 then
begin
Idx := Pos(C_Doc, ResStr);
if Idx > 0 then
begin
Inc(Idx, Length(C_Doc));
sVal := '';
while (Idx < Length(ResStr)) and (ResStr[Idx] <>'"') do
begin
sVal := sVal + ResStr[Idx];
Inc(Idx);
end;
ADoc := StrToIntDef(sVal, 0);
end;
end;
finally
Data.Free;
end;
end;
А вот это как-раз то, что я не стал описывать выше. В этой функции мы получаем код дока, в который хотим запостить новость. Если будут вопросы – милости прошу в коменты.
Долго думал, надо ли приводить следующую функцию или нет, но решил, что надо… Хотя на данный момент, мне так кажеться, она лишняя. Итак, следующая функция – отправка изображения и получение урл-а этого изображения на сайте:
function TInetThread.PostHDDImage(const ADoc, APath: string; out AUrl: string): Integer;
var
lData: TIdMultiPartFormDataStream;
lResStr, lRes: string;
lUrl, lPref: string;
Idx: Integer;
begin
Result := -1;
AUrl := '';
lData := TIdMultiPartFormDataStream.Create;
try
lData.AddFile(C_HDDImage, APath, GetMIMETypeFromFile(APath));
lUrl := ADoc;
if lUrl[Length(lUrl)] <> '/' then lUrl := lUrl + '/';
lUrl := lUrl + C_AddImage;
lResStr := FHTTP.Post(lUrl, lData);
if (lResStr <> '') and (Pos(C_SParseImg, LowerCase(lResStr)) > 0) then
begin
lRes := '';
Idx := Pos(C_SParseImg, LowerCase(lResStr)) + Length(C_SParseImg) + 1;
while lResStr[Idx] <> '''' do
begin
lRes := lRes + lResStr[Idx];
Inc(Idx);
end;
lPref := '';
Inc(Idx, 4);
while lResStr[Idx] <> '''' do
begin
lPref := lPref + lResStr[Idx];
Inc(Idx);
end;
if (lRes = '') or (lPref = '')
then Result := 4
else AUrl := lPref + ':' + lRes;
end else Result := 3;
finally
lData.Free;
end;
end;
Ну и на последок – функция, которая отправляет саму новость на сайт (включая изображения в новости). Использовать ее напрямую у Вас не выйдет, придется немного подумать самим =):
procedure TInetThread.Execute;
var
lDocID: Integer;
lTxt: string;
lData: TStream;
lUrl: string;
lCat: Integer;
lResStr: string;
lStr: string;
begin
if (FHandle > 0) and (WM_StartPost > 0) then PostMessage(FHandle, WM_StartPost, 0, 0);
try
FHTTP.ReadTimeout := 1000 * ATimeOut;
FHTTP.Request.Referer := pg_NNM;
FHTTP.AllowCookies := True;
FHTTP.CookieManager.CookieCollection.Clear;
PostMessage(FHandle, WM_StatusText, Integer(PChar(str_Auths)), Length(str_Auths));
FResult := DoLogin(ALogin, APassword, AIsProxy, AProxyAdress, AProxyPort, AProxyLogin, AProxyPassword);
if FResult >= 0 then Exit;
PostMessage(FHandle, WM_StatusText, Integer(PChar(str_GetDocs)), Length(str_GetDocs));
FResult := GetDoc(ADoc, lDocID);
if FResult >= 0 then Exit;
lTxt := AText;
PostMessage(FHandle, WM_StatusText, Integer(PChar(str_StartImages)), Length(str_StartImages));
FResult := DoImages(ADoc, lTxt);
if FResult >= 0 then Exit;
PostMessage(FHandle, WM_StatusText, Integer(PChar(str_DoText)), Length(str_DoText));
lTxt := StringReplace(lTxt, delsym, newsym, [rfReplaceAll]);
ACaption := StringReplace(ACaption, delsym, newsym, [rfReplaceAll]);
AInLink := StringReplace(AInLink, delsym, newsym, [rfReplaceAll]);
FHTTP.Host := 'www.nnm.ru';
lUrl := ADoc;
if lUrl[Length(lUrl)] <> '/' then lUrl := lUrl + '/';
lUrl := lUrl + C_AddNew;
FHTTP.Request.Referer := lUrl;
FHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
FHTTP.Request.AcceptCharSet := 'windows-1251,utf-8;q=0.7,*;q=0.7';
//FHTTP.Request.AcceptEncoding := 'gzip,deflate';
FHTTP.Request.AcceptLanguage := 'ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3';
FHTTP.Request.Accept := 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5';
lStr := C_Caption + '=' + ACaption + '&' + C_Text + '=' + lTxt + '&' + C_Link + '=' + AInLink;
if ACategory <> '' then
begin
lCat := -1;
if ACategory = 'Newz' then lCat := 1 else
if ACategory = 'Filez' then lCat := 2 else
if ACategory = 'Mobilz' then lCat := 3 else
if ACategory = 'Music' then lCat := 9 else
if ACategory = 'Palmz' then lCat := 10 else
if ACategory = 'Gamez' then lCat := 12 else
if ACategory = 'Ironz' then lCat := 13 else
if ACategory = 'Webz' then lCat := 14 else
if ACategory = 'Creative' then lCat := 15 else
if ACategory = 'Video' then lCat := 16 else
if ACategory = 'Humor' then lCat := 17 else
if ACategory = 'Other' then lCat := 18;
if lCat > 0 then lStr := lStr + '&' + C_Category + '=' + IntToStr(lCat) +
'&' + C_IsCategory + '=1';
end;
lStr := lStr + '&vopr=&otv1=&otv2=&otv3=&otv4=&otv5=&otv6=&otv7=&otv8=&otv9=';
lStr := lStr + '&' + C_InDoc + '=' + IntToStr(lDocID);
lStr := lStr + '&' + C_Login + '=' + ALogin;
lStr := lStr + '&' + C_Password + '=' + APassword;
lStr := lStr + '&' + C_NoSex + '=1';
lData := TStringStream.Create(lStr);
try
FHTTP.HTTPOptions := [hoForceEncodeParams];
lResStr := FHTTP.Post(C_PostNews, lData);
FHTTP.HTTPOptions := [];
if lResStr = '' then FResult := 5;
finally
lData.Free;
end;
finally
if (FHandle > 0) and (WM_StartPost > 0) then
begin
if FResult < 0 then FResult := 0;
PostMessage(FHandle, WM_EndPost, FResult, 0);
end;
end;
end;
Ну вот пожалуй и все… Итак, практически половину программы привел =). Есть вопросы, просьбы, предложения – в коменты или в личку…
[cut]
P.S.: наверное некоторые будут удивлены, почему не представлен собранный демонстрационный код… На это есть несколько причин. Во-первых, такую программу я уже выкладывал =))) – NoNaMe Post Editor. Во-вторых, мне кажеться, что для того, чтобы научиться – надо делать самому. Не важно, получается или нет сразу. Не важно, насколько красивый и чистый код будет в начале. Важно – делать до тех пор, пока не будет работать.
С уважением, aktuba.