HTMLParser.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:8k
源码类别:

Email服务器

开发平台:

Delphi

  1. unit HtmlParser;
  2. interface
  3. uses
  4.   DomCore, HtmlReader, HtmlTags;
  5. type
  6.   THtmlParser = class
  7.   private
  8.     FHtmlDocument: TDocument;
  9.     FHtmlReader: THtmlReader;
  10.     FCurrentNode: TNode;
  11.     FCurrentTag: THtmlTag;
  12.     function FindDefParent: TElement;
  13.     function FindParent: TElement;
  14.     function FindParentElement(tagList: THtmlTagSet): TElement;
  15.     function FindTableParent: TElement;
  16.     function FindThisElement: TElement;
  17.     function GetMainElement(const tagName: TDomString): TElement;
  18.     procedure ProcessAttributeEnd(Sender: TObject);
  19.     procedure ProcessAttributeStart(Sender: TObject);
  20.     procedure ProcessCDataSection(Sender: TObject);
  21.     procedure ProcessComment(Sender: TObject);
  22.     procedure ProcessDocType(Sender: TObject);
  23.     procedure ProcessElementEnd(Sender: TObject);
  24.     procedure ProcessElementStart(Sender: TObject);
  25.     procedure ProcessEndElement(Sender: TObject);
  26.     procedure ProcessEntityReference(Sender: TObject);
  27.     procedure ProcessTextNode(Sender: TObject);
  28.   public
  29.     constructor Create;
  30.     destructor Destroy; override;
  31.     function parseString(const htmlStr: TDomString): TDocument;
  32.     property HtmlDocument: TDocument read FHtmlDocument;
  33.   end;
  34. implementation
  35. const
  36.   htmlTagName = 'html';
  37.   headTagName = 'head';
  38.   bodyTagName = 'body';
  39. constructor THtmlParser.Create;
  40. begin
  41.   inherited Create;
  42.   FHtmlReader := THtmlReader.Create;
  43.   with FHtmlReader do
  44.   begin
  45.     OnAttributeEnd := ProcessAttributeEnd;
  46.     OnAttributeStart := ProcessAttributeStart;
  47.     OnCDataSection := ProcessCDataSection;
  48.     OnComment := ProcessComment;
  49.     OnDocType := ProcessDocType;
  50.     OnElementEnd := ProcessElementEnd;
  51.     OnElementStart := ProcessElementStart;
  52.     OnEndElement := ProcessEndElement;
  53.     OnEntityReference := ProcessEntityReference;
  54.     //OnNotation := ProcessNotation;
  55.     //OnProcessingInstruction := ProcessProcessingInstruction;
  56.     OnTextNode := ProcessTextNode;
  57.   end
  58. end;
  59. destructor THtmlParser.Destroy;
  60. begin
  61.   FHtmlReader.Free;
  62.   inherited Destroy
  63. end;
  64. function THtmlParser.FindDefParent: TElement;
  65. begin
  66.   if FCurrentTag.Number in [HEAD_TAG, BODY_TAG] then
  67.     Result := FHtmlDocument.appendChild(FHtmlDocument.createElement(htmlTagName)) as TElement
  68.   else
  69.   if FCurrentTag.Number in HeadTags then
  70.     Result := GetMainElement(headTagName)
  71.   else
  72.     Result := GetMainElement(bodyTagName)
  73. end;
  74. function THtmlParser.FindParent: TElement;
  75. begin
  76.   if (FCurrentTag.Number = P_TAG) or (FCurrentTag.Number in BlockTags) then
  77.     Result := FindParentElement(BlockParentTags)
  78.   else
  79.   if FCurrentTag.Number = LI_TAG then
  80.     Result := FindParentElement(ListItemParentTags)
  81.   else
  82.   if FCurrentTag.Number in [DD_TAG, DT_TAG] then
  83.     Result := FindParentElement(DefItemParentTags)
  84.   else
  85.   if FCurrentTag.Number in [TD_TAG, TH_TAG] then
  86.     Result := FindParentElement(CellParentTags)
  87.   else
  88.   if FCurrentTag.Number = TR_TAG then
  89.     Result := FindParentElement(RowParentTags)
  90.   else
  91.   if FCurrentTag.Number = COL_TAG then
  92.     Result := FindParentElement(ColParentTags)
  93.   else
  94.   if FCurrentTag.Number in [COLGROUP_TAG, THEAD_TAG, TFOOT_TAG, TBODY_TAG] then
  95.     Result := FindParentElement(TableSectionParentTags)
  96.   else
  97.   if FCurrentTag.Number = TABLE_TAG then
  98.     Result := FindTableParent
  99.   else
  100.   if FCurrentTag.Number = OPTION_TAG then
  101.     Result := FindParentElement(OptionParentTags)
  102.   else
  103.   if FCurrentTag.Number in [HEAD_TAG, BODY_TAG] then
  104.     Result := FHtmlDocument.documentElement as TElement
  105.   else
  106.     Result := nil;
  107.   if Result = nil then
  108.     Result := FindDefParent
  109. end;
  110. function THtmlParser.FindParentElement(tagList: THtmlTagSet): TElement;
  111. var
  112.   Node: TNode;      
  113.   HtmlTag: THtmlTag;
  114. begin
  115.   Node := FCurrentNode;
  116.   while Node.nodeType = ELEMENT_NODE do
  117.   begin
  118.     HtmlTag := HtmlTagList.GetTagByName(Node.nodeName);
  119.     if HtmlTag.Number in tagList then
  120.     begin
  121.       Result := Node as TElement;
  122.       Exit
  123.     end;
  124.     Node := Node.parentNode
  125.   end;
  126.   Result := nil
  127. end;
  128. function THtmlParser.FindTableParent: TElement;
  129. var
  130.   Node: TNode;
  131.   HtmlTag: THtmlTag;
  132. begin
  133.   Node := FCurrentNode;
  134.   while Node.nodeType = ELEMENT_NODE do
  135.   begin
  136.     HtmlTag := HtmlTagList.GetTagByName(Node.nodeName);
  137.     if (HtmlTag.Number = TD_TAG) or (HtmlTag.Number in BlockTags) then
  138.     begin
  139.       Result := Node as TElement;
  140.       Exit
  141.     end;
  142.     Node := Node.parentNode
  143.   end;
  144.   Result := GetMainElement(bodyTagName)
  145. end;
  146. function THtmlParser.FindThisElement: TElement;
  147. var
  148.   Node: TNode;
  149. begin
  150.   Node := FCurrentNode;
  151.   while Node.nodeType = ELEMENT_NODE do
  152.   begin
  153.     Result := Node as TElement;
  154.     if Result.tagName = FHtmlReader.nodeName then
  155.       Exit;
  156.     Node := Node.parentNode
  157.   end;
  158.   Result := nil
  159. end;
  160. function THtmlParser.GetMainElement(const tagName: TDomString): TElement;
  161. var
  162.   child: TNode;
  163.   I: Integer;
  164. begin
  165.   if FHtmlDocument.documentElement = nil then
  166.     FHtmlDocument.appendChild(FHtmlDocument.createElement(htmlTagName));
  167.   for I := 0 to FHtmlDocument.documentElement.childNodes.length - 1 do
  168.   begin
  169.     child := FHtmlDocument.documentElement.childNodes.item(I);
  170.     if (child.nodeType = ELEMENT_NODE) and (child.nodeName = tagName) then
  171.     begin
  172.       Result := child as TElement;
  173.       Exit
  174.     end
  175.   end;
  176.   Result := FHtmlDocument.createElement(tagName);
  177.   FHtmlDocument.documentElement.appendChild(Result)
  178. end;
  179. procedure THtmlParser.ProcessAttributeEnd(Sender: TObject);
  180. begin
  181.   FCurrentNode := (FCurrentNode as TAttr).ownerElement
  182. end;
  183. procedure THtmlParser.ProcessAttributeStart(Sender: TObject);
  184. var
  185.   Attr: TAttr;
  186. begin
  187.   Attr := FHtmlDocument.createAttribute((Sender as THtmlReader).nodeName);
  188.   (FCurrentNode as TElement).setAttributeNode(Attr);
  189.   FCurrentNode := Attr
  190. end;
  191. procedure THtmlParser.ProcessCDataSection(Sender: TObject);
  192. var
  193.   CDataSection: TCDataSection;
  194. begin
  195.   CDataSection := FHtmlDocument.createCDATASection(FHtmlReader.nodeValue);
  196.   FCurrentNode.appendChild(CDataSection)
  197. end;
  198. procedure THtmlParser.ProcessComment(Sender: TObject);
  199. var
  200.   Comment: TComment;
  201. begin
  202.   Comment := FHtmlDocument.createComment(FHtmlReader.nodeValue);
  203.   FCurrentNode.appendChild(Comment)
  204. end;
  205. procedure THtmlParser.ProcessDocType(Sender: TObject);
  206. begin
  207.   with FHtmlReader do
  208.     FHtmlDocument.docType := DomImplementation.createDocumentType(nodeName, publicID, systemID);
  209. end;
  210. procedure THtmlParser.ProcessElementEnd(Sender: TObject);
  211. begin
  212.   if FHtmlReader.isEmptyElement or (FCurrentTag.Number in EmptyTags) then
  213.     FCurrentNode := FCurrentNode.parentNode;
  214.   FCurrentTag := nil
  215. end;
  216. procedure THtmlParser.ProcessElementStart(Sender: TObject);
  217. var
  218.   Element: TElement;
  219.   Parent: TNode;
  220. begin
  221.   FCurrentTag := HtmlTagList.GetTagByName(FHtmlReader.nodeName);
  222.   if FCurrentTag.Number in NeedFindParentTags + BlockTags then
  223.   begin
  224.     Parent := FindParent;
  225.     if not Assigned(Parent) then
  226.       raise DomException.Create(HIERARCHY_REQUEST_ERR);
  227.     FCurrentNode := Parent
  228.   end;
  229.   Element := FHtmlDocument.createElement(FHtmlReader.nodeName);
  230.   FCurrentNode.appendChild(Element);
  231.   FCurrentNode := Element
  232. end;
  233. procedure THtmlParser.ProcessEndElement(Sender: TObject);
  234. var
  235.   Element: TElement;
  236. begin
  237.   Element := FindThisElement;
  238.   if Assigned(Element) then
  239.     FCurrentNode := Element.parentNode
  240. {  else
  241.   if IsBlockTagName(FHtmlReader.nodeName) then
  242.     raise DomException.Create(HIERARCHY_REQUEST_ERR)}
  243. end;
  244. procedure THtmlParser.ProcessEntityReference(Sender: TObject);
  245. var
  246.   EntityReference: TEntityReference;
  247. begin
  248.   EntityReference := FHtmlDocument.createEntityReference(FHtmlReader.nodeName);
  249.   FCurrentNode.appendChild(EntityReference)
  250. end;
  251. procedure THtmlParser.ProcessTextNode(Sender: TObject);
  252. var
  253.   TextNode: TTextNode;
  254. begin
  255.   TextNode := FHtmlDocument.createTextNode(FHtmlReader.nodeValue);
  256.   FCurrentNode.appendChild(TextNode)
  257. end;
  258. function THtmlParser.parseString(const htmlStr: TDomString): TDocument;
  259. begin
  260.   FHtmlReader.htmlStr := htmlStr;
  261.   FHtmlDocument := DomImplementation.createEmptyDocument(nil);
  262.   FCurrentNode := FHtmlDocument;
  263.   try
  264.   while FHtmlReader.Read do;
  265.   except
  266.     // TODO: Add event ?
  267.   end;
  268.   Result := FHtmlDocument
  269. end;
  270. end.