2011-05-16 20 views
0

我一直在我的ErrorDocument頁面上運行腳本,該腳本在引用者是我的站點時記錄並通過電子郵件發送給我。最近我以爲我可以使用相同的腳本來記錄外部鏈接。我試圖檢索外部頁面標題,如果它們顯示一個404留在我的網站上的錯誤消息,如果沒有,然後做重定向。這是可行的,但即使網站可用,速度也很慢。比直接訪問外部頁面慢得多。在PHP中重定向之前檢查鏈接有效性的最快方法

我認爲這個問題必須躺在我是如何檢索頭(見function is404),而不是在錯誤報告或全部preg_replace的,因爲它的工作原理相當快,當我使用它在我的ErrorDocument的,但無論如何,我會發布所有代碼以防其他地方出現問題。

題外話: 我還沒有決定是否要部署還沒有這個,很明顯它是依賴於獲取速度問題固定的,但我想知道你的意見是對這種方法假定它工作加快速度的。我的網站的好處是明確的,它使用戶停留在網站上,而不是傾倒到可能無法解讀的404上,並讓我知道斷開的鏈接。但是,如果外部頁面具有自定義404,那麼在斷開鏈接後用戶可能會更好。我試圖通過在錯誤消息中包含指向404的鏈接並檢查域的可用性並提供一個指向它的鏈接(如果可用)來抵消這一點。其他的東西也是可行的,例如鏈接到谷歌的結果。那麼,如果你遇到了這個問題,你會感到高興,矛盾,輕度惹惱,還是反感?

更新: 我在最後還是決定去一個框架式的解決方案,由梅爾,因此我在iframe加載外部頁面,並在任何允許用戶報告打破了頂部的小酒吧的建議鏈接應該存在。這個解決方案中存在的問題是,如果我實際上沒有重定向頁面,如果用戶希望爲其添加書籤,他們最終會得到一個指向我的重定向腳本的鏈接(除非引用者是我的網站,否則它會被阻止;所以他們會鏈接到禁止頁面)。這通過一個簡單的window.location=http://external.site修復,但問題是何時調用它。 iframe onload事件觸發頁面是404還是沒有,所以這是不好的,並使用一個簡單的超時使頁面顯然已經加載只是看起來醜陋與我的「有這個頁面錯誤」消息在頂部。我去了一個組合。當onload觸發時,我正在對腳本進行Ajax調用,該腳本從我的服務器檢查外部站點的狀態。 (我使用的方法與我下面的方法相同,但是這次似乎運行的非常好,我認爲速度問題在其他地方,儘管我不知道在哪裏)。我認爲使用Ajax調用(使用超時)將防止Adrian指出的站點阻塞問題。如果我的服務器認爲該頁面不是404,我立即刪除橫幅。如果它認爲這是我設置了一個超時,並留下標誌(希望)足夠長的用戶想要點擊它。正如答案中指出的那樣,有些情況下我的404代碼與用戶看到的不匹配,但是最糟糕的情況是錯過了一個錯誤的機會來報告錯誤或在罰款期間停留一段時間的橫幅頁。希望大部分時間都能解決。對於沒有啓用JavaScript的用戶,我只需使用meta refresh並立即轉發到鏈接。如果我在這裏出了問題或錯過了其他重要的東西,我會很感激你的反饋。謝謝。


下面的代碼:

// /../php/urlfns.php 

    1 <?php 
    2 function hasProtocol($uri) { 
    3  return preg_match('#^.+://#', $uri); 
    4 } 
    5 
    6 function getDomain($uri, $keepproto=false) { 
    7  return preg_replace('#^((.+://)?(.+?))\/.*#', $keepproto?'${1}':'${3}', $uri.'/'); 
    8 } 
    9 
10 function getBaseDomain($uri, $keepproto=false) { 
11  $dom = getDomain($uri, $keepproto); 
12  if(!$keepproto) { 
13   return preg_replace('#.*\.(.+\..+)$#', '${1}', '.'.$dom); 
14  } 
15  else 
16  if(hasProtocol($dom)) { 
17   return preg_replace('#^(.+://).*\.(.+\..+)$#', '${1}${2}', $dom); 
18  } 
19  else { 
20   return preg_replace('#^.*\.(.+\..+)$#', '${1}', '.'.$dom); 
21  } 
22 } 
23 
24 function isOnDomain($uri, $domain, $allowsubs=false) { 
25  $uridom = getDomain($uri); 
26 
27  if(!$allowsubs) { 
28   return ($uridom===$domain); 
29  } 
30  else { 
31   $basedom = getBaseDomain($domain); 
32   return (strlen($uridom) - strlen($basedom) === strrpos($uridom, $basedom)); 
33  } 
34 } 
35 
36 function stripDomain($uri) { 
37  return preg_replace('#^(.+://)?.*?(/.*)#', '${2}', $uri); 
38 } 
39 
40 function is404($uri) { 
41  stream_context_get_default(array(
42   'http'=>array(
43    'method' => 'HEAD' 
44  ) 
45 )); 
46  $hds = @get_headers($uri); 
47  return (!$hds || strpos($hds[0], ' 404 ') !== false); 
48 } 
49 ?> 

**

// /../php/badlink.php 

    1 <?php 
    2 require_once('urlfns.php'); 
    3 
    4 function reportbadlink($lntype='generic', $subdomains=false, $blcache='../badlinks/') { 
    5 
    6  if(isset($_SERVER['HTTP_REFERER']) && 
    7   isOnDomain($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'], $subdomains)) { 
    8 
    9   $reffile = $_SERVER['DOCUMENT_ROOT'].'/'.stripDomain($_SERVER['HTTP_REFERER']); 
10 
11   $blid = md5($lntype.$_SERVER['REQUEST_URI'][email protected]($reffile)); 
12   $blfil = dirname(__FILE__).'/'.$blcache.'/'.$blid; 
13 
14   if(!file_exists($blfil)) { 
15    $report = ' On: '.$_SERVER['HTTP_REFERER']."\n". 
16      ' To: '.$_SERVER['REQUEST_URI']."\n". 
17      'Type: '.$lntype."\n". 
18      ' #: '.$blid."\n\n"; 
19 
20    file_put_contents($blfil, $report); 
21    mail('[email protected]', 'A broken link has been found.', $report); 
22   } 
23  } 
24 } 
25 ?> 

**

// /ssi/extlink.php 
    1 <? 
    2  require_once('../../php/urlfns.php'); 
    3  $uri = $_SERVER['QUERY_STRING']; 
    4 
    5  if(!hasProtocol($uri)) { 
    6   $uri = 'http://'.$uri; 
    7  } 
    8  
    9  if(!is404($uri)) { 
10   header('Location: '.$uri); 
11   exit; 
12  } 
13   
14  header("Cache-Control: no-cache, must-revalidate"); 
15  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); 
16 
17  require_once('../../php/badlink.php'); 
18  reportbadlink('external'); 
19   
20  $baseuri = getDomain($uri,true); 
21  if(preg_match('#^'.$baseuri.'/?$#', $uri) || is404($baseuri)) { 
22   $baseuri = null; 
23  } 
24     
25  $errtype = 'External page not found'; 
26  $errmesg = '<p><em><a href="'.$uri.'">'.$uri.'</a> was not found.</em></p>'. 
27    '<p>This may be because the site is temporarily unavailable, or it may be a broken link.</p>'. 
28    ($baseuri ? '<p><a href="'.$baseuri.'">'.$baseuri.'</a> appears to be available however and you may find '. 
29    ' what you are looking for there.</p>' : ''); 
30 ?> 
31 <!DOCTYPE html> 
32 <html lang="en" dir="ltr"> 
    ... 

回答

2

我絕對不會部署這個,只是因爲你的假設是,如果你可以到達該網站,那麼你的用戶也可以。然而,如果你不能,但用戶可以因爲本地化的路由問題,你已經給他們發送了一條紅色鯡魚。

我寧願部署一個框架解決方案,在小頂部框架中,您有一個按鈕可以將鏈接報告爲已損壞。與Google圖片的右側窗格類似。然後,您可以在鏈接報告的最後x天內報告鏈接多少次,並在鏈接旁邊顯示鏈接數量。

對我來說,最終用戶會好得多,然後有人聲稱該網站不適合我。 「我不相信你,你在那裏做什麼陰暗的事情,我只是複製鏈接,粘貼到我的地址欄中,自己去那裏!」。再一次,我是一個憤世嫉俗的人。

+0

謝謝,你說得很好。如果它對最終用戶體驗產生不利影響,那絕對是我不想做的事情。我和你一樣,是一個憤世嫉俗的人,會和你一樣做,我只是想知道我的大多數用戶是否會一樣。感謝您的輸入。 – tjm 2011-05-16 13:31:10

+0

嗯。我也喜歡這個解決方案,並且想知道如果一次加載到框架中,我可以以某種方式訪問​​響應頭文件(也許通過javascript),從而獲得兩全其美的好處,但快速搜索似乎表明這是不可能的。 – tjm 2011-05-16 14:11:57

1

我會與外部網頁的SQL表,並定期檢查網頁與cron任務。這樣,人們點擊幾乎沒有任何延遲。

+0

我曾希望避免使用SQL,但我可以將它看作一種可能的解決方案。謝謝。 – tjm 2011-05-16 13:32:22

0

您也可以使用fsockopen來打開URL。如果返回的資源有errno of 404那麼它的一個斷開的鏈接。保存在所有的正則表達式

+0

現在關閉閱讀'fsockopen',謝謝。 – tjm 2011-05-16 13:32:42

1

管理員應該在外部鏈接斷開時得到通知的想法非常整齊,但作爲用戶,我不想從您的網站獲取該消息。我不會相信它是一個真正的404,所以我可能會複製該URL並將其粘貼到一個新的選項卡中,以便檢查。這會讓我做更多的工作,而不是幫助我。另外,正如梅爾所說,如果你的支票出錯了怎麼辦?

如果您仍想檢查外部鏈接的有效性,請確保它不會以任何方式影響用戶。使用jodes提到的cron腳本似乎是一個好主意。

有點關係的故事,可能深思:

我已經使用了PHP腳本來檢查圖像中的另一個域的存在(我們的域名,但另一臺服務器上...)和替代如果檢查失敗,圖像href爲默認圖像href。這工作正常了一個星期左右(雖然它使網站有點慢),但星期一我們得到了另一個域(電子商務服務器)的巨大流量高峯,該域名下降,突然我們有加載時間在幾分鐘的網站,我是在工作。

換句話說,這是™

我懵服務器端檢查出真快,爲了再升獲得網站非常糟糕的主意。後來,我構建了一個小型javascript,將附加事件偵聽器添加到圖像上,觸發加載,並用默認圖像替換返回404的任何圖像。它工作的很好,對於少數沒有啓用js的用戶來說,它可以工作,即使它在資源管理器中看起來有點醜陋(破碎的圖標圖標)。

+0

感謝您分享您的觀點和經驗。我正在考慮混合使用jodes和Mels響應的方式。 – tjm 2011-05-16 14:16:33