#html #parsing #delphi
Вопрос:
Иногда мне нужно анализировать такие данные, как эти:
<tr>
<td data-th="Name">
John Smith
</td>
<td data-th="Phone">
1234567
</td>
<td data-th="Postal">
16803
</td>
<td data-th="Office Number">
12345678
</td>
<td data-th="Remarks">
Hello
</td>
</tr>
<tr>
<td data-th="Name">
Mary Smith
</td>
<td data-th="Phone">
1234589
</td>
<td data-th="Postal">
16801
</td>
<td data-th="Office Number">
2385234
</td>
<td data-th="Remarks">
Hi There
</td>
</tr>
Я бы сделал что-то вроде загрузки этого в TStringList
:
for i := 0 to oStringList.Count-1 do
begin
if oStringList[i].Trim = '<tr>' then
begin
// start of record
end else if oStringList[i].Trim = '</tr>' then
begin
// end of record
end else
begin
// part of record data
end;
end;
Есть ли лучший способ сделать это, либо с помощью очень эффективного кода, либо уже существуют действительно хорошие компоненты Delphi (желательно бесплатные/с открытым исходным кодом), которые могут это сделать? Я видел поток (датированный более 3 лет назад) в stackoverflow, в котором упоминался компонент, просто интересно, появилось ли что-то лучшее.
Спасибо.
Обновление: попытка использовать компонент htmlp —> как настроить код для анализа приведенных выше данных… схематичный пример не помог. я хочу просмотреть каждый TR/TR и получить
var HtmlParser: THtmlParser;
var HtmlDoc: TDocument;
var x: Integer;
var body, el: TElement;
var node: TNode;
begin
HtmlParser := THtmlParser.Create;
try
HtmlDoc := HtmlParser.parseString(memo1.Text);
try
body := GetDocBody(HtmlDoc);
if Assigned(body) then
for x := 0 to body.childNodes.length - 1 do
begin
node := body.childNodes.item(x);
if (node is TElement) then
begin
el := node as TElement;
if (el.tagName = 'td') then //and (el.GetAttribute('data-th') = 'Name') then
begin
// iterate el.childNodes here...
//ShowMessage(IntToStr(el.childNodes.length));
memo1.Lines.Add(IntToStr(el.childNodes.length));
end else
begin
end;
end else
begin
memo1.Lines.Add('node is not element');
end;
end;
finally
HtmlDoc.Free;
end;
finally
HtmlParser.Free
end;
end;
Комментарии:
1. «Есть ли лучший способ сделать это[?]» Да, используйте синтаксический анализатор HTML. Однако мы не можем рекомендовать его, поскольку рекомендации по программному обеспечению явно запрещены правилами переполнения стека.
2. Тогда могу я спросить, является ли htmlp очень хорошим выбором, поскольку вы не можете рекомендовать?
3. Не ожидайте, что HTML будет состоять из нескольких строк — заполнение всего вместе без единого взлома строки является законным. Синтаксический анализатор (HTML), скорее всего, задохнется от синтаксических/логических ошибок (таких как ваши 2
</td>
в ряд), к которым вам, как человеку, было бы легко адаптироваться, но, с другой стороны, он также, скорее всего, поддерживает сущности (amp;<
) и многое другое, чего следует ожидать от HTML.4. Это правда. Я исправил двойника
</td>
. могу я спросить, есть ли у кого-нибудь опыт работы с синтаксическим анализатором htmlp, чтобы знать, как анализировать в этом случае?5. Зачем вам вообще нужен синтаксический анализ HTML?
Ответ №1:
Когда это хорошо сформированный HTML, подобный этому, где начальные записи также имеют конечные записи ( <TR>...</TR>
), тогда это в основном XML. Таким образом, вы можете использовать средство чтения XML для анализа документа.
Используя синтаксический анализатор XML kbmMW, как это:
const
HTML =
' <tr>'
' <td data-th="Name">'
' John Smith'
' </td>'
' <td data-th="Phone">'
' 1234567'
' </td>'
' <td data-th="Postal">'
' 16803'
' </td>'
' <td data-th="Office Number">'
' 12345678'
' </td>'
' <td data-th="Remarks">'
' Hello'
' </td>'
'</tr>'
'<tr>'
' <td data-th="Name">'
' Mary Smith'
' </td>'
' <td data-th="Phone">'
' 1234589'
' </td>'
' <td data-th="Postal">'
' 16801'
' </td>'
' <td data-th="Office Number">'
' 2385234'
' </td>'
' <td data-th="Remarks">'
' Hi There'
' </td>'
'</tr>';
procedure TForm1.Button1Click(Sender: TObject);
var
xml:TkbmMWDOMXML;
i,j:integer;
nTR,nTD:TkbmMWDOMXMLNodeList;
n1,n2:TkbmMWDOMXMLNode;
begin
Memo1.Clear;
xml:=TkbmMWDOMXML.Create(HTML);
try
nTR:=xml.Root.ChildrenByName['tr'];
try
for i:=0 to nTR.Count-1 do
begin
n1:=nTR.Nodes[i];
nTD:=n1.ChildrenByName['td'];
try
for j:=0 to nTD.Count-1 do
begin
n2:=nTD.Nodes[j];
Memo1.Lines.Add(n2.AttribByName['data-th'] '=' n2.Data);
end;
finally
nTD.Free;
end;
end;
finally
nTR.Free;
end;
finally
xml.Free;
end;
end;
Приводит к этому:
Name=John Smith
Phone=1234567
Postal=16803
Office Number=12345678
Remarks=Hello
Name=Mary Smith
Phone=1234589
Postal=16801
Office Number=2385234
Remarks=Hi There
Анализатор XML kbmMW включен, наряду со многим другим, в бесплатную версию сообщества, которую можно загрузить с https://portal.components4developers.com после регистрации.
Комментарии:
1. Разве html, который также является xml, не известен как xhtml
2. @DavidHeffernan Определенно да. HTML-это SGML, поэтому наиболее распространенным знаменателем является только часть «ML». HTML5 должно быть невозможно разобрать как XML, потому что отсутствующие закрывающие теги теперь легальны , поэтому этот ответ подразумевает HTML2 через 4 или XHTML1.
3. @питер Существует множество доступных синтаксических анализаторов XML, и многие из них имеют более разрешительные лицензии, чем анализатор Кима, который он продвигает здесь. Действительно, в стандартных библиотеках Delphi есть полнофункциональный синтаксический анализатор XML, которого должно быть более чем достаточно для удовлетворения ваших потребностей.
4. @KimMadsen мои извинения на днях, когда я увидел ваш ответ, сайт находился в процессе технического обслуживания, поэтому я не смог принять ответ как правильный.
5. @AmigoJack спасибо за ваш ответ