Первым делом посмотрела наработки Сергея (либу IngHTTPSend), предложенные им в комментарии к моей первой статье о Synapse. Безусловно, у него в обертку включены очень полезные функции (в том числе уделено большое внимание кодировкам). Но у каждого программиста есть еще ряд функций, которые он таскает с собой в голове и дописывает к любым своим либам :) Например, у меня есть некоторые заморочки по поводу хэдеров запросов. Я обычно создаю несколько функций для формирования заголовков с одинаковым названием, но с разным набором входных параметров. Например, в функцию может передаваться UserAgent, а может и не передаваться (если не передается, то берется случайный из списка возможных). Обработчики кодов ответа (в частности, 3XX) я тоже обычно выношу.
А потом пришла к выводу, что ни для чего "глубокомысленного" я сегодня не готова, глянула в списочек ссылок ресурсов, подходящих для демонстрации написания простейших парсеров, и решила поместить в блоге пример с кодом парсера вопросов с http://labs.wordtracker.com/keyword-questions/.
Ресурс Wordtracker.com я уже брала в качестве "подопытного" для иллюстрирования возможностей cURL.
Вопросы будем добывать с использованием Synapse.
Исследую запрос на сайте. Как видно, это обычный GET-запрос, с ним никаких сложностей не будет. Ввожу слово и исследую html-код для составления регулярки. Регулярные выражения я всегда тестирую с использованием самописной утилитки, о которой я уже писала на блоге.
Создаю простую форму, на которой Edit1 - для ввода ключевого слова, SG - TStringGrid для вывода результатов.
Листинг кода получается следующий:
procedure TMainF.btnGETClick(Sender: TObject);
var
HTTP : THTTPSend;
Res : boolean;
S : string;
RE : TRegExp;
mc : MatchCollection;
m : Match;
sm : SubMatches;
i : integer;
begin
HTTP := THTTPSend.Create;
HTTP.UserAgent:= GetRandomUseragent;
try
Res := HTTP.HTTPMethod('GET', 'http://labs.wordtracker.com/keyword-questions/questions?seed='+Edit1.Text+'&commit=Search');
if Res then
begin
case HTTP.Resultcode of
200 :
begin
S := StreamToString(HTTP.Document);
RE := TRegExp.Create(nil);
try
RE.Multiline := true;
RE.Global := true;
RE.IgnoreCase := true;
RE.Pattern := '<tr.*?>[\x0D\x0A]+<td.*?>[\x0D\x0A]+\d*[\x0D\x0A]+</td>[\x0D\x0A]+<td>(.*?)</td>[\x0D\x0A]+<td.*?>(\d*)</td>[\x0D\x0A]+</tr>';
mc := RE.Execute(S) as MatchCollection;
if mc.Count > 0 then begin
for i := 0 to mc.Count - 1 do
begin
m := mc[i] as Match;
sm := m.SubMatches as SubMatches;
SG.RowCount:=SG.RowCount+1;
SG.Cells[0,i+1] := VarToStr(sm[0]);
SG.Cells[1,i+1] := VarToStr(sm[1]);
end;
end;
finally
RE.Free;
end;
end;
end;
end
finally
HTTP.Free;
end;
end;
где:
function StreamToString(aStream: TStream): string;
var
SS: TStringStream;
begin
if aStream <> nil then
begin
SS := TStringStream.Create('');
try
aStream.Position := 0;
SS.CopyFrom(aStream, aStream.Size);
Result := SS.DataString;
finally
SS.Free;
end;
end else
begin
Result := '';
end;
end;
Результат представляется в виде конечного списка. Я вывела на форму. Вы можете сохранять в файл или куда угодно.
___
Кто бы что ни говорил, а составление регулярных выражений захватывает похлеще разгадываний судоку. :)
___
Продолжаю возвращать должки за упоминание RSSAdder-a (несмотря на то, что в последнее время заняться им все еще не получается). Сегодня благодарю за упоминания о нем авторов блогов Sibilev.net - Мишкины хроники и Maximum Seo.
___
Чтобы быть в курсе обновлений блога, можно подписаться на RSS.
В вашем методе форма будет подвисать. Надо выносить в отдельный поток
ОтветитьУдалить@Анонимный, это понятно. Я для себя не ставила задачи выкладывать на блоге идеальные готовые решения. Это прототип, просто основа для обучающей статьи. У кого будет желание - доработает по вкусу :)
ОтветитьУдалитьКлассно! Я два дня ломал голову - оказалось [\x0D\x0A] - решение.
ОтветитьУдалитьЕдинственно мне во многих местах пришлось "смазывать" сочетаниями ".*?" - иначе не работало.