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

posted @ 2017-07-10 10:43  amiko  阅读(985)  评论(0编辑  收藏  举报