工作记录:html网页采集利器:simple_html_dom,phpQuery

之前做网页采集一般使用正则匹配和字符串查找替换,很是麻烦,后来自从有了phpQuery类之后,采集方式变的和jquery一样简便。可并不是所有的html页面都能被phpQuery正确解析,有时候会出现解析失败的情况,例如很明显的一个ID标签对象,phpQuery却获取为空,在http://code.google.com/p/phpquery/上找到项目之后,发现没有更新的版本了,于是决定采用另一个html解析利器simple_html_dom作为候补,项目下载地址:http://sourceforge.net/projects/simplehtmldom/files/。

simple_html_dom的常用操作:

include 'simple_html_dom.php';
//面向过程方式2种读取方式
$html = file_get_html('http://www.baidu.com');
$html = str_get_html(file_get_contents('http://www.baidu.com'));

//面向对象方式2种读取方式
$html = new simple_html_dom();
$html->load_file('http://www.baidu.com');
$html->load(file_get_contents('http://www.baidu.com'));

foreach($html->find('img') as $img)
{
	print_r($img->src)."<br>";
}

simple_html_dom主打find方法,如果find只有一个参数其查找到的元素一般以对象数组的方式进行遍历;如果添加第二个参数表示索引则返回一个元素,如下:

echo "<pre>";
print_r($html->find('img', 0)->src);
echo "</pre>";

simple_html_dom的find方法参数一与jquery的模式基本一致,可以使用条件搜索,返回的变量e是一个对象,具有以下几类属性:
$e->tag         与原生js的tagName对应,jquery的$(e).attr('nodeName')对应
$e->outertext   与原生js的outerHTML对应,jquery的$(e).attr('outerHTML')对应
$e->innertext   与原生js的innerHTML对应,jquery的$(e).attr('innerHTML')或$(e).html()对应
$e->plaintext   与原生js的innerText对应,jquery的$(e).attr('innerText')或$(e).text()对应
常用的方法如下:
mixed $e->children(index)   index为索引序号,返回第n个子节点对象
element $e->parent()        返回父节点对象
element $e->first_child()   返回第一个子节点对象
element $e->last_child()    返回最后一个子节点对象
element $e->next_sibling()  返回下一个邻节点对象
element $e->prev_sibling()  返回上一个邻节点对象
功能虽然发现的不多,但对应一般的html解析已经足够。如果需要解析大量html建议使用$html->clear()释放内存。
phpQuery的功能比simple_html_dom强大,使用方式上更接近于jquery,查看其源码可以知道其所支持的属性和方法。
phpQuery存在单文件版本和多文件版本,一般使用单文件足以。使用方法如下:

include 'phpQuery-onefile.php';
//面向对象方式2种读取方式
phpQuery::newDocumentFile('http://www.baidu.com');
$contents = get_html_contents($main_url);
phpQuery::newDocument($contents);
foreach(pq('img') as $img)   
{   
   echo pq($img)->attr('src')."<br>";
}

可以看出,phpQuery的pq相当于jquery的$符号,使用方式基本一致,查阅jquery手册即可。不过gbk网页需要注意编码问题,因为其解析为utf8编码,网上说可以使用phpQuery::$defaultCharset="GBK"进行申明,但不一定成功,建议在utf8环境下操作,解析获取到想要的内容之后再转码为所需的编码。
同样,如果需要解析大量html页面,建议使用phpQuery::$documents = array();方法释放内存。

===========================================================================

虽然上面2种html解析方式已经能够满足node节点的操作,但是细化到字符串的操作,如js变量获取,只能获取到script标签的内容而无法获取变量内容,于是我写了下面这个字符串截取函数对获取的html元素进行截取:

/** 
* 字符串边界截取函数
* @param    string      $haystack       待截取的字符串
* @param    string      $left           待查找的左侧字符串
* @param    string      $right          待查找的右侧字符串
* @return   string      截取之后的字符串,不带$left和$right
* @author   shuiguang  
*/
function subByString($haystack, $left = '', $right = '')
{
    $left_pos = false;
    $right_pos = false;
    if($left == '' || ($left_pos = strpos($haystack, $left)) === false)
    {
        $start_pos = 0;
    }else{
        $start_pos = $left_pos;
    }
    $right_data = substr($haystack, $start_pos + ($left_pos === false ? 0 : strlen($left)));
    
    if($right == '' || ($right_pos = strpos($right_data, $right)) === false)
    {
        return substr($haystack, $left_pos+strlen($left));
    }else{
        $end_pos = $start_pos + $right_pos;
        return substr($haystack, ($left_pos === false ? 0 : $start_pos+strlen($left)), $end_pos-$start_pos);
    }
}

经过测试,发现上面的方法有bug,例如,查找的元素为'abcde',左侧元素为'f',右侧元素为'g',截取后的为bcde,少了一个字符,修复如下:

/**
 * 用于字符串截取(从左往右读取),用于正向截取2个字符串之间的内容,不含边界
 * @author Z
 * @param String haystack 待截取的字符串
 * @param String left  待查找的左侧字符串
 * @param String right 待查找的右侧字符串
 * @return String 截取之后的字符串,不带left和right
 */
function subByString($haystack, $left = '', $right = '') {
    $left_pos = false;
    $right_pos = false;
    if($left == '' || ($left_pos = strpos($haystack, $left)) === false) {
        $start_pos = 0;
    }else{
        $start_pos = $left_pos;
    }
    $right_data = substr($haystack, $start_pos + ($left_pos === false ? 0 : strlen($left)));
    if($right == '' || ($right_pos = strpos($right_data, $right)) === false) {
        return substr($haystack, $left_pos === false ? 0 : $left_pos+strlen($left));
    }else{
        return substr($haystack, $left_pos === false ? 0 : $left_pos+strlen($left), $right_pos);
    }
}


/**
 * 用于字符串逆向截取(从右往左读取),用于截取2个字符串之间的内容,不含边界
 * @author Z
 * @param String haystack 待截取的字符串
 * @param String left  待查找的左侧字符串
 * @param String right 待查找的右侧字符串
 * @return String 截取之后的字符串,不带left和right
 */
function getMiddleReverse($haystack, $left = '', $right = '') {
    $revhtml = strrev($haystack);
    $revsuffix = strrev($right);
    $revpreffix = strrev($left);
    $middle = subByString($revhtml, $revsuffix, $revpreffix);
    $result = strrev($middle);
    return $result;
}

后来,在javascript中得到了相应的应用,需要注意strpos和indexOf的查找失败值不同,写法如下:

/**
 * 用于字符串截取(从左往右读取),用于正向截取2个字符串之间的内容,不含边界
 * author Z
 * @param String haystack 待截取的字符串
 * @param String left  待查找的左侧字符串
 * @param String right 待查找的右侧字符串
 * @return String 截取之后的字符串,不带left和right
 */
function subByString(haystack, left, right) {
	var left_pos = 0;
	var right_pos = 0;
	var start_pos, leftIndex, rightIndex;
	if(left == "" || (left_pos = haystack.indexOf(left)) == -1) {
		start_pos = 0;
	}else{
		start_pos = left_pos;
	}
	leftIndex = start_pos + (left_pos == -1 ? 0 : left.length);
	right_data = haystack.substring(leftIndex, haystack.length);
	if(right == "" || (right_pos = right_data.indexOf(right)) == -1) {
		rightIndex = left_pos == -1 ? 0 : left_pos+left.length;
		return haystack.substring(rightIndex, haystack.length);
	}else{
		leftIndex = left_pos == -1 ? 0 : left_pos+left.length;
		return haystack.substring(leftIndex, leftIndex+right_pos);
	}
}

java语言版和javascript版几乎一致:

/**
 * 用于字符串截取(从左往右读取),用于正向截取2个字符串之间的内容,不含边界
 * @author Z
 * @param String haystack 待截取的字符串
 * @param String left  待查找的左侧字符串
 * @param String right 待查找的右侧字符串
 * @return String 截取之后的字符串,不带left和right
 */
public static String subByString(String haystack, String left, String right) {
    int left_pos = 0;
    int right_pos = 0;
    int start_pos, leftIndex, rightIndex;
    if (left.equals("") || (left_pos = haystack.indexOf(left)) == -1) {
        start_pos = 0;
    } else {
        start_pos = left_pos;
    }
    leftIndex = start_pos + (left_pos == -1 ? 0 : left.length());
    String right_data = haystack.substring(leftIndex, haystack.length());
    if (right.equals("") || (right_pos = right_data.indexOf(right)) == -1) {
        rightIndex = left_pos == -1 ? 0 : left_pos + left.length();
        return haystack.substring(rightIndex, haystack.length());
    } else {
        leftIndex = left_pos == -1 ? 0 : left_pos + left.length();
        return haystack.substring(leftIndex, leftIndex + right_pos);
    }
}

/**
 * 用于字符串逆向截取(从右往左读取),用于截取2个字符串之间的内容,不含边界
 * author Z
 * @param String haystack 待截取的字符串
 * @param String left  待查找的左侧字符串
 * @param String right 待查找的右侧字符串
 * @return String 截取之后的字符串,不带left和right
 */
public static String getMiddleReverse(String haystack, String left, String right) {
    StringBuffer sb = new StringBuffer(haystack);
    StringBuffer lb = new StringBuffer(left);
    StringBuffer rb = new StringBuffer(right);
    StringBuffer revhtml = sb.reverse();
    StringBuffer revsuffix = rb.reverse();
    StringBuffer revpreffix = lb.reverse();
    String middle = subByString(revhtml.toString(), revsuffix.toString(), revpreffix.toString());
    StringBuffer result = new StringBuffer(middle).reverse();
    return result.toString();
}

上面的2个php类和1个函数文件的下载地址:

simple_html_dom:http://download.csdn.net/detail/zhengshuiguang/8124219
phpQuery:http://download.csdn.net/detail/zhengshuiguang/8124243

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值