robots.txtを取得しクロール拒否されていないかチェック②

# 概要

前回は、[robots.txtを取得する処理](https://qiita.com/ishi720/items/d985bb711744ce9864fb)を作成しました。
今回は、`robots.txt`からクロール拒否されていないかチェックする処理を作成したいと思います。


# robots.txtの構成

まずは、サンプルとしてQiitaのrobots.txtを拝借しました。
Qiitaのrobots.txtは以下のような構成になっています。

```text:robots.txt
# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
User-agent: *
Disallow: /*/edit$
Disallow: /api/*
Disallow: /graphql$
Disallow: /policies/td-optout$
Disallow: /search
Disallow: *.md
Disallow: */items/*/revisions
Disallow: */private/*/revisions
Allow:    /api/*/docs$

Sitemap: https://cdn.qiita.com/sitemap-https/sitemap.xml.gz
```

# robots.txtの見方

### User-agent

クローラーの名前を指定します。

`User-agent: *`とすると、すべてのクローラーに伝える内容となります。
`User-agent: GoogleBot`とすると、GoogleBotのみに伝える内容となります。
以下のように書くと、それぞれのクローラーに対して別の内容を伝えることができます。

```robots.txt
User-agent: *
...
...
...

User-agent: GoogleBot
...
...
...
```
### Disallow

クロールを拒否するときに指定します。

### Allow

クロールを許可するときに指定します。
デフォルト許可なので基本的にあまり意味はないと思われがちですが、以下のようにDisallowで禁止した一部を許可するときに有効です。

```robots.txt
User-agent: *
Disallow: /api/*
Allow:    /api/*/docs$
```

### Sitemap

サイトマップの場所を知らせます。
クローラにホームページの構成を伝えるために使われます。

# 実装

クロールしたいURLがクロール可能かどうかをチェックするプログラムを実装します。



## 関数の作成

まずは、`robots.txt`を一行ごとに配列に変換する関数です。


```php
function lineBreakArray($text) {
    $array = explode("\n", $text); // 改行ごとに分割
    $array = array_map('trim', $array); // 各行にtrim()をかける
    $array = array_filter($array, 'strlen'); // 文字数が0の行を取り除く
    $array = array_values($array); // キーを連番に振る
    return $array;
}
```

`User-agent`や`Disallow`といった項目を抽出する関数です。

```php
function itemExtraction($value) {
    $item = preg_replace("/^(.*?):.+/", "$1", $value);// 項目を抽出する
    $item = preg_replace("/^(\s|　)/", "", $item); // スペース削除
    $item = mb_strtolower($item); // 小文字にする
    return $item;
}
```

`User-agent`や`Disallow`の:（コロン）より右側の情報を抽出する関数です。

```php
function conditionExtraction($value) {
    $condition = preg_replace("/.*?:(.+)/", "$1", $value); // 判定を抽出する
    $condition = preg_replace("/#.*/", "", $condition); // コメントアウト以降を削る
    $condition = preg_replace("/^(\s|　)/", "", $condition); // スペース削除
    return $condition;
}
```

URLがクロール許可されているかチェックする関数です。

```php
/*
 * robots.txtのチェックを行う
 * 第三引数の$userAgentは省略可
*/
function robotsCheck($requestUrl, $robotsArray, $userAgent = "*") {

    $crawl_flg = true;
    if ($userAgent == "") {
        $userAgent = "*";
    }

    if (array_key_exists($userAgent, $robotsArray)) { //指定されたuserAgentの場合
        foreach ($robotsArray[$userAgent]['disallow'] as $key => $value) {
            if (preg_match('~'. asteriskRegexp($value) .'~',$requestUrl)) {
                $crawl_flg = false;
            }
        }
    } elseif ($robotsArray['*'] ) { //指定されたuserAgentが無い場合は*を使う
        foreach ($robotsArray['*']['disallow'] as $key => $value) {
            if (preg_match('~'. asteriskRegexp($value) .'~', $requestUrl)) {
                $crawl_flg = false;
            }
        }
    }
    return $crawl_flg;
}
```

*(ワイルドカード)を正規表現で使えるように変換する関数

```php
function asteriskRegexp($str) {
    return preg_replace("~(\*)~", ".*?", $str);
}
```

## メイン処理

`$robotsTxt`は、[前回の記事](https://qiita.com/ishi720/items/d985bb711744ce9864fb)で取得した`robots.txt`が格納されています。
これを実行することで、クロールが許可されているかチェックできるようになりました。

```php

$requestUrl = 'https://qiita.com/ishi720'; // クロールしたいURLを指定
$myUserAgent = 'myUserAgent'; // ユーザーエージェントを指定

// 1行ごとに配列格納
$lineBreakRobotsArray = lineBreakArray($robotsTxt);

$robotsArray = array();
$userAgentArray = array();
$userAgentNum = 0;

foreach ($lineBreakRobotsArray as $key => $value) {
    //コメント行を削除
    if (preg_match('/^#/', $value)) {
        continue;
    }

    $item = itemExtraction($value); //itemを抽出
    $condition = conditionExtraction($value); //conditionを抽出

    if (preg_match('/^user-agent$/', $item)) { //User-agentが含まれている場合
        if ($userAgentNum + 1 !=  $key) {
            // 1回目のuserAgentが来た場合
            $userAgentArray = array();
        }
        $userAgentArray[] = $condition;
        $userAgentNum = $key;
    } elseif (preg_match('/^(disallow|allow|crawl-delay)$/', $item)) {
        foreach ($userAgentArray as $userAgent) {
            $robotsArray[$userAgent][$item][] = $condition;
        }
    } elseif (preg_match('/^sitemap$/', $item)) {
        $robotsArray[$item][] = $condition;
    }
}

$crawl_flg = robotsCheck($requestUrl, $robotsArray, $myUserAgent);


// 結果の出力
if ($crawl_flg) {
    print("クロールは拒否されていません");
} else {
    print("クロールは拒否されています");
}
```





# 終わりに

クロール拒否されていないかチェックする処理を紹介しました。
`robots.txt`に準ずることで、お行儀の良いクローラが作成できます。
少しでも参考になれば幸いです。

ここまで読んでいただきありがとうございました。