クラスの可読性 {
// 判定結果のマークビット名を保存
const ATTR_CONTENT_SCORE = "コンテンツスコア";
// DOM 解析クラスは現在 UTF-8 エンコーディングのみをサポートしています
const DOM_DEFAULT_CHARSET = "utf-8";
//判定失敗時の表示内容
const MESSAGE_CAN_NOT_GET = "このページのコンテンツを読みやすく解析できませんでした。";
// DOM 解析クラス (PHP5 に組み込まれています)
保護された $DOM = null;
//解析が必要なソースコード
protected $source = "";
//章の親要素のリスト
private $parentNodes = array();
// 削除する必要があるタグ
// 注: http://www.45it.net から追加のタグを追加しました
private $junkTags = Array("style", "form", "iframe", "script", "button", "input", "textarea",
「noscript」、「select」、「option」、「object」、「applet」、「basefont」、
"bgsound"、"blink"、"canvas"、"command"、"menu"、"nav"、"datalist"、
"埋め込み"、"フレーム"、"フレームセット"、"keygen"、"ラベル"、"マーキー"、"リンク");
//削除する必要がある属性
private $junkAttrs = Array("style", "class", "onclick", "onmouseover", "align", "border", "margin");
/**
*コンストラクター
* @param $input_char 文字列のエンコーディング。デフォルトはutf-8ですが、省略可能です */
function __construct($source, $input_char = "utf-8") {
$this->source = $source;
// DOM 解析クラスは UTF-8 形式の文字のみを処理できます
$source = mb_convert_encoding($source, 'HTML-ENTITIES', $input_char);
// HTML タグを前処理し、冗長なタグを削除します。
$source = $this->preparSource($source); //DOM 解析クラスを生成する
$this->DOM = new DOMDocument('1.0', $input_char);
試してみてください{
//libxml_use_internal_errors(true);
// いくつかのエラー メッセージが表示されますが、問題ありません :^)
if (!@$this->DOM->loadHTML(''.$source)) {
throw new Exception("HTML 解析エラー!");
}
foreach ($this->DOM->childNodes as $item) {
if ($item->nodeType == XML_PI_NODE) {
$this->DOM->removeChild($item) // ハックを削除します ;
}
}
//適切に挿入
$this->DOM->encoding = 可読性::DOM_DEFAULT_CHARSET;
} catch (例外 $e) {
// ...
}
}
/**
* DOM 解析クラスで正確に処理できるように HTML タグを前処理します
*
* @return 文字列 */
プライベート関数 preparSource($string) {
// 解析エラーを避けるために、冗長な HTML エンコード タグを削除します
preg_match("/charset=([\w|\-]+);?/", $string, $match);
if (isset($match[1])) {
$string = preg_replace("/charset=([\w|\-]+);?/", "", $string, 1);
}
// 二重化されたすべての を置き換えます。 の付いたタグタグを削除し、フォントを削除します。
$string = preg_replace("/ [ \r\n\s]* /i", " ", $string );
$string = preg_replace("/gt;]*>/i", "", $string);
// @https://github.com/feelinglucky/php-readability/issues/7を参照
// - http://stackoverflow.com/questions/7130867/remove-script-tag-from-html-content から
$string = preg_replace("##is", "", $string);
トリムを返す($string);
}
/**
* DOM 要素からすべての $TagName タグを削除します
*
* @return DOMDocument */
プライベート関数removeJunkTag($RootNode, $TagName) {
$Tags = $RootNode->getElementsByTagName($TagName);
//注: タグを削除すると結果からもタグが削除されるため、常にインデックス 0 を指定してください。
while($Tag = $Tags->item(0)){
$parentNode = $Tag->parentNode;
$parentNode->removeChild($Tag);
}
$RootNode を返します;
}
/**
* 要素から不要な属性をすべて削除します */
プライベート関数removeJunkAttr($RootNode, $Attr) {
$Tags = $RootNode->getElementsByTagName("*");
$i = 0;
while($Tag = $Tags->item($i++)) {
$Tag->removeAttribute($Attr);
}
$RootNode を返す;
}
/**
* 評価に基づいてページのメインコンテンツのボックスモデルを取得します
* 決定アルゴリズムは http://code.google.com/p/arc90labs-readability/ から取得しました。
※鄭暁さんのブログから転載させていただきました
* @return DOMNode */
プライベート関数 getTopBox() {
// 获得页面全部的章节
$allParagraphs = $this->DOM->getElementsByTagName("p");
// すべての段落を調べて、最高のスコアを持つチャンクを見つけます。
// スコアは次のようなものによって決定されます: の数、カンマ、特殊クラスなど
$i = 0;
while($paragraph = $allParagraphs->item($i++)) {
$parentNode = $paragraph->parentNode;
$contentScore = intval($parentNode->getAttribute(Readability::ATTR_CONTENT_SCORE));
$className = $parentNode->getAttribute("クラス");
$id = $parentNode->getAttribute("id");
// 特別なクラス名を探します
if (preg_match("/(コメント|メタ|フッター|脚注)/i", $className)) {
$contentScore -= 50;
else if(preg_match(
"/((^|\\s)(post|hentry|entry[-]?(content|text|body)?|article[-]?(content|text|body)?)(\\s|$) )/私"、
$className)) {
$contentScore += 25;
}
// 特別なIDを探してください
if (preg_match("/(コメント|メタ|フッター|脚注)/i", $id)) {
$contentScore -= 50;
else if (preg_match(
"/^(post|hentry|entry[-]?(content|text|body)?|article[-]?(content|text|body)?)$/i",
$id)) {
$contentScore += 25;
}
// 見つかった段落にポイントを追加します
// この段落内のカンマにポイントを追加します
if (strlen($paragraph->nodeValue) > 10) {
$contentScore += strlen($paragraph->nodeValue);
}
//保存父元素の判定得分
$parentNode->setAttribute(Readability::ATTR_CONTENT_SCORE, $contentScore);
// 保存章节的父元素,便次快速获取
array_push($this->parentNodes, $parentNode);
}
$topBox = null;
// パフォーマンスのためのインデックスからの代入
// http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=5 を参照してください
for ($i = 0, $len = sizeof($this->parentNodes); $i
$parentNode = $this->parentNodes[$i];
$contentScore = intval($parentNode->getAttribute(Readability::ATTR_CONTENT_SCORE));
$orgContentScore = intval($topBox ? $topBox->getAttribute(Readability::ATTR_CONTENT_SCORE) : 0);
if ($contentScore && $contentScore > $orgContentScore) {
$topBox = $parentNode;
}
}
// この時点で$topBoxが決定されたページコンテンツのメイン要素となるはずです
$topBox を返します;
}
/**
* HTML ページのタイトルを取得します
*
* @return 文字列 */
パブリック関数 getTitle() {
$split_point = ' - ';
$titleNodes = $this->DOM->getElementsByTagName("title");
if ($titleNodes->length
&& $titleNode = $titleNodes->item(0)) {
// @ http://stackoverflow.com/questions/717328/how-to-explode-string-right-to-left を参照
$title = トリム($titleNode->nodeValue);
$result = array_map('strrev',explode($split_point, strrev($title)));
サイズを返します($result) > 1? array_pop($result) : $title;
}
null を返す;
}
/**
* 先頭の画像 URL を取得します
*
* @return 文字列 */
パブリック関数 getLeadImageUrl($node) {
$images = $node->getElementsByTagName("img");
if ($images->length && $leadImage = $images->item(0)) {
return $leadImage->getAttribute("src");
}
null を返す;
}
/**
* ページのメインコンテンツ (Readability 後のコンテンツ) を取得します
*
* @return 配列 */
パブリック関数 getContent() {
if (!$this->DOM) が false を返す;
//ページタイトルを取得します
$ContentTitle = $this->getTitle();
// ページのメインコンテンツを取得します
$ContentBox = $this->getTopBox();
// 適切なトップボックスが見つかったかどうかを確認します。
if($ContentBox === null)
throw new RuntimeException(Readability::MESSAGE_CAN_NOT_GET);
// コンテンツを新しい DOMDocument にコピーします
$Target = 新しい DOMDocument;
$Target->appendChild($Target->importNode($ContentBox, true));
//不要なタグを削除
foreach ($this->junkTags を $tag として) {
$Target = $this->removeJunkTag($Target, $tag);
}
//不要な属性を削除
foreach ($this->junkAttrs as $attr) {
$Target = $this->removeJunkAttr($Target, $attr);
}
$content = mb_convert_encoding($Target->saveHTML(), Readability::DOM_DEFAULT_CHARSET, "HTML-ENTITIES");
//配列の形式で返される複数のデータ
配列を返す(
'lead_image_url' => $this->getLeadImageUrl($Target),
'word_count' => mb_strlen(strip_tags($content), 可読性::DOM_DEFAULT_CHARSET),
'タイトル' => $ContentTitle : null,
'コンテンツ' => $コンテンツ
);
}
関数 __destruct() { }
}
|