python:html元素解析

說明

主要是總結我通過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

Enjoytoday,EnjoyCoding

發佈了52 篇原創文章 · 獲贊 43 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章