•  

ГлавнаяIndyЧастые вопросы по Indy → У меня в программе создаётся огромное количество компонентов TWebBrowser

Создано: 16.05.2014 21:49:34 · Исправлено: 16.05.2014 21:49:34 · Прочтений: 1000

Доброе вдемя суток! Кратко: У меня в программе создаётся огромное количество компонентов TWebBrowser. (около 3500) Смысл: программка просматривает вебсайт, на котором содержится информация об организациях, в каждой из которых по алфавиту - списки сотрудников и их контактных данных. Я расшифровал строку запроса. Каждая страница открывается в отдельном экземпляре TWebBrowser. Затем, на каждый WebBrowserDocumentComplete я считываю данные со страницы и загоняю их в базу данных. После этого, мне этот экземпляр TWebBrowser не нужен и я его удаляю. Сделал вариант с динамическим массивом компонентов. В обработчике события WebBrowserDocumentComplete после нужных операций пишу: wb[TWebBrowser(ASender).Tag] := nil; wb[TWebBrowser(ASender).Tag].Free; Но память это никак не освобождает. Сделал вариант с обычным созданием компонент TWebBrowser, но после того, как в своём обработчике события WebBrowserDocumentComplete я вызываю TWebBrowser(ASender).Free; Выход из обработчика сопровождается exception. Я понимаю, что я удалил экземпляр компонента, а его процедура ещё осталась и после выполнения программе возвращаться некуда. Вопрос: Может кто-нибудь подскажет, как мне удалять компонент из массива так, чтобы он освобождал память или как мне после выполнения обработчика WebBrowserDocumentComplete безопасно удалить компонент? Как вариант: Я думаю создавать список хэндлов отработавших компонентов TWebBrowser и по таймеру уничтожать их, но мне кажется, что это слишком уж в обход.
А вы не пробовали использовать другие методы для закачивания содержимого страниц? Персонально я думаю что создавать 3500 ОЛЕ объектов с их всех мощной функциональностью - это уж слишком. Я бы лично создал нить в которой использовал компонент Инди или же Сынапс для закачки нужной информации(страницы) и запускал бы столько екземпляров нити сколько нужно.
wb[TWebBrowser(ASender).Tag] := nil; wb[TWebBrowser(ASender).Tag].Free; Но память это никак не освобождает. LOL. И не будет. Местами строчки поменяйте.
Вместо TWebBrowser используйте TIdHttp! А почему 3500?
По-моему, свыше 20 одновременных TCP соединений ни одно соединение, хоть выделенка, хоть (тем более!!!) диалап не потянут. Создавайте очередь, в нее втыкайте задания на закачку и несколько потоков, как только поток освобождается - он смотрит, нету ли еще заданий, если есть - стартует с новыми параметрами. Немного стоит подумать над тем, как быть, если число заданий растет (придется создавать новые потоки), но это решаемо, причем несложно.
ЛЮДИ! Это от непонимания или от нежелания понимать?! Зачем для скачки данных загружать броузер??? Вы что, боитесь, что не сможете понять суперсложный HTML и выдрать из него нужную информацию самостоятельно? ;-) Используем ОДИН TIdHTTP, скачиваем страничку, выдергиваем из нее нужную инфу (хотя бы регулярными выражениями), загружаем в БД, скачиваем следующую. Какие могут быть 3500(!) экземпляров TWebBrowser(!). Чтобы оценить юмор ситуации, предлагаю аналогию. В папке на локальном компьютере лежат 3500 файлов с фотографиями. Для переноса их в другую папку предлагается загрузить их все в фотошоп, а оттуда выполнить Save As :-D P.S. С фотками и фотошопом такого не видел, но встречал людей, которые подобным способом переносили винвордовские документы ;-)
Я извиняюсь, но с TidHTTP я не работал. Я вообще первый раз решаю подобного рода задачу. Просто программу необходимо было сделать в кротчайшие сроки - и я накидал как можно быстрее. Я поэтому и обратился за советом, чтобы узнать, как это правильно делается. Мне важно, чтобы я мог использовать механизмы библиотеки MSHTML_TLB. Сам документ (TWebBrowser.Document) я присваиваю переменной типа IHtmlDocument2. Ищу в нём нужную таблицу и присваиваю её переменной типа IHTMLTable. Далее работаю с этой таблицей. Вопрос: Используя TidHTTP я смогу получить сам документ, чтобы работать с тем же механизмом?
>>> Используем ОДИН TIdHTTP Не надо, используй нужное количество потоков, но не более 10. >>> подобным способом переносили винвордовские документы Предложите другим способом преобразовать документы из Word 2000 в Word 97 с переносом в другую папку :-) А мне - приходилось.
>>> Используя TidHTTP я смогу получить сам документ, чтобы работать с тем же механизмом? Используя TIdHTTP Вы сможете получить HTML-страничку, переданную сервером. На вопрос, сможете ли Вы сами выдернуть нужные данные из HTML-файла известной структуры, я ответить не в состоянии, так как это знаете только Вы. Но в принципе, это не такая уж и сверхсложная задача. >>> Не надо, используй нужное количество потоков, но не более 10. Это уже относится вообще к параллельной обработке, но не к специфике данной конкретной задачи. Хотя, возможно, имеет смысл распараллелить: пока одну страничку обрабатываем, другая закачивается. >>> Предложите другим способом преобразовать документы из Word 2000 в Word 97 с переносом в другую папку :-) А мне - приходилось. Мне тоже приходилось. Но в моем примере речь шла не о смене форматов, а об извращенном варианте копирования файлов. Просто люди не умели работать с проводником и не знали, как можно скопировать файл в другую папку.
>>> пока одну страничку обрабатываем, другая закачивается Время, затрачиваемое на ожидание соединения можно с успехом использовать для того, чтобы парсить другую страничку и качать третью. У меня, например, оффлайн-клиент Королевства закачивает данные в три потока - скорость выросла по сравнению с однопоточной загрузкой в 2-2,5 раза. Так что смысл в создании нескольких (небольшого количества) потоков все-же имеется.
У меня в программе создаётся огромное количество компонентов TWebBrowser. (около 3500) У меня была ситуация, когда при создании около 500 экземпляров TWebBrowser и закрытии программы система ругнулась Out of Resources :-)
>>> система ругнулась Out of Resources Вообще то, правильно сделала. Я слышал, что при попытке одновременного подключения к большому количеству ресурсов, система имеет право заблокировать Вас
В общем, основываясь на ваших ответах и перечитанным факам, сделал такой вариант: В конце отработки последнего обработчика события компонента TWebBrowser, я заношу Handle компонента TWebBrowser в список: HWNDtoFree.Add(IntToStr(TWebBrowser(ASender).Handle)); Затем, по таймеру (пока 3 сек.) я проверяю, если список HWNDtoFree не пустой, то читаю хэндлы и удаляю компоненты: if HWNDtoFree.Count > 0 then for I := 0 to HWNDtoFree.Count - 1 do begin TWebBrowser(FindControl(StrToInt(HWNDtoFree[i]))).Free; end; HWNDtoFree.Clear; обязательно выполняю: Finalize(wb[TWebBrowser(ASender).Tag]); где Tag хранит номер элемента массива. Бесконечный рост памяти остановлен.
В общем, основываясь на ваших ответах и перечитанным факам, сделал такой вариант: ... О, месье знает толк в извращениях :-D З.Ы. К сожалению, советы Bass_UA и Geo не были услышаны :-( З.З.Ы. Особенно понравился пример с Photoshop-ом :-)
О, месье знает толк в извращениях :-D З.Ы. К сожалению, советы Bass_UA и Geo не были услышаны :-( Обещаю, что после того, как руки дойдут до компонентов indy - переделаю. Тем более, я думаю, что одну функцию перековырять - много времени не отнимет.
как руки дойдут до компонентов indy - переделаю. Снимаю своё предложение Вместо TWebBrowser используйте TIdHttp! В Вашем сучае, похоже, ещё проще использовать виндовые Internet API функции. Как минимум появится независимость от сторонних компонентов.
Спасибо всем за ваши советы! Использовал Internet API для загрузки документа:
function DownloadFile(const Url: string): string;
var
  NetHandle: HINTERNET;
  UrlHandle: HINTERNET;
  Buffer: array[0..1024] of char;
  BytesRead: cardinal;
begin
  Result := ;
  NetHandle := InternetOpen(Delphi 11.x, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  if Assigned(NetHandle) then
  begin
    UrlHandle := InternetOpenUrl(NetHandle, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);
    if Assigned(UrlHandle) then
{ UrlHandle правильный? Начинаем загрузку }
      begin
        FillChar(Buffer, SizeOf(Buffer), 0);
        repeat
          Result := Result + Buffer;
          FillChar(Buffer, SizeOf(Buffer), 0);
          InternetReadFile(UrlHandle, @Buffer, SizeOf(Buffer), BytesRead);
        until BytesRead = 0;
        InternetCloseHandle(UrlHandle);
      end
    else
      begin
{ UrlHandle неправильный. Генерируем исключительную ситуацию. }
        raise Exception.CreateFmt(Невозможно открыть URL %s!, [Url]);
      end;
    InternetCloseHandle(NetHandle);
  end
  else
{ NetHandle недопустимый. Генерируем исключительную ситуацию }
  raise Exception.Create(Невозможно инициализировать Wininet!);
end;
Скорость и потребление ресурсов - радуют до безобразия. Записываю тело страницы в IHtmlDocument2:
uses ..., ActiveX, ...; // ActiveX - для PSafeArray

procedure Parse(address: string);
var
  ...
  HTMLDoc: IHtmlDocument2;
  ...
  v: variant;
  ...
begin
  v := VarArrayCreate([0, 0], varVariant);
  v[0] := DownloadFile(address);
  HTMLDoc := CoHTMLDocument.Create as IHTMLDocument2;
  HTMLDoc.write(PSafeArray(TVarData(v).VArray));
  {...Произвожу необходимые операции...}
end;
Не судите за ошибки. Первый вариант был написан за полчаса - что знал. Я ведь только учусь...
Осталось наплодить потоков (но умеренно; не 3500) и сделать докачку (а можно и не делать).
Наплодил! С условием: если потоков больше 20, то следующий создаётся через 2 секунды, а если потоков больше 35, то следующий создаётся через 3 секунды.
... и сделать докачку ... не стал, т.к. подключение к интернету у нас постоянное...