- はじめに -
この記事は Webスクレイピング Advent Calendar 2017 - Adventar の1日目の記事です。
近年では、Pythonが様々な場面で使われるようになりました。
Webからデータを取ってくる際のスクリプトとして利用し、そのままデータを機械学習における学習データとするといった案件も多く見るようになっています。
ありがたい事に本年度書きました以下の記事は、はてなブログに投稿されたPython関連の記事の中で歴代はてブ数1位だそうです。
Webスクレイピングも日に日に情報が増え、様々なパッケージやフレームワークによって手軽になっています。
本記事は、スクレイピングやクローラを記述する際に抜けがちな、「規約」について記載するものです。
スクレイピングの間隔はどうすればいい?規約は?違法でないの?という人のために法律等もまとめています。
追記2019/01/07:
著作権法が改正され、機械学習モデリングなどに使えるスクレイピング関連のデータの取扱も変わりました。
以下の記事が詳しいので一読しておくと良いでしょう。
www.itmedia.co.jp
- Webスクレイピングにおけるルール -
Webスクレイピングまとめ記事の後半で記載しているが、ネット上のデータを収集して取り扱う上での著作権法、対象のWebサーバに適切にアクセスし身元の開示や負荷を考える上での動産不法侵入の2つを特に気にすべきである。
よりシンプルに以下にまとめてあるため、参考に。
https://vaaaaaanquish.hatenablog.com/entry/2017/06/25/202924#法律の話
著作権法
前者の著作権法は、スクレイピングしたデータでデータセットを作って公開したりしたらダメ、データ分析に使用する場合は他法律に触れなければOK等といった内容で、SNS等でもよく議論に上がる知的財産権に属する内容である。
日本におけるスクレイピングでの著作権の取扱は「つまるところ、データ分析や教育、引用等の認められた利用の範囲内であれば、スクレイピング行為自体は著作権法上認められた行為」となっている。これについては、以下記事が最も参考になる。
「日本は機械学習パラダイス」 その理由は著作権法にあり - ITmedia NEWS
公開されている情報の利用、機械学習におけるデータの取得等は、著作権法においては問題ない。
しかし得たデータの分析においては問題ないが、それらを公開したり、そのデータを直接的に利用して金銭を得る事は問題となる。
また当然、各サービスの規約や後述する動産不法侵入、robot.txtによって制限されている場合がある。
また、取得するスクリプトの公開等についても現状グレーとなっている。
ちなみに取得著作物が自由に使えるパターンについては文化庁のWebサイトにある以下を見ておくと良い。
動産不法侵入
後者の動産不法侵入は、スクレイピングするコードを書いたのだから意図性がある、相手のサーバへの負荷を考慮する、Webページが提示する条項を守るといった内容である。
著作権等で認められた場合でも高負荷なbotを作成した場合、例えツールを利用しただけの場合でも意図性が優先され裁判となる可能性がある。
相手のサーバの負荷、ネットワークの負荷、アクセス先の制約について「知らなかった」で済まされないようになっており、また実際の判例も存在する。
本記事で取り扱う規約保持のスクリプトは、主に後者の動産不法侵入の内容に含まれるWebページが提示する条項を守る「同意の欠如」から身を守るためのものである。
主に以下の内容をPythonスクリプトで実行していくための記事である。
- aタグにrel="nofollow"が設定されていたら辿らない
- robots metaタグの属性記載に従う
- HTTP ヘッダーのX-Robots-Tagに従う
- robots.txtに従う
- User-agentなどを正しく設定する
- robot.txt -
metatagやHTTPヘッダーの確認も重要であり、後述するが、まず前提としてrobot.txt周りの話を記述する。
robot.txtとは
robot.txtとはクローラーに対する指示書である。
1994年に採用されたMK1994、および拡張のMK1996、Google, Yahoo and Microsoftが2008年に出してきたGYM2008、2007年のACAP等が存在し、各検索サービス等が対応したりしていなかったりというのが現状である。
そもそもは検索エンジンのクローラに対する指示書。
これ以上の歴史的経緯は割愛するが、気になる場合は以下追っていくと良。
Nikita The Spider * Articles and News
Robots Exclusion Standard - WikipediaAutomated Content Access Protocol - Wikipedia
Webスクレイピング、クローリングをする上では、HTACCESS等より拘束力はないが、まずはじめにrobot.txtの準拠をしておくと良い。
robot.txtの拘束力については、「法として定義されたルールではない」「検索エンジンに向けたものでありスクレイピングにおいて準拠すべきか」などの議論が幾度となく繰り返されているが、そちらについてはここでは強く言及しないものとする。
拘束力についての議論もありながら、現実robot.txtの準拠については事件の判例にも関わっている。
検索エンジンと著作権 - 三浦基/小林憲一
フィールド対Google事件 - Wikipedia
Google、新聞記事掲載が著作権侵害とするベルギーの判決にコメント
また、以下日本における「1回URL叩いたら1秒Sleepしましょう」という言説の元となっている、国立国会図書館法に関連する資料にもrobot.txtに関する記載がある。
国立国会図書館法によるインターネット資料の収集について
この国立国会図書館の資料については、言及しておく。
岡崎市立中央図書館事件について調べ、容疑者のブログ等をよく読むと理解できるはずだが、「1アクセスごとに1秒の間隔」という基準に意味は何の意味もなく、これらの言説は無視すべきである(筆者はそういった基準を国立国会図書館が公開している事自体どうかと思う)。この岡崎市中央図書館の件は、スクレイピング時に間隔を開けていたにも関わらず、サーバ側の不具合によって高負荷と判断され、逮捕、起訴猶予となった。高木浩光氏のブログにもあるように、明らかな誤認逮捕であり、警察、検察ならびに岡崎市の技術理解が乏しい事が分かったというだけの事件ではあったものの、この事件のように「相手のサーバにとっての高負荷」が発生してしまうだけで裁判となる可能性があり、その事について我々は肝に命じておくべきである。
要は「節度」となるのだが、実際の判例を見るに準拠しておいて損はないだろう。
これらが気になる場合は、以下の書籍に歴史的経緯や判例の項目があるため、一読すべきである。
Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術
- 作者: 渋川よしき
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/06/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
robot.txtフォーマット
robot.txtはドメイン直下に配置されるテキストファイルであり、Webスクレイピングやクローリングを行う際、以下のように読みに行く事になる。
https://hogehoge.com/robot.txt
以下にrobot.txtのsampleを示す。
User-agent: * Disallow: * User-agent: Googlebot Allow: * Disallow: /private
上記は「Googleクローラーは/private以外どこでもアクセスしていいけど、それ以外のbotは全て禁止する」という指示を記したものである。
基本的にMK1994のSyntaxに従っていれば、python内のurlib.robotparserでparseする事が出来る。
後述するように、自作パーサーを作る他、Robotexclusionrulesparser、reppyといったパーサーパッケージも存在する。
また、一部Webスクレイピングまとめ記事でも紹介したようなmechanizeパッケージ等、自動でrobot.txtを読み込んでキャッシュしておき、規約に応じたスクレイピングが可能なものもある。
# mechanize # set_handle_robots(False)しなければ以下エラーになる httperror_seek_wrapper: HTTP Error 403: request disallowed by robots.txt
http://wwwsearch.sourceforge.net/mechanize/
後述するようにスクレイピングのためのフレームワークとしても有名なScrapyにもそれらを適応するパラメータが存在する。
- Pythonスクリプトでの規約の読み込み -
上記の内容を踏まえ、Pythonで各規約情報を読み込むスクリプトを示す。
ここでは「aタグのrelチェック」「robot metaの検出」「HTTPヘッダーのチェック」に加えて、既存のPython謹製パーサーによるrobot.txtのパースについて記述する。
aタグのrelチェック
aタグにはrel属性でnofollowが指定されている場合がある。
rel="nofollow"はスパムコメントに対する防御策として提案され、コメント欄等にスパムURLが投稿される可能性のある箇所に自動的にnofollowを付ける事で、GoogleやYahooの検索エンジンにおけるリンク先考慮のPageRankを下げないように設置するものである。
nofollow - Wikipedia
RFC 3667 - IETF Rights in Contributions
RFC 3668 - Intellectual Property Rights in IETF Technology
近年ではSEO対策に加え、スクレイピング負荷対策等に使われる場合がある。
robot.txtに比べスクレイピング、クローリングに対する拘束範囲は小さいが、rel="nofollow"されたリンクはWebサイト作者の意図しないリンクである可能性が高いため、処理しておくと良い。
BeautifulSoupならfindAllの引数としてlambdaや正規表現が渡せるので、それらを利用する。
from bs4 import BeautifulSoup import re html = """ <html><body> <a href="sample.com" /> <a rel="nofollow" href="badsample.com" /> <a rel="nofollow" href="badsample.com" /> <a href="sample.jp" /> <a href="sample.ne.jp" /> </body></html> """ soup = BeautifulSoup(html, "html5lib") # hrefが設定されておりrelにnofollowが設定されてないaタグを探す links = soup.findAll('a', href=True, rel=lambda x: "nofollow" not in str(x).lower()) for link in links: print(link['href'])
lxmlを使って解析している場合はrel属性のついたlinkを探せるfind_rel_linksがある。
import lxml.html h = lxml.html.fromstring(html) for x in h.find_rel_links("nofollow"): print(lxml.html.tostring(x))
robot metaの検出
クローリングされないため、Webサイトにmetatagとしてnofollow、noarchive、noindexを設置する方法がある(他にもnocacheや特定サービス拒否のcontent属性がある)。noarchiveであればそのページを保存しないし、nofollowがついていれば以降リンクを走査しないようにしておくと安全。
http://www.robotstxt.org/meta.html
HTML 4.01仕様における記載
BeautifulSupの解析器にLambdaを投げるのが吉。
from bs4 import BeautifulSoup html = """ <html><body> <meta content="nofollow" name="robots"/> <meta content="noarchive" name="robots"/> <meta content="noindex" name="robots"/> <a href="sample.com" /> </body></html> """ soup = BeautifulSoup(html, "html5lib") meta = soup.find_all('meta', attrs={"name":"robots"}, content=lambda x: "nofollow" in str(x).lower() or "noarchive" in str(x).lower()) print(meta) print(len(meta) > 0)
HTTPヘッダーにおけるX-Robots-Tagのチェック
Googleによると、HTTPヘッダーでも上記metatag設定と同義の事ができるらしい。
Robots meta tag and X-Robots-Tag HTTP header specifications | Search | Google Developers
一応チェックできる。例えばrequestsなら下記。
import requests r = requests.get('https://hogehoge.com') print( "nofollow" not in str(r.headers.get("X-Robots-Tag")) ) print( "noarchive" not in str(r.headers.get("X-Robots-Tag")) )
設定してるWebページがそこまで多い訳じゃないが、見ておくと良さげ。
urllib.robotparserを使ったrobot.txtのparse
21.10. urllib.robotparser — robots.txt のためのパーザ — Python 3.6.5 ドキュメント
Pythonデフォルトで使えるパーサ。
Googleのrobot.txtをparseしてみる。
[https://www.google.com/robots.txt]
以下のように読み込んでcan_fetchしていく
import urllib.robotparser rp = urllib.robotparser.RobotFileParser() rp.set_url("https://www.google.com/robots.txt") rp.read() # エージェントがURLを見れるか if rp.can_fetch("*", "https://www.google.com/search"): print("OK") else: print("NG") # クローラの遅延時間指定パラメータの取得 # なければNone print(rp.crawl_delay("*")) print(rp.request_rate("*"))
参考:Parsing Robots.txt in python - Stack Overflow
Robotexclusionrulesparserを使ったrobot.txtのparse
Robotexclusionrulesparserはurllib.robotparserのBSD License代替スクリプト
Nikita The Spider * A Robot Exclusion Rules Parser for Python
前述したGYM2008とMK1994/96との構文の差で発生する問題(ワイルドカード*をどう読むか)等について触れられており、両者をparseできるようになっている。non-ASCIIやBOM等にも対応。
RobotExclusionRulesParser and RobotFileParserLookalikeの2つのクラスを提供していて前者がwrapperになっている。
BSDライセンスなのでGithubで検索すると導入しているところがちらほら。
以下URLのスクリプトを利用するか、pipで導入する。
http://nikitathespider.com/python/rerp/robotexclusionrulesparser-1.7.1.py
pip install robotexclusionrulesparser
使い方はほぼurllibと同じ。
import robotexclusionrulesparser rerp = robotexclusionrulesparser.RobotExclusionRulesParser() rerp.fetch('https://www.google.com/robots.txt') if rerp.is_allowed('*', '/search'): print("OK") else: print("NG")
get_crawl_delayやparseもあるので結構使える。
複数のWebサイトを取って回る、urllibじゃparseできない時に有用。
reppyを使ったrobot.txtのparse
robot.txtのparseパッケージ
クロール遅延やサイトマップの読み込み、robots.txtをキャッシュしておく機能が備わっている。
GitHub - seomoz/reppy: Modern robots.txt Parser for Python
導入はpipで
pip install reppy
使い方もほぼ上記と同じ。
sitemapが見れるだけでなくHeaderWithDefaultPolicyでこちらのPolicyに合わせてbool値が出せたりする。
from reppy.robots import Robots # This utility uses `requests` to fetch the content robots = Robots.fetch('https://www.google.com/robots.txt') if robots.allowed('https://www.google.com/search', 'robot'): print("OK") else: print("NG") print(robots.agent('*').delay) print(list(robots.sitemaps))
reppyの強みはcacheにある。
from reppy.cache import RobotsCache robots = RobotsCache(capacity=100) robots.allowed('https://www.google.com/search', 'robot') print(robots.cache)
これによりrobotsを使いまわしながら、複数のサイトのrobot.txtを採用しながらスクレイピングを行うことができる。
scrapyではどうすればいいの?
scrapyでは、以上のrobot.txtパーサを利用しなくてもurllibを利用してくれるパラメータが存在する。
ご存知の通り、scrapyのデフォルト設定はスクレイピング先のサーバに負荷をかけまくる極悪設定になっているので、必ず以下くらいを設定しておくと良い。
DOWNLOAD_DELAY = 5 ROBOTSTXT_OBEY = True
負荷を気にするなら、そもそもScrapyを使わないという選択肢も大いにあるが…
- おわりに -
robots.txt、Metatags辺りに対応する方法についてまとめておきました。
最近、辻さんがTwitterで仰っていましたが、robot.txtとnoindexを情報隠ぺいのように使う企業も存在します(下記はrobot.txtとタグの優先順序により失敗していましたが…)。
「CTOが集団痴漢で逮捕」とかの都合が悪い情報開示ページにnoindex付けるような会社はなにやっても駄目 https://t.co/LOmjizOpCs pic.twitter.com/HkgTxohXh2
— 辻正浩 | Masahiro Tsuji (@tsuj) 2017年11月28日
把握した。
— 辻正浩 | Masahiro Tsuji (@tsuj) 2017年11月28日
普通に公開→検索結果に現れる→検索されなくしたい→「そのページを検索結果から除外する」noindexを付与&「そのURLにクローラアクセスを禁止する」robots.txtを編集を同時に実施
→アクセス禁止されているので検索結果除外のnoindexもGoogleが認識できずに検索結果に表示継続中!
直近だと、Ads.txtなるものも出てきました。
Industry Aligns to Adopt ads.txt Specification – IAB Tech Lab
今後クローラ界隈がどうなっていくかは未知ですが、する側もされる側も良識を持って対応しなければいけないというのが現状で、こうしていれば間違いない、批判されないという方法はありません。
日本では解析のためのWebスクレイピングが認められている訳ですが、その中でも全員が節度を持って行動する事が全員のためになると私は思います。
「日本は機械学習パラダイス」 その理由は著作権法にあり - ITmedia NEWS
最後になりましたが、上記に加えてWebスクレイピングを行う際はUser-Agentを適切に設定しましょう。
明日はanoChickさん!期待です!
adventar.org
Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術
- 作者: 渋川よしき
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/06/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る