каждая из функций в результате должна возвращать Tstrings, каждый элемент которой будет содержать innerHTML той части html, которая удовлетворяет условию.
Но, на первой функции все остановилось:
function getElementsByAttr(iDoc: IHTMLDocument2; iTagName, iAttribute, iValue: String): TStrings;
var
iDisp: IDispatch;
iElement:IHTMLElement;
i:integer;
begin
getElementsByAttr := TStrings.Create();
if not Assigned(iDoc) then begin
getElementsByAttr.Clear;
Exit;
end;
for i:=1 to iDoc.All.Get_length do begin
iDisp := iDoc.Get_all.item(pred(i), 0);
iDisp.QueryInterface(IHTMLElement, iElement);
if Assigned(iElement) then
begin
If (iElement.Get_tagName = iTagName) And (iElement.getAttribute(iAttribute, 0) = iValue) Then
getElementsByAttr.Add(iElement.innerHTML);
end;
end;
end;
при использовании функции программа выдает Abstract Error.
Не могу понять в чем ошибка.
(или, может, я изобретаю велосипед и такие функции уже есть?)
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
28-06-2010 04:30
А чего их делать - регулярные выражения всегда решают все проблемы. А на кой использовать WebBrowser для парсинга мне не понятно совершенно. Сложно, а пользы практически никакой. Ну может поначалу кажется проще, а потом - регулярные выражения становятся столь просты, что про WebBrowser просто забываешь как про страшный сон. Не, для отображения он конечно хорош, а вот для парсинга ну никуда не годен.
WebBrowser не обяхательно использовать. Можно воспользоваться IHTMLDocument2 как уже указвали ранее.
Использовать регулярные выражения для парсинга в данном случае черевато ошиьками (сам так делал, признаю). Конкретнее почему НЕ надо использовать регекс здесь: http://stackoverflow.com/questions/701166 ( Can you provide some examples of why it is hard to parse XML and HTML with a regex? )
>>> парсингом html и не сделал для себя несколько удобных функций
А чего их делать - регулярные выражения всегда решают все проблемы. А на кой использовать WebBrowser для парсинга мне не понятно совершенно. Сложно, а пользы практически никакой. Ну может поначалу кажется проще, а потом - регулярные выражения становятся столь просты, что про WebBrowser просто забываешь как про страшный сон. Не, для отображения он конечно хорош, а вот для парсинга ну никуда не годен.
>>> насколько использование mshtml "утяжеляет" конечный файл
Я использую XMLDocument для парсинга XML и размер выходного файла вырос на 150 килобайт. В принципе, несущественно.
а нет ли у кого-нить готовых функций getElementsByAttr, getElementsByTagName, написанных без использования mshtml? насколько использование mshtml "утяжеляет" конечный файл?
говорит ли это о том, что её можно парсить как xml?
Насколько я понимаю, да. Т.е. вообще HTML - нестрогий, теги могут закрываться, а могут и нет, например у абзаца (параграфа) может быть закрывающий тег, а может и не быть, атрибуты не обязательно - в кавычках и т.д. В XHTML - всё строго, как в XML.
Есть ли в этом какие-нибудь преимущества?
На мой взгляд, нет. Для XML-парсера документ - просто древовидная структура. HTML-парсер из IE (mshtml.dll) знает о конкретном устройстве HTML страницы, что у нее есть body, title и т.д., знает о конкретных ее элементах.
Например, как я уже написал, можно сходу получить все ссылки на странице просто через свойство Document.Anchors.
А через интерфейс IHtmlAnchor можно получить практически всю информацию о ссылке. Это просто пример.
Если страница в заголовке содержит
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
говорит ли это о том, что её можно парсить как xml?
Есть ли в этом какие-нибудь преимущества?
05-03-2008 05:00 | Комментарий к предыдущим ответам
И еще, в Дельфи строчки
iDisp := iDoc.Get_all.item(pred(i), 0);
iDisp.QueryInterface(IHTMLElement, iElement);
можно заменить на
iElement := iDoc.Get_all.item(pred(i), 0) as IHTMLElement;
Код получается короче и имхо понятней.
05-03-2008 04:47 | Комментарий к предыдущим ответам
>>>Html не возможно распарсить ввиде дерева, его всегда выводят.
Html распарсить можно. Это делает например встроенный в IE парсер из mshtml.dll, которым пользуется автор вопроса.
Автору: советую изучить интерфейсы IHtmlDocument2 и IHtmlDocument2 из MSHTML_TLB. Там есть множество полезных функций. Интерфейс IHtmlDocument3 можно получить из переменной типа IHtmlDocument2 через оператор as.
Например в IHTMLDocument3:
function getElementsByName(const v: WideString): IHTMLElementCollection; safecall;
function getElementById(const v: WideString): IHTMLElement; safecall;
function getElementsByTagName(const v: WideString): IHTMLElementCollection; safecall;
property documentElement: IHTMLElement read Get_documentElement;
Html не возможно распарсить ввиде дерева, его всегда выводят. А под парсингом подразумевается нахождение определенных значений. Это делается средствами регулярных выражений. А зачем вам это потребовалось не понятно.
function getElementsByAttr(iDoc: IHTMLDocument2; iTagName, iAttribute, iValue: String): TStringList;
var
iDisp: IDispatch;
iElement:IHTMLElement;
i:integer;
begin
getElementsByAttr := TStringList.Create();
if not Assigned(iDoc) then begin
getElementsByAttr.Clear;
Exit;
end;
for i:=1 to iDoc.All.Get_length do
begin
iDisp := iDoc.Get_all.item(pred(i), 0);
iDisp.QueryInterface(IHTMLElement, iElement);
if Assigned(iElement) then
begin
If UpperCase(iElement.Get_tagName) = UpperCase(iTagName) Then
If iElement.getAttribute(iAttribute, 0) <> null then
If UpperCase(iElement.getAttribute(iAttribute, 0)) = UpperCase(iValue) Then
getElementsByAttr.Add(iElement.innerHTML);
end;
end;
end;
function getElementsByTagName(iDoc: IHTMLDocument2; iTagName: String): TStringList;
var
iDisp: IDispatch;
iElement:IHTMLElement;
i:integer;
begin
getElementsByTagName := TStringList.Create();
if not Assigned(iDoc) then begin
getElementsByTagName.Clear;
Exit;
end;
for i:=1 to iDoc.All.Get_length do
begin
iDisp := iDoc.Get_all.item(pred(i), 0);
iDisp.QueryInterface(IHTMLElement, iElement);
if Assigned(iElement) then
begin
If UpperCase(iElement.Get_tagName) = UpperCase(iTagName) Then
getElementsByTagName.Add(iElement.innerHTML);
end;
end;
end;
Хотелось бы в результате работе функций получать не TStringList, а IHTMLDocument2 снова, чтобы были возможны конструкции вида:
Кстати, обнаружил интересный комментарий к функции getAttribute в MSDN:
When retrieving the CLASS attribute using this method, set the strAttributeName to be "className"
(долго не мог понять почему же по запросу iElement.getAttribute('class', 0) ничего не возвращается)
03-03-2008 05:07 | Вопрос к автору: запрос дополнительной информации
Вы парсите XML? Тогда лучше импортировать MSXML_TLB и загружать туда фаил
var
XMLDOM: RXMLDOM;
PI: IXMLDOMProcessingInstruction;
begin
XMLDOM.IDoc := CoDOMDocument.Create;
XMLDOM.IDoc.Set_async(False);
PI := XMLDOM.IDoc.CreateProcessingInstruction('xml', 'version="1.0"');
XMLDOM.IDoc.appendChild(PI);
If (UpperCase(iElement.Get_tagName) = UpperCase(iTagName)) And (UpperCase(iElement.getAttribute(iAttribute, 0)) = UpperCase(iValue)) Then
(чтоб регистр не учитывался), вроде стало более-менее работать. Тем не менее, почему-то на этой строке иногда появляется ошибка -
Couldn't convert variant of type (Null) into type (String)
это getAttribute что ли возвращает null?
и, пока еще не понял, какова глубина работы моей функции...
т.е. если запрос таков
getElementsByAttr("div", "class", "text") и html содержит текст вида:
<div class="text">
some text 1...
<div class="text">
some text 2...
</div>
</div>
то моя функция должна вернуть мне две строки или одну?
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.