2013-01-15 11 views
13

Jak mogę zanalizować dane: Nazwa: & Wartość tekst z wewnątrz znacznika za pomocą DIHtmlParser? Próbowałem to zrobić z TCLHtmlParser z Clever Components, ale nie udało się. Drugie pytanie brzmi: czy DIHtmlParser może analizować poszczególne znaczniki, na przykład przechodząc przez swoje znaczniki podrzędne. To całkowity koszmar dla tak prostego problemu.Parsowanie znaczników HTML

<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> 
+0

Witamy StackOverflow. AFAIK nie ma standardowego sposobu konwersji HTML na JSON. Edytuj swoje pytanie, aby było bardziej precyzyjne i podaj kilka przykładów tego, co próbujesz osiągnąć, jeśli oczekujesz przydatnej odpowiedzi, ponieważ w obecnym stanie Twoje pytanie jest zbyt szerokie, a kandydat do zamknięcia. – jachguate

+3

Wydaje mi się, że gdy masz coś * zdolnego * do analizowania dokumentu XHTML, tak że można go bezstratnie konwertować do dokumentu JSON, w rzeczywistości * nie potrzebujesz * JSON. Po prostu użyj dowolnej struktury, którą interpreter XHTML wygenerował bezpośrednio. W tym momencie nie potrzebujesz konwertera HTML-to-JSON; potrzebujesz tylko biblioteki HTML, która pozwala programowo uzyskać dostęp do dokumentu. –

+0

@RobKennedy Parsowanie JSON jest szybsze niż XML lub HTML. Przepraszam za złamanie twojej bańki. :) –

Odpowiedz

14

Można użyć IHTMLDocument2 DOM do analizowania cokolwiek elementy trzeba z 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; 

chciałem wspomnieć o innym bardzo ładne HTML parsera znalazłem dzisiaj: htmlp (Delphi HTML DOM Parser i konwerter). Nie jest to oczywiście tak elastyczny jak IHTMLDocument2, ale jest bardzo łatwy w obsłudze, szybki, darmowy i obsługuje Unicode dla starszych wersji Delphi.

wykorzystanie próbki:

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; 
+0

czy to działa w wątku? –

+5

@ T0xic: na pewno zadziała w wątku, nie zapomnij zadzwonić CoInitialize pierwszy ... – whosrdaddy

+0

Ah tak. :) Dzięki! –

0

Użyj parsera HTML do pracy na plikach HTML.

Może wykona zadanie DIHtmlParser.

RegEx nie jest analizatorem składni i konwersja z HTML na JSON nie jest rozsądną opcją.

+1

DIHtmlParser? Jest to bardzo złożony parser. Nic takiego jak TCLHtmlParser od Clever Components. Próbowałem go użyć i to był koszmar. Zdolność do pewności, ale użyteczność? Straszne .. –

0

Można również użyć kombinacji HTMLP parser z THtmlFormatter i 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;