【2020年度版】個人用クローラーの開発手順とその注意点

# はじめに
　本記事の目的は、Webコンテンツ収集の自動化に興味ある方が、理想的なクローラーを開発できるようになる事です。そのために、クローラー開発の12ステップを紹介します。
　クローラーとは、Webサイトを巡回し、コンテンツを記録・収集する自動プログラムを言います。その中でも理想的なクローラーとは、法律や各人の倫理観に従い、対象Webサイトの運用を妨害しないものであると私は考えています。
　理想的でないクローラーは、Webサイトからアクセスを拒否されたり、エラー画面に強制遷移されたりします。
　退屈な作業はパソコンにまかせ、トラブルは回避しつつ、あなたの可処分時間を増やしていきましょう。
　※本記事は個人でのクローラー開発を想定しています。
　※本記事の内容はクローラー開発・運用のトラブルを100%防げるものではありません。ご自身の責任と判断でご活用ください。特に、法律に関する記述は、法律家でない著者の解釈を多分に含むものであるため、参考情報程度のご認識でお願いいたします。責任を取ることはできません。
　※Pythonの知識があると、本記事の理解が進みます。

 ■参考サイト（Python入門サイト）
* [プログラミング言語 Python - python.jp](https://www.python.jp/)
* [AI Academy | Python・機械学習・AIを実践的に学べるプログラミング学習サービス](https://aiacademy.jp/)
* [Python3入門編のレッスン一覧 | プログラミング学習サービス【paizaラーニング】](https://paiza.jp/works/python3/primer)

# 01. コンテンツ収集の目的は「個人や家族間で使用する」「Web検索サービスを提供する」「情報解析をする」のいずれかとする。
　Webコンテンツは、すべて誰かの著作物となります。その為、著作権法に従うものとなります。
　著作権法では、次の目的は著作物が自由に使える場合とされています。

* 個人や家族間で使用する。
* Web検索サービスを提供する。
* 情報解析をする。

■参考サイト
* [著作物が自由に使える場合 | 文化庁](https://www.bunka.go.jp/seisaku/chosakuken/seidokaisetsu/gaiyo/chosakubutsu_jiyu.html)

　特に後者二つは、[平成30年改正著作権法](https://www.bunka.go.jp/seisaku/chosakuken/hokaisei/h30_hokaisei/)で適法性がより明確になりましたが、その制約も明確化されましたので、詳細をご確認いただければ幸いです。

■参考サイト
* [弁護士が解説　“平成30年改正著作権法”がビジネスに与える「衝撃」 (1/5) - ITmedia NEWS](https://www.itmedia.co.jp/news/articles/1904/04/news009.html)

　ただし、それらの目的であっても、次の点には共通で注意する必要があります。

# 02. 対象のWebサイトは合法的なコンテンツを掲載しているものにする。
　違法アップロードされたコンテンツを違法なものと認識しつつダウンロードする事は禁止されます。[著作権法改正（令和3年1月1日施行）](https://www.bunka.go.jp/seisaku/chosakuken/hokaisei/r02_hokaisei/)では、コンテンツの種類（ex.テキスト,画像,音声,動画）は問われません。
　著作者がコンテンツの提供に同意しているWebサイトを対象とする事が、強く推奨されます。

■参考サイト
* [改正著作権法が成立。違法ダウンロードなどの対象を拡大：教育とICT Onlin](https://project.nikkeibp.co.jp/pc/atcl/19/06/21/00003/060800083/)

# 03. Webサイトの会員向けコンテンツを対象とする場合は、そのWebサイトの利用規約に従う。
　Webサイトの会員となる為に利用規約に同意した場合は、その利用規約を守る必要があります。利用規約の中でクローラーが禁じられている場合は、そのWebサイトをクローラーの対象とする事はできません。

# 04. Webサイトからクローラーへの指示がされている場合は、その指示に従う。
　Webサイトでは、Webサイト全体または指定のWebページや特定の画面項目に対して、クローラーの動作を指示している場合があります。その指示方法として次のものがあり、それらに従う必要があります。

* Robots.txt……Webサイト単位のクローラー巡回ルール（テキストファイル）
⇒ [Robots.txt の仕様  |  Google 検索デベロッパー ガイド  |  Google Developers](https://developers.google.com/search/reference/robots_txt?hl=ja)

* robotsメタタグ、data-nosnippet、X-Robots-Tag……Webページ単位の巡回ルール（レスポンス内のキーワード）
⇒ [robots メタタグ、data-nosnippet、X-Robots-Tag の仕様  |  Google 検索デベロッパー ガイド](https://developers.google.com/search/reference/robots_meta_tag?hl=ja)

* aタグのrel属性……Webページのハイパーリンク単位での巡回ルール。（HTMLタグの属性）
⇒ [Google に外部リンクの関係性を伝える - Search Console ヘルプ](https://support.google.com/webmasters/answer/96569?hl=ja)

# 05. VPNやプロキシサーバで接続元IPアドレスを非通知とする事は推奨されない。
　接続元のIPアドレスは、接続先のWebサイトに記録され、アクセス遮断や身元の特定で使用される可能性があります。それを回避するために、VPNやプロキシサーバ（接続先から接続元の間に仲介者を挟むサービス）を使用し、接続元のIPアドレスを非通知とする事もできます。（正確には仲介者のIPアドレスが接続先に通知される）
　しかし、WebサイトによってはVPNやプロキシサーバからのアクセスを遮断される事もあります。また、仲介者にすべての通信を託すので、信頼できないサービスを利用すると、情報流出に繋がる危険性もあります。そして、Webサイト側ではIPアドレスでのアクセス遮断ができなくなるため、より上位の対策を行われる可能性が高まります。
# 06. ブラウザの自動操作ツール「 [Selenium](https://www.selenium.dev/)」をクローラーとして流用する。
　ここからは具体的なクローラー開発についてです。
　Webサイトの多くは情報が表示されるまでに、Javascriptでの処理を前提としています。その処理後の情報を得る為に、ブラウザ（正確にはレンダリングエンジン）に処理をさせます。その為に、ブラウザの自動操作ツール「 [Selenium](https://www.selenium.dev/)」をクローラーとして流用する事が推奨されます。Seleniumのインストール手順や基本操作は、下記のサイトが大変参考になりました。

[【完全版】PythonとSeleniumでブラウザを自動操作(クローリング／スクレイピング)するチートシート | たぬハック](https://tanuhack.com/selenium/)

　著者は操作対象のブラウザをFirefoxとしていますが、特にGoogleChromeでも、MicrosoftEdgeでも、お好きなものをお使い下さい。本記事ではFirefox前提で説明していきます。

[Firefoxのダウンロードページ](
https://www.mozilla.org/ja/firefox/new/)

[FirefoxのWebDriverのダウンロードページ](
https://github.com/mozilla/geckodriver/releases)
　※ご使用のOSが対象のものをダウンロードします。

　バックグラウンドで処理を行わせる事（ヘッドレス設定）もできますが、はじめのうちは画面を表示しておく事が推奨されます。画面遷移で失敗する事もあれば、指定した画面項目を取得できない事もあります。また、Webサイトがリニューアルされる事で、全面改修が必要になることもあります。

```python:Firefoxを起動する
from selenium import webdriver
from selenium.webdriver.firefox.options import Options

# オプションの設定
options = Options()
#options.set_headless() # 画面非表示にしたい場合はコメントをはずす。

# ブラウザの起動
driver = webdriver.Firefox(executable_path='FirefoxのWebDriverのパス（ex.~/geckodriver）', options=options)

# 待機時間の設定
driver.implicitly_wait(5) # 画面項目が表示されるまでの最大待機秒
driver.set_page_load_timeout(180) # 画面表示までの最大待機秒
```

# 07. 対象Webサービスのクッキーがある状態でアクセスする。
　大抵のWebサービスは各訪問者のブラウザにクッキーを発行し、そのクッキーで訪問者の管理をしています。そして、大抵のクローラーは初期訪問（クッキーなし）の状態でアクセスしますので、クッキーがあるだけで一般ユーザとして扱ってもらえる可能性が高まります。
　ブラウザで新たなプロファイル（ブラウザのユーザ情報）を作成し、対象のWebサイトに手動でアクセスしておきます。

* [プロファイルマネージャーを使用して、Firefox のプロファイルを作成または削除する | Firefox ヘルプ](https://support.mozilla.org/ja/kb/profile-manager-create-and-remove-firefox-profiles)

　その後に、先のプロファイルのパスをSeleniumにて指定する事で、再訪問（クッキーあり）の状態でクローラーを動かせます。Firefoxのプロファイルは次の場所にフォルダで保管されおり、プロファイルが複数ある場合は更新日時で新規作成したものを判断できます。
` C:/Users/ユーザ名/AppData/Roaming/Mozilla/Firefox/Profiles/` 

```python:プロファイルを設定する
# ブラウザの起動
profiler = webdriver.FirefoxProfile('Firefoxプロファイルのパス（ex.~/Firefox/Profiles/FOLDER-NAME)')
driver = webdriver.Firefox(executable_path='FirefoxのWebDriverのパス', options=options, firefox_profile=profiler)
```

# 08. 同一サイトの任意の別ページへの遷移は、ハイパーリンクのURLを書き換えてクリックする事で行う。
　Seleniumには画面遷移用のコマンドが用意されており、対象のWebサイトに遷移する際は次のコマンドを使用します。このコマンドは、ブラウザのURL欄にURLを直接入力し、画面遷移する動きと同じになります。

```python:別サイトのページへ遷移する
driver.get('任意のURL')
```

　同一サイト内の移動は、ハイパーリンクやボタンをクリックする通常の操作を模倣したコマンドで行います。

```python:同一サイトの別ページへ遷移する
# ハイパーリンクを取得
link = driver.find_element_by_css_selector('CSSセレクター')
# 先の項目をクリック
link.click()
```

　ただし、同一サイト内でも任意のページに遷移したい事があります。（ex. 商品Aの詳細画面からコンテンツを取得した後に、その関連商品として商品Bのリンクが提示された事にして、商品Bの詳細画面に遷移する）
　その際には、ハイパーリンクやボタンでの遷移先URLをJavascriptで書き換え、その項目をクリックする事で遷移します。

```python:ハイパーリンクのURLを書き換え、同一サイトの別ページへ遷移する
# ハイパーリンクを取得
link = driver.find_element_by_css_selector('CSSセレクター')
# 先の項目が画面内に見える位置までスクロール
driver.execute_script("arguments[0].scrollIntoView()", link)
# 先の項目の遷移先URLを任意のものに書き換え
driver.execute_script("arguments[0].setAttribute('href','{}')".format('任意のURL'), link)
# 先の項目をクリック
link.click()
```

　これは同一サイト内の遷移であるにも関わらず、URL欄に直接URLを入力して遷移するような不自然な動作を避けるためのものです。技術的に言えば、同一ドメインのリファラー（画面遷移元URL）が設定されている状態で、Javascriptのクリックイベントを起こしながら遷移する事を目的としています。

# 09. 画面遷移後は、ランダム秒待機する。

　画面遷移後には、ランダム秒待機する処理を入れます。
　これは、Webサイトへの負荷を軽減するためのものでもありますが、画面遷移のトラブルを防ぐためのものでもあります。画面遷移後の待機時間によっては、Javascriptでの処理前に操作した為に、正常に画面遷移できなかったというトラブルがありました。この待機時間は、Webサイトによって変更するのが良いと思います。

```python:ランダム秒待機する
from time import sleep
import random

def get_wait_secs():
  """画面の待機秒数の取得"""
  max_wait = 7.0   #最大待機秒
  min_wait = 3.0   #最小待機秒
  mean_wait = 5.0  #平均待機秒
  sigma_wait = 1.0 #標準偏差（ブレ幅）
  return min([max_wait, max([min_wait, round(random.normalvariate(mean_wait, sigma_wait))])])

sleep(get_wait_secs())
```

# 10. ファイルをダウンロードする際は、ユーザーエージェントとリファラーをリクエストに付ける。
　ファイル、特に画像ファイルのダウンロードは、直リンクや無秩序なダウンロードを避けるために、上記の制約が設定されている事があります。通常のブラウザ操作では、ユーザーエージェント（接続元のOSやブラウザ等の情報）とリファラー（画面遷移元URL）は自動で設定されます。

```python:画像ファイルをダウンロードする
import requests
import shutil

img = driver.find_element_by_css_selector('imgタグへのCSSセレクター')
src = img.get_attribute('src')
r = requests.get(src, stream=True, headers={'User-Agent':'ユーザーエージェント' , 'Referer':driver.current_url})
if r.status_code == 200:
  with open('画面保存先のパス', 'wb') as f:
    r.raw.decode_content = True
    shutil.copyfileobj(r.raw, f)
```

#11. クローラーの起動時間は不定期にする。また、一回の起動時間は数時間程度にする。
　クローラーの起動は、手作業でも良いですが、定期的に予約起動させる事もできます。OSによって変わりますが、次のツールが標準で用意されています。

* Windows - タスクスケジューラ
[【Windows 10対応】タスクスケジューラで定期的な作業を自動化する (1/2)：Tech TIPS - ＠IT](https://www.atmarkit.co.jp/ait/articles/1305/31/news049.html)

* MacOS/Linux - cron
[cron の設定ガイド](https://www.express.nec.co.jp/linux/distributions/knowledge/system/crond.html)

　ただ、正確に同じ時間でWebサイトへアクセスし続けると、その時間にWebサイトの負荷が集中してしまうため、ある程度アクセス時間にブレ幅を用意する事が推奨されます。その手段としては、予約起動した後に、08の手順と同様の手順でランダムな時間停止させる方法があります。

　また、数時間を超えるアクセスも同様であるため、ある程度アクセスし続けたらクローラーを止める処理を入れる事が推奨されます。

# 12. あなたの倫理観でクローラーの運用ルールを追加する。
　上記以外でも、対象Webサイトの運用を妨害しない運用ルールを見つけたら、それを導入していきます。Webサイトを利用させてもらっていることに感謝いたします。

（2020年9月6日 現在）