【问题标题】:HTML Tag ParsingHTML 标签解析
【发布时间】:2013-01-15 22:46:00
【问题描述】:

如何使用 DIHtmlParser 从标签中解析 Name: & Value 文本?我尝试使用 Clever Components 的 TCLHtmlParser 进行此操作,但失败了。第二个问题是 DIHtmlParser 是否可以解析单个标签,例如遍历其子标签。对于这样一个简单的问题,这简直就是一场噩梦。

<div class="tvRow tvFirst hasLabel tvFirst" title="example1">
  <label class="tvLabel">Name:</label>
  <span class="tvValue">Value</span>
<div class="clear"></div></div>

<div class="tvRow tvFirst hasLabel tvFirst" title="example2">
  <label class="tvLabel">Name:</label>
  <span class="tvValue">Value</span>
<div class="clear"></div></div>

【问题讨论】:

  • 欢迎来到 StackOverflow。 AFAIK 没有将 HTML 转换为 JSON 的标准方法。如果您期望得到有用的答案,请编辑您的问题以使其更准确,并提供一些示例来说明您要完成的工作,因为在当前状态下,您的问题过于宽泛,需要结束。
  • 在我看来,一旦你能够解析一个 XHTML 文档,这样它就可以无损地转换为 JSON 文档,你实际上并没有 不再需要 JSON。只需使用 XHTML 解释器直接生成的任何结构。那时,您不需要 HTML-to-JSON 转换器;您只需要一个可以让您以编程方式访问文档的 HTML 库。
  • 抱歉打破了你的气泡,但是如果你要从HTML转换成JSON,你必须先解析HTML。只有转换完成后才能开始解析 JSON。因此,将 HTML 转为 JSON 再解析 JSON 的速度总是会比自己解析 HTML 的速度慢。
  • @t0xic 使用 HTML 解析器。正则表达式不能用于解析 HTML。甚至 Jon Skeet 也无法使用正则表达式等解析 HTML。stackoverflow.com/questions/1732348/…
  • @t0xic,提取标签本身是困难的部分:一旦获得标签,您就可以轻松提取属性。但是不要自欺欺人,你真的不能用 RegEx 解析 HTML:你很难猜测你看到的标签是否在评论中,或者它可能是无效的,因为它永远不会关闭。这就是为什么我说它取决于输入。如果您想在特定时间从特定网站抓取特定信息,您可以使用 RegEx。如果它变得更复杂,那么您将需要您的解析器。

标签: html delphi delphi-xe2


【解决方案1】:

您可以使用IHTMLDocument2 DOM 从 HTML 中解析您需要的任何元素:

uses ActiveX, MSHTML;

const
  HTML =
  '<div class="tvRow tvFirst hasLabel tvFirst" title="example1">' +
  '<label class="tvLabel">Name:</label>' +
  '<span class="tvValue">Value</span>' +
  '<div class="clear"></div>' +
  '</div>';

procedure TForm1.Button1Click(Sender: TObject);
var
  doc: OleVariant;
  el: OleVariant;
  i: Integer;
begin
  doc := coHTMLDocument.Create as IHTMLDocument2;
  doc.write(HTML);
  doc.close;
  ShowMessage(doc.body.innerHTML);
  for i := 0 to doc.body.all.length - 1 do
  begin
    el := doc.body.all.item(i);
    if (el.tagName = 'LABEL') and (el.className = 'tvLabel') then
      ShowMessage(el.innerText);
    if (el.tagName = 'SPAN') and (el.className = 'tvValue') then
      ShowMessage(el.innerText);
  end;
end;

我想提一下我今天发现的另一个非常好的 HTML 解析器:htmlp(Delphi Dom HTML Parser and Converter)。它显然不如IHTMLDocument2 灵活,但它非常易于使用、快速、免费,并且支持旧版 Delphi 的 Unicode。

示例用法:

uses HtmlParser, DomCore;

function GetDocBody(HtmlDoc: TDocument): TElement;
var
  i: integer;
  node: TNode;
begin
  Result := nil;
  for i := 0 to HtmlDoc.documentElement.childNodes.length - 1 do
  begin
    node := HtmlDoc.documentElement.childNodes.item(i);
    if node.nodeName = 'body' then
    begin
      Result := node as TElement;
      Break;
    end;
  end;
end;

procedure THTMLForm.Button2Click(Sender: TObject);
var
  HtmlParser: THtmlParser;
  HtmlDoc: TDocument;
  i: Integer;
  body, el: TElement;
  node: TNode;
begin
  HtmlParser := THtmlParser.Create;
  try
    HtmlDoc := HtmlParser.parseString(HTML);
    try
      body := GetDocBody(HtmlDoc);
      if Assigned(body) then
        for i := 0 to body.childNodes.length - 1 do
        begin
          node := body.childNodes.item(i);
          if (node is TElement) then
          begin
            el := node as TElement;
            if (el.tagName = 'div') and (el.GetAttribute('class') = 'tvRow tvFirst hasLabel tvFirst') then
            begin
              // iterate el.childNodes here...
              ShowMessage(IntToStr(el.childNodes.length));
            end;
          end;
        end;
    finally
      HtmlDoc.Free;
    end;
  finally
    HtmlParser.Free
  end;
end;

【讨论】:

  • @T0xic:确定它会在线程中工作,别忘了先调用 CoInitialize...
【解决方案2】:

使用 HTML 解析器处理您的 html 文件。

也许DIHtmlParser 可以胜任。

RegEx 不是解析器,从 HTML 转换为 JSON 不是明智的选择。

【讨论】:

  • DIHtmlParser?它是一个非常复杂的解析器。与 Clever Components 的 TCLHtmlParser 完全不同。我试图使用它,它是一场噩梦。能力确定,但可用性?可怕..
【解决方案3】:

也可以将HTMLP parser 与 THtmlFormatter 和OXml XPath parsing 结合使用

uses
  // Htmlp
  HtmlParser,
  DomCore,
  Formatter,
  // OXml
  OXmlPDOM,
  OXmlUtils;

function HtmlToXHtml(const Html: string): string;
var
  HtmlParser: THtmlParser;
  HtmlDoc: TDocument;
  Formatter: THtmlFormatter;
begin
  HtmlParser := THtmlParser.Create;
  try
    HtmlDoc := HtmlParser.ParseString(Html);
    try
      Formatter := THtmlFormatter.Create;
      try
        Result := Formatter.GetText(HtmlDoc);
      finally
        Formatter.Free;
      end;
    finally
      HtmlDoc.Free;
    end;
  finally
    HtmlParser.Free;
  end;
end;

type
  TCard = record
    Store: string;
    Quality: string;
    Quantity: string;
    Price: string;
  end;
  TCards = array of TCard;

function ParseCard(const Node: PXMLNode): TCard;
const
  StoreXPath = 'div[1]/ax';
  QualityXPath = 'div[3]';
  QuantityXPath = 'div[4]';
  PriceXPath = 'div[5]';
var
  CurrentNode: PXMLNode;
begin
  Result := Default(TCard);
  if Node.SelectNode(StoreXPath, CurrentNode) then
     Result.Store := CurrentNode.Text;
  if Node.SelectNode(QualityXPath, CurrentNode) then
     Result.Quality := CurrentNode.Text;
  if Node.SelectNode(QuantityXPath, CurrentNode) then
     Result.Quantity := CurrentNode.Text;
  if Node.SelectNode(PriceXPath, CurrentNode) then
     Result.Price := CurrentNode.Text;
end;

procedure THTMLForm.OpenButtonClick(Sender: TObject);
var
  Html: string;
  Xml: string;
  FXmlDocument: IXMLDocument;
  QueryNode: PXMLNode;
  XPath: string;
  NodeList: IXMLNodeList;
  i: Integer;
  Card: TCard;
begin
  Html := System.IOUtils.TFile.ReadAllText(FileNameEdit.Text, TEncoding.UTF8);
  Xml := HtmlToXHtml(Html);
  Memo.Lines.Text := Xml;

  // Parse with XPath
  FXMLDocument := CreateXMLDoc;
  FXMLDocument.WriterSettings.IndentType := itIndent;
  if not FXMLDocument.LoadFromXML(Xml) then
    raise Exception.Create('Source document is not valid');
  QueryNode := FXmlDocument.DocumentElement;
  XPath := '//div[@class="row pricetableline"]';
  NodeList := QueryNode.SelectNodes(XPath);
  for i := 0 to NodeList.Count -1 do
  begin
    Card := ParseCard(NodeList[i]);
    Memo.Lines.Text := Memo.Lines.Text + sLineBreak +
      Format('%0:s %1:s %2:s %3:s', [Card.Store, Card.Quality, Card.Quantity, Card.Price]);
  end;

  Memo.SelStart := 0;
  Memo.SelLength := 0;
end;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 2016-05-06
    • 2012-10-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 2014-06-06
    相关资源
    最近更新 更多