說明
主要是總結我通過python實現html解析的一個初步的思路和記錄實現基礎html解析的代碼。本解析方式僅僅
只是實現了html按元素解析的功能,具體元素的分類獲取還得需要再進行進一步的優化。
html解析
html解析,當前實現我將其分爲兩個部分:一個是元素節點的定義,一個是元素節點解析。
1) 解析實現
解析通過html的節點進行控制,通過遍歷html中的所有節點,對節點進行數據描述。html中的節點(即元素)
格式爲:
<element ..../> #單閉合
<element ...>....</element> #節點閉合
目前支持這兩類節點的解析(對於不規範的節點書寫解析當前或存在一些問題),通過對節點的數據的定義(節點
名稱,節點狀態(start,end),節點包含文本,節點包含屬性等),python實現通過定義類對象對元素進行定
義。代碼如下:
class Element:
elementName="Doucument"
START_DOCUMENT = 0
START_HTML = 1
START_HEAD = 2
END_HEAD = 3
START_BODY =4
END_BODY=5
START_ELEMENT=6
END_ELEMENT=7
ELEMENT_TEXT=8
END_HTML=9
END_DOCUMENT=10
NO_ELEMENT=100
'''
html基本元素
elementName:元素名稱(header,body之類)
text:元素包含文本內容
'''
def __init__(self,elementName=None,text=None,id=None,**attributes):
if elementName:
self.elementName=elementName
if text:
self.text=text
if id:
self.id=id
if attributes and len(attributes)>0:
self.attributes=attributes
self.content=None
self.elementDict={}
def getElementId(self):
return self.id
def toString(self):
if self.content:
return self.content
else:
buffer=""
if self.attributes and len(self.attributes):
for key in self.attributes:
if len(buffer):
buffer = "%s=\"%s\"" % (key[0],key[1])
else:
a=buffer
buffer="%s %s=\"%s\"" %(a,key[0],key[1])
if self.text and len(self.text):
return "<%s %s> %s </%s>" %(self.elementName,buffer,self.text,self.elementName)
else:
return "<%s %s/>" % (self.elementName,buffer)
@staticmethod
def element(content=None):
# print "content:%s" % content
element = Element()
if content and len(content.strip().rstrip())>0:
eleStr=content.strip().rstrip()
element.content=content
if len(eleStr) and not eleStr.startswith("<"):
'''
text 內容
'''
element.elementName=Element.elementName
element.text=eleStr
element.id=Element.ELEMENT_TEXT
elif len(eleStr) and eleStr.startswith("<"):
'''
標籤內容
'''
if eleStr.startswith('</'):
'''
element 結束符號
'''
element.id=Element.END_ELEMENT
element.elementName=eleStr[2:len(eleStr)-1]
if element.elementName:
if hasattr(element,"END_"+element.elementName.upper()):
element.id=getattr(element,"END_"+element.elementName.upper())
else:
element.id=Element.END_ELEMENT
else:
'''
element 開始符號
'''
element.id=Element.START_ELEMENT
params_str=None
if eleStr.endswith("/>"):
params_str=eleStr[1:-2]
else:
params_str=eleStr[1:-1]
if not params_str:
assert "Unpredictable error."
params=params_str.split()
element.elementName=params[0]
attr_dict = {}
prev_key=None
for attr in params[1:]:
if "=" in attr:
attr_map=attr.split("=")
key=attr_map[0].strip().rstrip()
value_str=attr_map[1].strip().rstrip()
index=len(value_str)
value=value_str[1:index-1]
attr_dict[key]=value
prev_key=key
else:
if attr.endswith("\""):
attr_dict[prev_key]+=" "+attr[:-1]
else:
attr_dict[prev_key] += " " + attr
if len(attr_dict) >0:
element.attributes=attr_dict
if hasattr(element,"START_"+element.elementName.upper()):
element.id = getattr(element, "START_" + element.elementName.upper())
else:
element.id=Element.START_ELEMENT
Element.elementName=element.elementName
else:
element.elementName=None
element.text=None
element.attributes=None
element.id=Element.NO_ELEMENT
return element
2) 解析實現
html解析通過標誌”<”和”>”實現對html元素的解析,解析實現通過生成器的方式,逐個迭代。解析主要分爲
三個類型:
簡單的單個元素集合
單一開始和結束元素集合,格式如下:
<html> #單一開始 </html> #單一結束
單封閉(自封閉)元素集合
自封閉的元素單獨處理,會自動迭代成開始標籤和結束標籤,格式如下:
<input type="submit" value="Submit" /> #自封閉
元素文本數據
元素文本單獨處理,是處於元素開始和結束標籤之間的文本數據,依賴文本之前的開始標籤
如上,爲基本的格式介紹,python解析代碼如下所示:
import codecs
from params import *
class Parser:
'''
html parser class.
'''
def __init__(self,fileName=None):
self.fileName=fileName
self.begin=0
self.over=0
self.index=0
def parser(self):
if not self.fileName:
raise "File not found."
with codecs.open(filename=self.fileName, mode='r', encoding='utf-8') as inputfile:
content = inputfile.read()
if (not content) or len(content.strip().rstrip())==0:
raise "get file content false."
content=unicode(content.strip().rstrip())
# print "total content:", content
try:
index=content.index("<html") if ("<html" in content) else content.index("<html")
except BaseException as error:
print "parse erro:",str(error)
assert True
content=content[index:]
# print "get content:",content
#----------------------------------begin parser-------------------------
yield Element.element("<DOCUMENT>")
while True:
try:
self.begin= content.index("<",self.over) #element begin index.
if self.begin> self.over:
text=content[self.over+1:self.begin].strip().rstrip()
if text and len(text)>0:
yield Element.element(text)
self.over= content.index(">",self.begin) #element end index
elementStr=content[self.begin:self.over+1].rstrip().strip()
# print "elementStr:",elementStr
if elementStr and len(elementStr):
if elementStr.startswith("<!"):
pass
elif elementStr.endswith("/>"):
yield Element.element(elementStr[:-2]+">")
yield Element.element("</"+elementStr.split()[0][1:]+">")
else:
yield Element.element(elementStr)
except BaseException as error:
print "index error:",str(error)
break
#-------------------------------end parser----------------------------------
yield Element.element("</DOCUMENT>")
3) 使用
完成如上的解析操作,使用就簡單很多,直接通過for循環遍歷,具體操作需要自行解析,代碼如下:
import codecs,sys,socket
from parser import *
fileName = "test.html"
content = ""
parser=Parser(fileName)
a=parser.parser()
for b in a:
if b.elementName == 'img':
print "img url is:", b.attributes['src']
如上,即是一個簡易版的html解析實現,
示例代碼在:https://github.com/fishly/graphicsProject-/tree/master/robots/htmlpraser