ホームページ >バックエンド開発 >PHPチュートリアル >HTMLタグを解析し、ノードを素早く見つけてノード情報を取得します。

HTMLタグを解析し、ノードを素早く見つけてノード情報を取得します。

WBOY
WBOYオリジナル
2016-07-25 08:48:491449ブラウズ
詳しい導入と使用方法については、ソースコードをクリックしてください。
  1. /**
  2. * HTML タグ解析パッケージ
  3. *
  4. * @category TagParse
  5. * @package TagParse
  6. * @author kun
  7. * @copyright 2014 kun
  8. * @license http://www.php.net /license/3_01.txt PHP ライセンス 3.01
  9. * @version 1.0
  10. * @link http://www.blogkun.com
  11. * @since 1.0
  12. */
  13. namespace TagParse;
  14. /**
  15. * TagDomRoot
  16. *
  17. * @category TagParse
  18. * @package TagParse
  19. * @author kun
  20. * @copyright 2014 kun
  21. * @license http://www.php.net/license/ 3_01.txt PHP ライセンス 3.01
  22. * @バージョン 1.0
  23. * @link http://www.blogkun.com
  24. * @since 1.0
  25. */
  26. class TagDomRoot
  27. {
  28. public $tag = 'root';
  29. public $plaintext;
  30. public $child = array();
  31. public $level = 0;
  32. public static $TagParseError = false;
  33. protected static $TagSet = array();
  34. protected static $FoundNode = array();
  35. public static $ErrorTag = array();
  36. /**
  37. * initProperty
  38. *
  39. * @access public
  40. *
  41. * @return null
  42. */
  43. public function initProperty()
  44. {
  45. $TagParseError = false;
  46. $TagSet = array();
  47. $FoundNode = array();
  48. $DumpScriptCode = array( );
  49. $ErrorTag = array();
  50. }
  51. /**
  52. * __construct
  53. *
  54. * @param string $str 解析するタグ文字列
  55. *
  56. * @access public
  57. *
  58. * @return TagDomRoot
  59. */
  60. public function __construct($str)
  61. {
  62. $this->_removeNoise($str);
  63. if ($str = == null) {
  64. self::$TagParseError = true;
  65. } else {
  66. $l = strpos($str, '<');
  67. if ($l !== false) {
  68. $this-> plaintext = substr($str, 0, $l);
  69. }
  70. $res = preg_match_all('~>(.*?)<~s', $str, $matches);
  71. if ($res != = false && $res > 0) {
  72. $this->plaintext .= implode($matches[1]);
  73. }
  74. $r = strrpos($str, '>');
  75. if ($r !== false) {
  76. $this->plaintext .= substr($str, $r+1);
  77. }
  78. $tagCollect = array();
  79. $attrCollect = array();
  80. $innerContentCollect = array();
  81. if ( $this->parseTag($str, $tagCollect, $attrCollect, $innerContentCollect) === false) {
  82. self::$TagParseError = true;
  83. }
  84. foreach ($tagCollect as $index => $tag ) {
  85. $this->child[] = new TagDomNode($tag, $this, $attrCollect[$index], $innerContentCollect[$index], $this->level+1);
  86. }
  87. }
  88. }
  89. /**
  90. * parseTag
  91. *
  92. * @parammixed$strDescription.
  93. *@parammixed&$tagCollectDescription.
  94. *@parammixed&$attrCollectDescription.
  95. *@parammixed&$innerContentCollectDescription.
  96. *
  97. * @access protected
  98. *
  99. * @return boolean Value.
  100. */
  101. 保護関数 parseTag($str, array &$tagCollect, array &$attrCollect, array &$innerContentCollect)
  102. {
  103. $selfClosingTags = array('img' => 1, 'br' => 1、'input' => 1、'link' => 1、'base' => 1, '埋め込み' => 1、'スペーサー' => 1);
  104. $end = -2;
  105. $close = 0;
  106. $error = false;
  107. $tag = '';
  108. while (true) {
  109. $l = strpos($str, '<', $ end+strlen($tag)+2);
  110. if ($l === false) {//parse end
  111. Break;
  112. }
  113. if (strpos(substr($str, $l, 2), '/' ) !== false) {//余った終了タグ、破棄
  114. $error = true;
  115. $end = $l+strlen($tag);
  116. self::$ErrorTag[] = substr($str, $l, strpos($str, '>', $l)-$l+1);
  117. 続き;
  118. }
  119. $r = strpos($str, '>', $l);
  120. $tag = substr( $str, $l+1, $r-$l-1);
  121. if (!ctype_alpha($tag[0]) || strpos($tag, '<') !== false) {
  122. $end = $r + 1;
  123. 続行;
  124. }
  125. $tag = preg_replace("~n+~", ' ', $tag);
  126. $space = strpos($tag, ' ');
  127. if ($space !== false) {
  128. $attrCollect[] = substr ($tag, $space+1);
  129. $tag = substr($tag, 0, $space);
  130. } else {
  131. $attrCollect[] = '';
  132. }
  133. $tagCollect[] = $tag;
  134. if (isset($selfClosingTags[$tag])) {
  135. $innerContentCollect[] = '';
  136. $end = $r-strlen($tag)-2;
  137. $close = $r+1;
  138. continue;
  139. }
  140. $countOpen = -1;
  141. $open = strpos($str, '<'.$tag, $close);
  142. $close = strpos($str, ' if ($close === false) {//余分な開始タグ
  143. $innerContentCollect[] = substr($str, $r+1);
  144. $error = true;
  145. self:: $ErrorTag[] = '<'.$tag.'>';
  146. break;
  147. }
  148. $start = $open;
  149. while ($open < $close && $open !== false) {
  150. $countOpen++;
  151. $open = strpos($str, '<'.$tag, $open+strlen($tag));
  152. }
  153. while ($countOpen > 0 && $close !== false) {
  154. $open = strpos($str, '<'.$tag, $close+strlen($tag)+3);
  155. $close = strpos($str, '', $close+strlen($tag)+3);
  156. if ($close === false) {
  157. Break;
  158. }
  159. $countOpen--;
  160. while ($open < $close && $open !== false) {
  161. $open = strpos($str, '<'.$tag, $open+strlen($tag)+3);
  162. $countOpen++;
  163. }
  164. }
  165. if ($close === false) {///标签闭合不配对
  166. $innerContentCollect[] = substr($str, $r+1);
  167. $error = true;
  168. Break;
  169. }
  170. $ end = $close;
  171. $r = strpos($str, '>', $start);
  172. $innerContentCollect[] = substr($str, $r+1, $end - $r - 1);
  173. }
  174. return !$error;
  175. }
  176. /**
  177. * _removeNoise
  178. *
  179. * @param string &$str 解析するタグ文字列
  180. *
  181. * @access private
  182. *
  183. * @return string
  184. */
  185. プライベート関数 _removeNoise(&$str)
  186. {
  187. $str = preg_replace('~~is', '', $str);
  188. $str = preg_replace('~~is', '', $str);
  189. $str = preg_replace('~~is', '', $str);
  190. }
  191. /**
  192. * parseSelectors
  193. *
  194. * @param string $selectors ユーザーの選択条件
  195. * @param 配列 &$selectorsTag タグ
  196. * @param 配列 &$selectorsAttr 属性
  197. *
  198. * @access protected
  199. *
  200. * @return null
  201. */
  202. protected function parseSelectors($selectors, array &$selectorsTag, array &$selectorsAttr)
  203. {
  204. preg_match_all('~([wd]+)([[wd -="._/]+])?~', $selectors, $matches);
  205. $selectorsTag = $matches[ 1];
  206. foreach ($matches[2] as $key => $value) {
  207. $selectorsAttr[$key] = array();
  208. if ($value !== '') {
  209. preg_match_all('~([wd-]+)="([wd-. _/] +)"~', $value, $matches);
  210. foreach ($matches[1] as $index => $attr) {
  211. $selectorsAttr[$key][$attr] = $matches[2][$ /**
  212. * find
  213. *
  214. * @param 混合 $selectors ユーザーの選択条件。
  215. * @param array $selectorsTag タグ。
  216. * @param array $selectorsAttr 属性。
  217. *
  218. * @access public
  219. *
  220. * @return array
  221. */
  222. public function find($selectors, $selectorsTag = array(), $selectorsAttr = array())
  223. {
  224. if ($selectors !== null) {
  225. $this->parseSelectors($selectors, $selectorsTag, $selectorsAttr);
  226. }
  227. var_dump($selectorsTag, $selectorsAttr);exit();
  228. if (!empty($selectorsTag)) {
  229. $this->seek($selectorsTag, $selectorsAttr);
  230. foreach ($this->gt;child as $key =>$node) {
  231. $node->find(null, $selectorsTag, $selectorsAttr) );
  232. }
  233. }
  234. if ($selectors !== null) {
  235. $res = self::$FoundNode;
  236. self::$FoundNode = array();
  237. return $res;
  238. }
  239. }
  240. /**
  241. * findGlobal
  242. *
  243. * @param string $selectors ユーザーの選択条件
  244. *
  245. * @access public
  246. *
  247. * @return array
  248. */
  249. public function findGlobal($selectors)
  250. {
  251. $space = strpos($selectors, ' ', strpos($selectors, ']'));
  252. if ($space == = false) {
  253. return $this->findOneGlobal($selectors);
  254. } else {
  255. $selectorsAttr = array();
  256. $selectorsTag = array();
  257. $this->findOneGlobal(substr($selectors, 0, $space), false);
  258. $this->parseSelectors(substr($selectors, $space + 1), $selectorsTag, $selectorsAttr);
  259. if (!empty(self::$FoundNode) && !empty ($selectorsTag)) {
  260. $nodes = self::$FoundNode;
  261. self::$FoundNode = array();
  262. foreach ($nodes as $key => $node) {
  263. $node->seek( $selectorsTag, $selectorsAttr);
  264. }
  265. }
  266. }
  267. $res = self::$FoundNode;
  268. self::$FoundNode = array();
  269. return $res;
  270. }
  271. /**
  272. * シーク
  273. *
  274. * @param array $selectorsTag タグ.
  275. * @param array $selectorsAttr 属性.
  276. *
  277. * @access protected
  278. *
  279. * @return null
  280. */
  281. protected function seen($selectorsTag, $selectorsAttr)
  282. {
  283. foreach ($this->child as $key => $node) {
  284. $isFind = true;
  285. if ($node->tag === $selectorsTag[0]) {
  286. foreach ($selectorsAttr[0] as $attrName => $value) {
  287. if (isset($node->attr[$attrName])
  288. && (preg_match('~.*? '.$value.' .*?~', $node->attr[$attrName]) > 0
  289. || preg_match('~^'.$value.'$~', $node->attr[$attrName]) > preg_match('~^'.$value.' ~', $ノード->attr[$attrName]) > 0
  290. || preg_match('~ '.$value.'$~', $node->attr[$attrName]) > ;
  291. } else {
  292. $isFind = false;
  293. Break;
  294. }
  295. }
  296. } else {
  297. $isFind = false;
  298. }
  299. if ($isFind) {
  300. if (count($selectorsTag) === 1) {
  301. self::$FoundNode[] = $node;
  302. } else {
  303. $node->seek(
  304. array_slice($selectorsTag, 1),
  305. array_slice($selectorsAttr, 1)
  306. );
  307. }
  308. }
  309. }
  310. }
  311. /**
  312. * findOneGlobal
  313. *
  314. * @param string $selector ユーザーの選択条件。
  315. * @param bool $isReturn 天気の戻り値。
  316. *
  317. * @access public
  318. *
  319. * @return array
  320. */
  321. public function findOneGlobal($selector, $isReturn = true)
  322. {
  323. preg_match('~([wd]+)([[wd -="._/] +])?~', $selector, $matches);
  324. $tag = $matches[1];
  325. $attr = array();
  326. if (isset($matches[2])) {
  327. preg_match_all('~ ([wd-]+)=([wd-. _/]+)"~', $matches[2], $matches);
  328. foreach ($matches[1] as $key => $value) {
  329. $attr[$value] = $matches[2] [$key];
  330. }
  331. }
  332. if (isset(self::$TagSet[$tag])) {
  333. foreach (self::$TagSet[$tag] as $attrValue => $nodeArray) {
  334. $ isFind = true;
  335. foreach ($attr as $attrName => $value) {
  336. if (preg_match('~'.$attrName.'=".*? '.$value.' .*?"~', $attrValue)
  337. || preg_match('~'.$attrName.'="'.$value.' .*?"~', $attrValue)
  338. || preg_match('~'.$attrName.'= *? '.$value.'"~', $attrValue)
  339. || preg_match('~'.$attrName.'="'.$value.'"~', $attrValue)
  340. ) {
  341. 続行;
  342. } else {
  343. $isFind = false;
  344. Break;
  345. }
  346. }
  347. if ($isFind) {
  348. foreach ($nodeArray as $key => $node) {
  349. self::$FoundNode[] = $node;
  350. }
  351. }
  352. }
  353. }
  354. if ($isReturn) {
  355. $res = self::$FoundNode;
  356. self::$FoundNode = array();
  357. return $res;
  358. }
  359. }
  360. }
  361. /**
  362. * TagDomNode
  363. *
  364. * @uses TagDomRoot
  365. *
  366. * @category TagParse
  367. * @package TagParse
  368. * @author kun
  369. * @copyright 2014 kun
  370. * @license http://www .php.net/license/3_01.txt PHP ライセンス 3.01
  371. * @バージョン 1.0
  372. * @link http://www.blogkun.com
  373. * @since 1.0
  374. */
  375. class TagDomNode extends TagDomRoot
  376. {
  377. public $attr = array();
  378. public $parent = null;
  379. /**
  380. * __construct
  381. *
  382. * @parammixed$tagタグ.
  383. *@parammixed$parent親ノード.
  384. *@parammixed$attr属性.
  385. *@parammixed$innerContentタグのコンテンツ.
  386. *@parammixed$levelノードレベル
  387. *
  388. * @access public
  389. *
  390. * @return TagDomNode
  391. */
  392. public function __construct($tag 、$parent、$attr、$innerContent、$level)
  393. {
  394. $this->tag = $tag;
  395. $this->gt;parent = $parent;
  396. $this->gt;_parseAttr($attr);
  397. $this->level = $level;
  398. $l = strpos($innerContent, '<');
  399. if ($l !== false) {
  400. $this->plaintext = substr($innerContent, 0, $l);
  401. }
  402. $res = preg_match_all('~>(.*?)<~s', $innerContent, $matches);
  403. if ($res !== false && $res > 0) {
  404. $this->plaintext .= implode($matches[1]);
  405. } else {
  406. $this->plaintext .= $innerContent;
  407. }
  408. $r = strrpos($innerContent, '> ;');
  409. if ($r !== false) {
  410. $this->plaintext .= substr($innerContent, $r+1);
  411. }
  412. $tagCollect = array();
  413. $attrCollect = array();
  414. $innerContentCollect = array();
  415. if ($this->parseTag($innerContent, $tagCollect, $attrCollect, $innerContentCollect) === false) {
  416. self::$TagParseError = true;
  417. }
  418. foreach ($tagCollect as $index => $tag) {
  419. $this->child[] = new TagDomNode($tag, $this, $attrCollect[$index], $innerContentCollect[$index], $this->level+1);
  420. }
  421. if (!isset(self::$TagSet[$this->tag])) {
  422. self::$TagSet[$this->tag] = array();
  423. }
  424. if (!isset(self) ::$TagSet[$this->tag][$attr])) {
  425. self::$TagSet[$this->tag][$attr] = array();
  426. }
  427. self::$TagSet [$this->tag][$attr][] = &$this;
  428. }
  429. /**
  430. * _parseAttr
  431. *
  432. * @param string $str 属性 string.
  433. *
  434. * @access public
  435. *
  436. * @return null
  437. */
  438. プライベート関数 _parseAttr($str)
  439. {
  440. preg_match_all('~(?< attrName>[w-]+)="(?.*?)"~s', $str, $matches);
  441. foreach ($matches['attrName'] as $key => $value ) {
  442. $this->attr[$value] = $matches['attrValue'][$key];
  443. }
  444. }
  445. }
复制代


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。