PageRenderTime 42ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/ThinkPHP/Extend/Library/ORG/Util/HtmlExtractor.class.php

https://bitbucket.org/zjut/labs
PHP | 222 lines | 131 code | 16 blank | 75 comment | 22 complexity | 8e882f5f073dc6019bff152f949c1f27 MD5 | raw file
  1. <?php
  2. /* 海龙挖掘机 2.0正式版
  3. * 正文提取,分析,可自动判断编码,自动转码
  4. * 原理:根据代码块加权的原理,首先将HTML分成若干个小块,然后对每个小块进行评分。
  5. * 取分数在3分以上的代码块中的内容返回
  6. * 加分项 1 含有标点符号
  7. * 2 含有<p>标签
  8. * 3 含有<br>标签
  9. * 减分项 1 含有li标签
  10. * 2 不包含任何标点符号
  11. * 3 含有关键词javascript
  12. * 4 不包含任何中文的,直接删除
  13. * 5 有<li><a这样标签
  14. * 实例:
  15. * $he = new HtmlExtractor();
  16. * $str = $he->text($html);
  17. * 其中$html是某个网页的HTML代码,$str是返回的正文,正文编码是utf-8的
  18. */
  19. class HtmlExtractor {
  20. /*
  21. * 取得汉字的个数(目前不太精确)
  22. */
  23. function chineseCount($str){
  24. $count = preg_match_all("/[\xB0-\xF7][\xA1-\xFE]/",$str,$ff);
  25. return $count;
  26. }
  27. /*
  28. * 判断一段文字是否是UTF-8,如果不是,那么要转成UTF-8
  29. */
  30. function getutf8($str){
  31. if(!$this->is_utf8(substr(strip_tags($str),0,500))){
  32. $str = $this->auto_charset($str,"gbk","utf-8");
  33. }
  34. return $str;
  35. }
  36. function is_utf8($string)
  37. {
  38. if(preg_match("/^([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){1}/",$string) == true || preg_match("/([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){1}$/",$string) == true || preg_match("/([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){2,}/",$string) == true){
  39. return true;
  40. }else{
  41. return false;
  42. }
  43. }
  44. /*
  45. * 自动转换字符集,支持数组和字符串
  46. */
  47. function auto_charset($fContents,$from,$to){
  48. $from = strtoupper($from)=='UTF8'? 'utf-8':$from;
  49. $to = strtoupper($to)=='UTF8'? 'utf-8':$to;
  50. if( strtoupper($from) === strtoupper($to) || empty($fContents) || (is_scalar($fContents) && !is_string($fContents)) ){
  51. //如果编码相同或者非字符串标量则不转换
  52. return $fContents;
  53. }
  54. if(is_string($fContents) ) {
  55. if(function_exists('mb_convert_encoding')){
  56. return mb_convert_encoding ($fContents, $to, $from);
  57. }elseif(function_exists('iconv')){
  58. return iconv($from,$to,$fContents);
  59. }else{
  60. return $fContents;
  61. }
  62. }
  63. elseif(is_array($fContents)){
  64. foreach ( $fContents as $key => $val ) {
  65. $_key = $this->auto_charset($key,$from,$to);
  66. $fContents[$_key] = $this->auto_charset($val,$from,$to);
  67. if($key != $_key )
  68. unset($fContents[$key]);
  69. }
  70. return $fContents;
  71. }
  72. else{
  73. return $fContents;
  74. }
  75. }
  76. /*
  77. * 进行正文提取动作
  78. */
  79. function text($str){
  80. $str = $this->clear($str);
  81. $str = $this->getutf8($str);
  82. $divList = $this->divList($str);
  83. $content = array();
  84. foreach($divList[0] as $k=>$v){
  85. //首先判断,如果这个内容块的汉字数量站总数量的一半还多,那么就直接保留
  86. //还要判断,是不是一个A标签把整个内容都扩上
  87. if($this->chineseCount($v)/(strlen($v)/3) >= 0.4 && $this->checkHref($v)){
  88. array_push($content,strip_tags($v,"<p><br>"));
  89. }else if($this->makeScore($v) >= 3){
  90. //然后根据分数判断,如果大于3分的,保留
  91. array_push($content,strip_tags($v,"<p><br>"));
  92. }else{
  93. //这些就是排除的内容了
  94. }
  95. }
  96. return implode("",$content);
  97. }
  98. /*
  99. * 判断是不是一个A标签把整个内容都扩上
  100. * 判断方法:把A标签和它的内容都去掉后,看是否还含有中文
  101. */
  102. private function checkHref($str){
  103. if(!preg_match("'<a[^>]*?>(.*)</a>'si",$str)){
  104. //如果不包含A标签,那不用管了,99%是正文
  105. return true;
  106. }
  107. $clear_str = preg_replace("'<a[^>]*?>(.*)</a>'si","",$str);
  108. if($this->chineseCount($clear_str)){
  109. return true;
  110. }else{
  111. return false;
  112. }
  113. }
  114. function makeScore($str){
  115. $score = 0;
  116. //标点分数
  117. $score += $this->score1($str);
  118. //判断含有P标签
  119. $score += $this->score2($str);
  120. //判断是否含有br标签
  121. $score += $this->score3($str);
  122. //判断是否含有li标签
  123. $score -= $this->score4($str);
  124. //判断是否不包含任何标点符号
  125. $score -= $this->score5($str);
  126. //判断javascript关键字
  127. $score -= $this->score6($str);
  128. //判断<li><a这样的标签
  129. $score -= $this->score7($str);
  130. return $score;
  131. }
  132. /*
  133. * 判断是否有标点符号
  134. */
  135. private function score1($str){
  136. //取得标点符号的个数
  137. $count = preg_match_all("/(,|。|!|(|)|“|”|;|《|》|、)/si",$str,$out);
  138. if($count){
  139. return $count * 2;
  140. }else{
  141. return 0;
  142. }
  143. }
  144. /*
  145. * 判断是否含有P标签
  146. */
  147. private function score2($str){
  148. $count = preg_match_all("'<p[^>]*?>.*?</p>'si",$str,$out);
  149. return $count * 2;
  150. }
  151. /*
  152. * 判断是否含有BR标签
  153. */
  154. private function score3($str){
  155. $count = preg_match_all("'<br/>'si",$str,$out) + preg_match_all("'<br>'si",$str,$out);
  156. return $count * 2;
  157. }
  158. /*
  159. * 判断是否含有li标签
  160. */
  161. private function score4($str){
  162. //有多少,减多少分 * 2
  163. $count = preg_match_all("'<li[^>]*?>.*?</li>'si",$str,$out);
  164. return $count * 2;
  165. }
  166. /*
  167. * 判断是否不包含任何标点符号
  168. */
  169. private function score5($str){
  170. if(!preg_match_all("/(,|。|!|(|)|“|”|;|《|》|、|【|】)/si",$str,$out)){
  171. return 2;
  172. }else{
  173. return 0;
  174. }
  175. }
  176. /*
  177. * 判断是否包含javascript关键字,有几个,减几分
  178. */
  179. private function score6($str){
  180. $count = preg_match_all("'javascript'si",$str,$out);
  181. return $count;
  182. }
  183. /*
  184. * 判断<li><a这样的标签,有几个,减几分
  185. */
  186. private function score7($str){
  187. $count = preg_match_all("'<li[^>]*?>.*?<a'si",$str,$out);
  188. return $count * 2;
  189. }
  190. /*
  191. * 去噪
  192. */
  193. private function clear($str){
  194. $str = preg_replace("'<script[^>]*?>.*?</script>'si","",$str);
  195. $str = preg_replace("'<style[^>]*?>.*?</style>'si","",$str);
  196. $str = preg_replace("'<!--.*?-->'si","",$str);
  197. return $str;
  198. }
  199. /*
  200. * 取得内容块
  201. */
  202. private function divList($str){
  203. preg_match_all("'<[^a][^>]*?>.*?</[^>]*?>'si",$str,$divlist);
  204. return $divlist;
  205. }
  206. }