*提取网页信息,兼容HTML 4.01 / XHTML 1.0
*测试页面代码来源:http://magiccards.info/m12/cn/3.html
更新记录
v0.5.3-1.1 20110831
@目标:标签领地arrTagManor存储标签索,并能快速定位(深度+标签结构)。
#重新定义intInfoType,并加入显示模式选择modType。
/// 0:内容 /// 1: /// 2: /// 3: /// 4:段落模式 /// 5:段落模式 /// 6:段落模式 /// 7:独行模式 /// 8:独行模式 /// 9:独行模式
v0.5.3-1.0 20110830
#优化intErrType信息提示方式。
#新增arrTag
6:独行模式 0+6:普通的标签<…> <…/>
#新增intInfoType,独行模式
6 : 独行模式 0+6:普通的标签<…> <…/>
7 : 独行模式 1+6:
v0.5.3 20110822
#修正0.5.2的深度错误。
#优化intInfoType,值如下
0 : 1 : 3 : 段模式下的 内容 4 : 段模式下的 9 : 不显示的标签 0+9:不显示
#更新arrTag索引
0:普通 1: 3:段落模式 0+3:普通的标签 5:段落模式 2+3:、、!DOCTYPE标签 9:不显示的 0+9:不显示
#增加intErrType,处理错误信息。
#新增boolNoInfo,修正在新逻辑下<标签>内容为空时的深度错误。
#修正最后产生空字符串的处理。
#修正<!–注释–>的处理。
#标签领地arrTagManor包括段落模式的<…>。
#暂时取消intInfoType:9不显示功能。
#暂时取消隐藏标签功能。
v0.5.2 20110821
#重写标签核对,更改成arrTag索引,取消段落模式可选。能够验证标签是否合法,并提速37%。
0:普通 1: 2: 3:段落模式 0+3:普通的标签 5:段落模式 2+3:标签 9:不显示的 0+9:不显示
#优化intLinefeedType,并显示行数。
#生成临时HTML展现结果代码,同时避免Delphi字符串包含&的特殊功能、自动转换&#编码。
#避免< >、< />、、<这里>等类似字符串容易被判定为标签。
#增加intInfoType值10,当标签不为合法时,则为10。
#深度值intDepth。
#修正标签领地arrTagManor。
#修正intInfoType,并解决最后会多出一行空内容。
#修正<标签>内容为空时的深度错误。
#更改boolBatter“连击状态”为intOldInfoType“标签类型”,记录之前标签的类型。
0:<标签> 1: 其他:段落模式
v0.5.1 20110815
#精确strTagName
#函数返回值改为TStringList
#更改并添加arrPartTag层级
0 不显示的标签,该标签内所有内容不显示 1 <…/> 2 段落模式的标签
#修正针对<!– –>的BUG
v0.5.0 20110814
#更新判断逻辑,关键结构只按照<和>。
#“自由段”改为“段落模式”,即可选择不换行,如a img br a标签带文本等
#不按照/>或/ >来判定不加深度,因为HTML 4.01下
是正确的写法。
#重写属性和标签名字判断部分
#重写<标签>内部判断逻辑,先判断类型,消除过多的多重IF。
#优化代码,提速33%-50%。
#“标签领地”概念,加快“自定义段”的标签在中的验证。
#“连击状态”概念,处理针对连续出现<标签>的#13关系。
#暂时取消检查标签的属性中带有 / 的合法性。
#暂时取消深度。
///内容类型判断 intInfoType////////////////////////////////////////////////////////////////////////
┌有<
│└┬虚内容<标签>
│ │└┬<标签>
│ │ │ └┬先处理<标签 / >右侧的空格
│ │ │ │默认<标签> //intInfoType:=0;
│ │ │ ├ //intInfoType:=9;
│ │ │ └ //intInfoType:=1;
│ │ │ └┬<标签/> //intInfoType:=2;
│ │ │ └自定义段 //intInfoType:=3;
│ │ └虚内容<标签> 提取虚内容 可能是最开始的。 //intInfoType:=7;
│ └虚内容>内容< │ └┬>内容< //intInfoType:=6; │ └虚内容+非标签的>+内容< //intInfoType:=7;
└>内容 最后的内容 //intInfoType:=8;
///////////////////////////////////////////////////////////////////////////////////////////////////
v0.4.1 20110808
#修正针对非规范写法代码的判断。如:<br > <br / > <br/ > <br>,规范的是<br />。
#提取标签名 strTagName
v0.4.0 20110807
#更换判断逻辑,只按照<、>、/判断
#针对标签的属性中带有 / 的合法进行判定。
待处理
@把Copy换成MidStr
@使用LowerCase UpperCase
@针对intErrType各个错误的补救办法。
@能够隐藏标签,直接显示内容。
@能够按照指定层级数显示。
@intInfoType:9不显示功能。
@支持、//、’等其他注释,支持样式表提取。
@结构出错时(如缺少)的处理方式。
@精确查找,减少提取的多余页面代码
@实战提取http://magiccards.info
@简化内容类型判断 intInfoType
@隐藏标签下…,里,会换行。
@针对原始页面代码中换行的部分进行修正
@对大小写的分辨
@遇到相同,如,即使td是段落模式,换行.
@支持对HTTP 消息的转换
@支持对属性的支持
@不显示标签后的内容要分段。
@减少arr for do的搜索时间
@按照指定格式进行显示 如 a href text
@对整个页面进行抓取,主要处理Head中的杂乱代码
@拆分标签中的属性及值
@判定HTML版本,并支持HTML5
源代码 v0.5.3 20110822
function aDismemberCode(strSource: string): TStringList; var //标签定位 intTagBef, intTagAft, intTagMark: Integer; //arrTag的定位 intTagNth, intTagAbc:integer; //内容类型,上一个内容类型,错误提示类型 intInfoType, intOldInfoType, intErrType: integer; //错误提示信息 strErrType: string; //内容,标签,标签名 strInfo, strTag, strTagName: string; //是标签,没有内容 boolIsTag, boolNoInfo: boolean; //换行模式,深度 intLinefeedType, intDepth: integer; arrTagManor, arrTagManorTemp: TStringList; //标签领地 不记录<.../> //boolTagHide: boolean; //隐藏标签 i:integer; const //////////////////////////////////////////////////////////////////////////// /// 0:普通 /// 1:</...> /// 3:段落模式 0+3:普通的标签 /// 5:段落模式 2+3:<.../>、<!---->、!DOCTYPE标签 /// 9:不显示的 0+9:不显示 arrTag : array[0..24,0..21] of string = (('!--','!DOCTYPE','','','','','','','','','', '5', '5', '','','','','','','','',''), ('a','area','abbr','acronym','address','applet','','','','','', '3', '5', '0', '0', '0', '0', '','','','',''), ('br','b','body','base','basefont','bdo','big','blockquote','button','','', '5', '3','0', '5', '5', '0', '0', '0', '0', '',''), ('col','caption','center','cite','code','colgroup','','','','','', '5', '0', '0', '0', '0', '0', '','','','',''), ('div','dd','dl','dt','del','dir','dfn','','','','', '0', '0', '0', '0', '0', '0', '0', '','','',''), ('em','','','','','','','','','','', '0', '','','','','','','','','',''), ('font','form','frame','frameset','fieldset','','','','','','', '0', '0', '5', '0', '0', '','','','','',''), ('','','','','','','','','','','', '','','','','','','','','','',''), ('hr','html','head','h1','h2','h3','h4','h5','h6','','', '5', '0', '0', '0', '0', '0', '0', '0', '0', '',''), ('img','input','i','iframe','ins','isindex','','','','','', '5', '5', '3','0', '0', '0', '','','','',''), ('','','','','','','','','','','', '','','','','','','','','','',''), ('kbd','','','','','','','','','','', '0', '','','','','','','','','',''), ('li','label','link','legend','','','','','','','', '3', '3', '5', '0', '','','','','','',''), ('meta','map','menu','','','','','','','','', '5', '0', '0', '','','','','','','',''), ('noframes','noscript','','','','','','','','','', '0', '0', '','','','','','','','',''), ('option','optgroup','object','ol','','','','','','','', '3', '3', '0', '0', '','','','','','',''), ('p','param','pre','','','','','','','','', '0','5', '0', '','','','','','','',''), ('q','','','','','','','','','','', '0','','','','','','','','','',''), ('','','','','','','','','','','', '','','','','','','','','','',''), ('span','style','strong','script','select','small','s','samp','strike','sub','sup', '3', '0', '0', '0', '3', '3', '0','0', '0', '0', '0'), ('table','tbody','td','tr','thead','th','title','textarea','tfoot','tt','', '0', '0', '0', '0', '0', '0', '3', '0', '0', '0', ''), ('ul','u','','','','','','','','','', '0', '3','','','','','','','','',''), ('var','','','','','','','','','','', '0', '','','','','','','','','',''), ('','','','','','','','','','','', '','','','','','','','','','',''), ('xmp','','','','','','','','','','', '0', '','','','','','','','','','') ); begin //标签定位 intTagAft := 0; //arrTag的定位 intTagNth := 0; intTagAbc := 0; //内容类型,上一个内容类型,错误提示类型 intInfoType := 0; intOldInfoType := 0; intErrType := 0; //是标签,没有内容 // boolNoInfo := false; //换行模式,深度 intLinefeedType := 0; intDepth := -1; arrTagManor := TStringList.Create; //标签领地 arrTagManorTemp := TStringList.Create; //boolTagHide := false;//隐藏标签 Result := TStringList.Create; try repeat boolIsTag := false; // intInfoType intTagBef := AnsiPos('<', strSource); if intTagBef <> 0 then begin //< intTagAft := AnsiPos('>', strSource); if intTagBef < intTagAft then begin //虚内容<标签> if intTagBef = 1 then begin //<标签> boolIsTag := true; strTag := trim(copy(strSource, intTagBef+1, intTagAft-2)); // 去掉<、>、左右空格的标签 strTag := '<'+trim(strTag)+'>'; if AnsiPos('/', strTag) = 2 then begin strTagName := copy(strTag, 3, length(strTag)-3); if arrTagManor.Count > 0 then begin arrTagManorTemp.CommaText := arrTagManor[arrTagManor.Count-1]; if arrTagManorTemp[0] = strTagName then begin arrTagManor.Delete(arrTagManor.Count-1); if arrTagManorTemp[1] = '0' then intInfoType := 1 else intInfoType := 4; end else intErrType := 1; end else intErrType := 3; end else begin //strTagName intTagMark := AnsiPos(' ', strTag); if intTagMark <> 0 then strTagName := copy(strTag, 2, intTagMark-2) else begin intTagMark := AnsiPos('/', strTag); if intTagMark <> 0 then strTagName := copy(strTag, 2, intTagMark-2) else strTagName := copy(strTag, 2, AnsiPos('>', strTag)-2); end; //!特殊标签 if strTagName[1] <> '!' then intTagAbc := ord(strTagName[1])-96 //a 97-96=1 else begin intTagAbc := 0; //! 33 if AnsiPos('!--', strTagName) = 1 then strTagName := '!--'; end; //arrTag intErrType := 4; //只有符合arrTag才是合法标签 if (intTagAbc <= high(arrTag)) and (intTagAbc >= low(arrTag)) then //不超过a~x begin for intTagNth := low(arrTag[intTagAbc]) to high(arrTag[intTagAbc]) do begin if arrTag[intTagAbc][intTagNth] = strTagName then begin intInfoType := strtoint(arrTag[intTagAbc][intTagNth+11]); case intInfoType of 5 : intInfoType := 3; else arrTagManor.add(strTagName+','+inttostr(intInfoType)); end; intErrType := 0; break; end; end; end; end; end else intInfoType := 3; //虚内容<标签> 提取虚内容 可能是最开始的 end else intInfoType := 3; // >内容< 虚内容+非标签的>+内容< end else intInfoType := 3; //空内容处理 if boolNoInfo then begin if intInfoType = 1 then intDepth := intDepth+1; boolNoInfo := false; end; //内容处理 if boolIsTag then begin if strTagName <> '!--' then begin strInfo := '<'+copy(strSource, 2, intTagAft-2)+'>'; delete(strSource, 1, intTagAft-1); end else begin intTagAft := AnsiPos('-->', strSource); strInfo := '<'+copy(strSource, 2, intTagAft)+'>'; //处理注释里的代码可见,<、>其中一个即可 strInfo := StringReplace(strInfo, '<', '<', [rfReplaceAll]); delete(strSource, 1, intTagAft+1); end; end else begin if intTagBef <> 0 then begin strInfo := copy(strSource, 1, intTagBef-1); delete(strSource, 1, intTagBef-1); if strInfo[1] = '>' then delete(strInfo, 1, 1); //去掉> if trim(strInfo) = '' then //空内容处理 begin if intOldInfoType in [0] then boolNoInfo := true; continue; end; end else begin //>内容结束 delete(strSource, 1, 1); strInfo := strSource; if trim(strInfo) = '' then continue; //</标签>结尾 end; end; strInfo:=trim(strInfo); //intInfoType 深度、段落、结构 case intInfoType of 0 : begin //<...> <.../> intLinefeedType := 1; if intOldInfoType = 0 then intDepth := intDepth+1; //if intOldInfoType = 3 then intErrType := 2; end; 1 : begin //</...> if intInfoType <> 3 then begin //非段落<...> intLinefeedType := 1; intDepth := intDepth-1; end else intErrType := 1; end; 3 : begin //段模式下的 <...> <.../> 内容 if intOldInfoType in [3,4] then //是段落模式 intLinefeedType := 0 else begin intLinefeedType := 1; if intOldInfoType = 0 then intDepth := intDepth+1; //是<...> end; end; 4 : begin //段模式下的 </...> if intOldInfoType in [3,4] then //是段落模式 intLinefeedType := 0 else begin if intInfoType <> 0 then begin intLinefeedType := 1; intDepth := intDepth-1; end else intErrType:=1; end; end; 9 : begin //不显示的标签 0+9:不显示 //continue; end else intErrType := 11; end; intOldInfoType := intInfoType; //上一个内容类型记录 //intErrType case intErrType of 0 : begin //没有错误; end; 1 : begin strErrType := '结构出错,需要</'+arrTagManorTemp[0]+'>'+'但结构中是</'+strTagName+'>'+#13+'第'+inttostr(Result.Count)+'行'; end; 2 : begin strErrType := '段内交错!'; end; 3 : begin strErrType := '</...>在<...>前或数量比对应<...>多'; end; 4 : begin strErrType := strTag+'不是合法的标签'; end; 11 : begin strErrType := '值:'+inttostr(intInfoType)+'超出intInfoType范围'+#13+'相关的arrTag取值为:'+arrTag[intTagAbc][intTagNth+11]; end else strErrType := '值:'+inttostr(intErrType)+'超出intErrType范围'; end; if intErrType in [1,2,3,4,11] then begin form1.Caption := form1.Caption+strErrType; intLinefeedType := 0; //错误解决办法 intErrType :=0; end; //输出 if Result.Count = 0 then intLinefeedType := 1; //防止刚开始count-1 case intLinefeedType of 0 : begin //+内容 Result.Strings[Result.Count-1] := Result.Strings[Result.Count-1]+strInfo; end; 1 : begin //#13+内容 for i := 1 to intDepth do strInfo := ' '+strInfo; //深度 Result.add('<br /><a style="width:50px;">'+'['+inttostr(Result.count)+']'+inttostr(intDepth)+'</a>|'+strInfo); end; end; until AnsiPos('>',strSource) = 0 ; Result.Insert(0,'行:'+inttostr(Result.Count)+'<style type="text/css">body,td,th {font-size: 11px;}</style>'); Result.SaveToFile(ExtractFilePath(Application.ExeName)+'tmp.html'); finally arrTagManor.Clear; arrTagManor.Free; arrTagManorTemp.Clear; arrTagManorTemp.Free; end; end;