`

用Javascript解析html

阅读更多

该文章有更新,请移步 http://pickerel.iteye.com/admin/blogs/267912

 

说到用Javascript解析html,大家肯定会想到dom或者正则表达式,但这两个都不是我今天我要说的。dom很不错,不过效率不高,而且必须将要解析的html插入到当前页面或者建立一个iframe才能进行,而用正则表达式,又有太过繁琐和难以维护的问题。

 

有人要说了,ruby、php、python有了那么多开源的优秀的html解析的类库,什么beautiful soap,什么Mechanize,什么Hpricot,什么ScRUBYt,你为什么非要自讨苦吃用javascript来干这活呢?

 

答案是:如果只允许你用javascript和html开发程序呢,比如开发adobe air的程序,比如下一步我要做的基于基于内嵌webkit组件的Android应用快速开发框架,有时候,轮子还是得自己造的。

 

我的这个解析实现只是雏形,它以Erik Arvidsson开发的SimpleHtmlParser 作为html的分析器。SimpleHtmlParser是一个基于Sax模型实现的html分析器,他能分析非标准的xml的格式的html别把转换作为一个标准的xml处理。有了这个解析器做基础,我写了个简单的html_extractor,用来分析html并获取指定标记内的内容。

 

html_extractor的使用

 

new html_extractor(html): 指定html字符串创建一个html_extractor对象

方法:

tag(tagName):设定一个待匹配的元素名,返回结果为当面的html_extractor对象

attr(attrName, attrValue):设定匹配的属性条件,attr必须在tag后,返回结果为当面的html_extractor对象

match(innerOrNot):执行匹配,返回结果为符合条件的字符串数组。

示例:

 

html = "<div>div1</div>";
//取出div标记下的内容,ret的结果是["div1"]
var ret = new html_extractor(html).tag("div").match();

html = "<div id=\"head\">head</div><div id=\"content\"><p><ul><li>item1</li><li>item2</li></ul></div>";		
//取出属性id=content的div下的所有li下的内容,返回结果将是["item1", "item2"]
ret = new html_extractor(html).tag("div").attr("id", "content").tag("li").match();

//提取baidu搜索结果
ret = new html_extractor(html).tag("td").attr("class", "f").match();
//提取google搜索结果
ret = new html_extractor(html).tag("li").attr("class", "g").match();

 

源代码(当前代码还非常原始,进攻参考,请慎重使用)

var html_extractor = function(html)
{
	this.parser = new SimpleHtmlParser;
	this.html  = html;
	this.tags = [];
	this.attrs = [];	
}
html_extractor.prototype.tag = function(tag)
{
	this.tags.push(tag.toLowerCase());

	return this;
}
html_extractor.prototype.attr = function(name, value)
{
	var len = this.tags.length - 1;
	if (this.attrs[len] == undefined)this.attrs[len] = [];
	this.attrs[len].push({name:name.toLowerCase(), value: value});
	return this;
}
html_extractor.prototype.match = function(inner)
{
	var self = this;
	var handler = function(){
		this._tag_index = 0;
		this._matched_tags = [];
		this._matched = [];
		this._result = "";
		this.result = [];
		this._all_matched = false;
		for( var i = 0; i < self.tags.length; i++)this._matched[i] = false;
		this.inner = true;
		if (inner != undefined && inner != null)
		{
			this.inner = inner;
		}

	};
	handler.prototype = {
		startElement:   function (tag, attrs) {
			this.tag_index++;
			tag = tag.toLowerCase();
			//air.trace("process tag:" + tag +  " " + this.tag_index);

			if (this._all_matched )
			{
				this._result += get_start_tag(tag, attrs);
				return;
			}

			for( var i = 0; i < this._matched.length; i++)
			{
				//air.trace(i + ":" + this._matched[i]);
				if (!this._matched[i] )
				{
					if (self.tags[i] == tag)
					{
						this._matched[i] = true;
						if (self.attrs[i] != undefined)
						{
							for(var n = 0; n < self.attrs[i].length; n++)
							{
								var attr = self.attrs[i][n];
								if (attr != undefined)
								{
									if(attrs[attr.name] != attr.value)	this._matched[i] = false;
								};
							}
						}
						if (this._matched[i] )
						{
							 //todo callback
					  		 //air.trace(i + ":" + this._matched[i] + " first");
 							 this._matched_tags[this.tag_index] = i;
							 if (i == self.tags.length -1)
							 {
								 this._all_matched = true;
								 if (!this.inner) this._result += get_start_tag(tag, attrs);
							 }
							 return;
						}
					}

					if (!this._matched[i] ){break;}

				}
			}		
		},
		endElement:     function (tag) {
			tag = tag.toLowerCase();

			if (this._matched_tags[this.tag_index] != undefined)
			{
				this._matched[this._matched_tags[this.tag_index]] = false;
				if (this._all_matched)
				{
					if (!this.inner)this._result += "</" + tag +">";
					this.result.push(this._result);
					this._result = "";
					this._all_matched = false;
				}
			}
			else if (this._all_matched)
			{
				this._result += "</" + tag +">";
			}
			//air.trace("finished tag:" + tag +  " " + this.tag_index);

			this.tag_index--;
		},
		characters:		function (s) { if(this._all_matched)this._result += s;},
		comment:		function (s) {}
	};
	this.parser.contentHandler = new handler;

	this.parser.parse(this.html);	
	//reset
	this.tags = [];
	this.attrs = [];
	return this.parser.contentHandler.result;
}
function get_start_tag(tag, attrs)
{
	var ret = "<" + tag;
	for (var key in attrs)
	{
		value = attrs[key];
		ret += " " + key + "=\"" + value + "\"";

	}
	ret += ">";
	return ret;
}

/** SimpleHtmlParser
 * Original code by Erik Arvidsson, Mozilla Public License
 * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
 */

/*
var handler ={
	startElement:   function (sTagName, oAttrs) {},
	endElement:     function (sTagName) {},
    characters:		function (s) {},
    comment:		function (s) {}
};
*/

function SimpleHtmlParser()
{
}

SimpleHtmlParser.prototype = {

	handler:	null,

	// regexps

	startTagRe:	/^<([^>\s\/]+)((\s+[^=>\s]+(\s*=\s*((\"[^"]*\")|(\'[^']*\')|[^>\s]+))?)*)\s*\/?\s*>/m,
	endTagRe:	/^<\/([^>\s]+)[^>]*>/m,
	attrRe:		/([^=\s]+)(\s*=\s*((\"([^"]*)\")|(\'([^']*)\')|[^>\s]+))?/gm,

	parse:	function (s, oHandler)
	{
		if (oHandler)
			this.contentHandler = oHandler;

		var i = 0;
		var res, lc, lm, rc, index;
		var treatAsChars = false;
		var oThis = this;
		while (s.length > 0)
		{
			// Comment
			if (s.substring(0, 4) == "<!--")
			{
				index = s.indexOf("-->");
				if (index != -1)
				{
					this.contentHandler.comment(s.substring(4, index));
					s = s.substring(index + 3);
					treatAsChars = false;
				}
				else
				{
					treatAsChars = true;
				}
			}

			// end tag
			else if (s.substring(0, 2) == "</")
			{
				if (this.endTagRe.test(s))
				{
					lc = RegExp.leftContext;
					lm = RegExp.lastMatch;
					rc = RegExp.rightContext;

					lm.replace(this.endTagRe, function ()
					{
						return oThis.parseEndTag.apply(oThis, arguments);
					});

					s = rc;
					treatAsChars = false;
				}
				else
				{
					treatAsChars = true;
				}
			}
			// start tag
			else if (s.charAt(0) == "<")
			{
				if (this.startTagRe.test(s))
				{
					lc = RegExp.leftContext;
					lm = RegExp.lastMatch;
					rc = RegExp.rightContext;

					lm.replace(this.startTagRe, function ()
					{
						return oThis.parseStartTag.apply(oThis, arguments);
					});

					s = rc;
					treatAsChars = false;
				}
				else
				{
					treatAsChars = true;
				}
			}

			if (treatAsChars)
			{
				index = s.indexOf("<");
				if (index == -1)
				{
					 this.contentHandler.characters(s);
					s = "";
				}
				else
				{
					this.contentHandler.characters(s.substring(0, index));
					s = s.substring(index);
				}
			}

			treatAsChars = true;
		}
	},

	parseStartTag:	function (sTag, sTagName, sRest)
	{
		var attrs = this.parseAttributes(sTagName, sRest);
		this.contentHandler.startElement(sTagName, attrs);
	},

	parseEndTag:	function (sTag, sTagName)
	{
		this.contentHandler.endElement(sTagName);
	},

	parseAttributes:	function (sTagName, s)
	{
		var oThis = this;
		var attrs = {};
		s.replace(this.attrRe, function (a0, a1, a2, a3, a4, a5, a6)
		{
			//attrs.push(oThis.parseAttribute(sTagName, a0, a1, a2, a3, a4, a5, a6));
			attr = oThis.parseAttribute(sTagName, a0, a1, a2, a3, a4, a5, a6);
			attrs[attr.name] = attr.value;
		});
		return attrs;
	},

	parseAttribute: function (sTagName, sAttribute, sName)
	{
		var value = "";
		if (arguments[7])
			value = arguments[8];
		else if (arguments[5])
			value = arguments[6];
		else if (arguments[3])
			value = arguments[4];

		var empty = !value && !arguments[3];
		return {name: sName.toLowerCase(), value: empty ? null : value};
	}
};

 

 

 

分享到:
评论
4 楼 shuigugu 2011-04-19  
extractor(html): 指定html字符串创建一个html_extractor对象
方法:
shuigugu 写道
源码在哪里啊?有可以下载的简单例子供我们参考参考么,非常感谢。

3 楼 shuigugu 2011-04-18  
源码在哪里啊?有可以下载的简单例子供我们参考参考么,非常感谢。
2 楼 pickerel 2008-12-26  
zhjzh1016 写道

怎么说startTagRe: /^&lt;([^&gt;\s\/]+)((\s+[^=&gt;\s]+(\s*=\s*((\"[^"]*\")|(\'[^']*\')|[^&gt;\s]+))?)*)\s*\/?\s*&gt;/m,&nbsp; 这一行有错误呢:}expected

请试试 http://pickerel.iteye.com/admin/blogs/267912 吧
1 楼 zhjzh1016 2008-12-26  
怎么说startTagRe: /^<([^>\s\/]+)((\s+[^=>\s]+(\s*=\s*((\"[^"]*\")|(\'[^']*\')|[^>\s]+))?)*)\s*\/?\s*>/m,  这一行有错误呢:}expected

相关推荐

    纯javascript解析CSV文件使用并转成HTML表格

    本篇文章将详细探讨如何使用纯JavaScript解析CSV文件,并将其内容转换为HTML表格。 首先,让我们了解CSV文件的基本结构。CSV文件由行组成,每行由一个或多个字段组成,字段之间用逗号分隔。在某些情况下,如果字段...

    javascript 解析 rss 实例

    在本文中,我们将深入探讨如何使用JavaScript解析RSS(Really Simple Syndication) feed,这是一种常见的数据格式,用于发布新闻提要、博客更新和其他定期发布的内容。RSS订阅允许用户获取网站的最新内容而无需直接...

    基于嵌入式浏览器的JavaScript解析器设计.pdf

    基于嵌入式浏览器的JavaScript解析器设计 本文主要介绍了基于嵌入式浏览器的JavaScript解析器设计,通过对SpiderMonkey原理的分析和探讨,设计了一种基于SpiderMonkey的JavaScript解析器,可以快速、高效地完成...

    用Markdown撰写文档javascript解析成HTML的编辑器针对微信公众号排版使用

    用 Markdown 撰写文档,javascript 解析成HTML的编辑器,针对微信公众号排版使用

    javascript解析xml文件

    在JavaScript中解析XML文件是一项常见的任务,特别是在网页开发中,我们可能需要将XML数据转换为HTML以便更好地展示或处理。JavaScript提供了DOMParser API和ActiveXObject(仅限于Internet Explorer)来解析XML。...

    javascript解析二维码插件以及demo

    总结来说,使用原生JavaScript解析二维码需要结合HTML5的Canvas和FileReader接口,以及第三方的解码库。"erweima-jiexi-demo"压缩包的"test.html"是一个很好的学习起点,通过实践和扩展,我们可以构建出更强大、更...

    将html里表格解析为json形式

    本篇文章将深入探讨如何使用JavaScript解析HTML表格并将其转换为JSON格式,以便于后台存储和处理。 首先,我们需要了解HTML表格的基本结构。HTML表格由`&lt;table&gt;`标签开始,包含一个或多个`&lt;tr&gt;`(表格行)标签,每...

    Qt Webkit解析html

    **Qt WebKit解析HTML** Qt WebKit 是一个强大的框架,用于在Qt应用程序中渲染和交互HTML内容。它基于WebKit引擎,这是一个广泛应用于Safari和Chrome等浏览器的开源渲染引擎。在Qt中,Qt WebKit提供了丰富的API,...

    JavaScript解析XML文件,在网页上以目录树的形式显示

    综上所述,要完成“JavaScript解析XML文件并在网页上以目录树形式显示”的任务,你需要理解XML的基本概念,熟悉JavaScript的DOM操作,掌握XML数据的解析方法,以及如何将XML数据转换为HTML结构,并通过CSS和...

    C++MFC解析HTML。

    在“C++ MFC解析HTML”的主题中,我们将深入探讨如何使用MFC来解析HTML文档,特别是提取其中的URL信息。MFC虽然不是一个专门用于处理HTML的库,但它可以通过集成Internet Explorer的ActiveX控件MSHTML来实现这一目标...

    解析带有html标签的json数据

    2. **保留HTML结构**:如果需要保持HTML结构以便在网页上显示,我们可以直接在前端使用JavaScript的DOM API或者在后端使用服务器端的HTML解析库,如Node.js的cheerio库,来操作这些HTML片段。 3. **安全问题**:...

    Go-利用goquery去除HTML文本中的javascript和白名单以外的属性

    首先,`goquery`库允许我们解析HTML文档并方便地选择和操作元素。要安装`goquery`,你可以使用`go get`命令: ```bash go get github.com/PuerkitoBio/goquery ``` 在解析HTML文档时,我们首先需要加载HTML内容: ...

    项目中使用到的解析html富文本

    在项目中,解析HTML富文本能够确保内容在不同平台和设备上保持一致的展示效果,提高用户体验。 三、富文本解析库与工具 1. JavaScript:在Web前端,常用的富文本解析库有TinyMCE、CKEditor、Quill等。它们提供了...

    javascript解析xml

    总结来说,JavaScript在XML解析中的应用包括使用DOM API操作XML文档、XPath或CSS选择器选取节点、XML与JSON的互转、使用库和框架简化操作,以及性能和安全性方面的考量。虽然现代Web开发中JSON更为流行,但理解XML的...

    JavaScript解析XML实现多级级联下拉列表

    总结来说,实现JavaScript解析XML以创建多级级联下拉列表涉及的主要知识点包括:JavaScript基础、XML文件结构与解析、DOM操作、事件监听以及动态生成HTML元素。通过这些技术,可以构建出交互性强、用户体验良好的Web...

    himalaya, JavaScript HTML到JSON解析器.zip

    Himalaya通过解析HTML标签、属性和文本节点,将其转化为JSON对象。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。使用Himalaya,我们可以将HTML的...

    JavaScript内核系列 pdf

    三、JavaScript内核解析 JavaScript的内核主要包括解释器和垃圾回收机制。解释器负责将JavaScript代码转化为机器可执行的指令,而垃圾回收则自动管理内存,避免内存泄漏。理解V8引擎的工作原理,如即时编译(JIT)和...

    JavaScript解析XML文档成树菜单

    总结,JavaScript解析XML文档成树菜单涉及到DOM解析、XMLHttpRequest或fetch请求、DOM遍历、HTML结构生成、事件处理等多个技术点。具体实现可能因XML结构和需求而异,但基本流程是相似的。阅读原博客(288976)可以...

    解析Html的源代码

    解析HTML源代码,我们可以使用多种方法。一种常见的方式是通过HTTP请求获取网页的源代码,这涉及到网络编程的知识,例如使用Python的`requests`库。以下是一个简单的示例: ```python import requests url = ...

Global site tag (gtag.js) - Google Analytics