Hatena::ブログ(Diary)

I* HACK! ウェブ関連のタレコミ

2017-05-12

Scrapyでサイトをクロールし、ElasticSearchでサイトの概要を把握する (3)

16:49

前回までで、Scrapyでクロールしたページ情報をElasticSearchにインデックスするところまで設定したが、

実はあれだけだと、うまくESやkibana上で検索できなかったりする。

デフォルトではDynamic Mappingといって

データをPOSTすれば勝手にフィールドなどを作ってくれるが、url文字列をうまく扱ってくれなかったり、細かい

データ型の指定や、analyzerの指定ができない。

https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-mapping.html


なので、scrapyから送る属性をelasticsearchにマッピングしてあげる。


今回は、kibanaのdev toolを使って下記のようなマッピングを行った。

#一度インデックスを削除
DELETE scrapy-2017

#マッピングルールを設定
# - タイトルやdescriptionは日本語解析ができるように
# - URLはngramでなど
PUT scrapy-2017
{
  "settings": {
    "index": {
      "analysis": {
        "tokenizer": {
          "ja_text_tokenizer": {
            "type": "kuromoji_tokenizer",
            "mode": "search"
          },
          "ngram_tokenizer": {
            "type": "ngram",
            "min_gram": 2,
            "max_gram": 2,
            "token_chars": [
              "letter",
              "digit",
              "punctuation",
              "symbol"
            ]
          }
        },
        "analyzer": {

          "ja_text_analyzer": {
            "tokenizer": "ja_text_tokenizer",
            "type": "custom",
            "char_filter": [
              "icu_normalizer"
            ],
            "filter": [
              "kuromoji_part_of_speech"
            ]
          },
          "ngram_analyzer": {
            "tokenizer": "ngram_tokenizer"
          }
        }
      }
    }
  },
  "mappings": {
    "crawledpage": {
      "dynamic": "strict",
      "properties": {
        "title": {
          "type": "text",
          "analyzer": "ja_text_analyzer"
        },
        "description": {
          "type": "text",
          "analyzer": "ja_text_analyzer"
        },
        "url": {
          "type": "text",
          "analyzer": "ngram_analyzer",
          "copy_to": "url_raw"
        },
        "path": {
          "type": "keyword",
          "index": "not_analyzed",
          "ignore_above": 256
        },
        "host": {
          "type": "keyword",
          "index": "not_analyzed",
          "ignore_above": 256
        }, 
        "referer": {
          "type": "text",
          "analyzer": "ngram_analyzer"
        },
        "word_count": {
          "type": "integer"
        },
        "status": {
          "type": "integer"
        }
      }
    }
  }
}

これでkibanaなどで適切に検索ができるようになった。

Scrapyでサイトをクロールし、ElasticSearchでサイトの概要を把握する (2)

16:09

Vol1の続き。


今回はScrapyでクロールし、その結果をElasticSearchにインデックスするところまで。


Scrapyのプロジェクトを作成

$ scrapy startproject scrapy_tutorial

ElasticSearchとの連携設定

$ cat scrapy_tutorial/settings.py
ITEM_PIPELINES = {
    'scrapyelasticsearch.scrapyelasticsearch.ElasticSearchPipeline': 500
}

ELASTICSEARCH_SERVERS = ['localhost']
ELASTICSEARCH_INDEX = 'scrapy'
ELASTICSEARCH_INDEX_DATE_FORMAT = '%Y'
ELASTICSEARCH_TYPE = 'items'
ELASTICSEARCH_UNIQ_KEY = 'url'  # Custom uniqe key

実はそんなに接続設定は多くなかったりする。

気にしないといけないのは、インデックスのフォーマットぐらい。

今回は、年ごとにインデックスを作っていく前提でインデックス名を設定した

(例えば、scrapy-2017, scrapy-2018.....)


Scrapyのコーディング

今回は、CrawlSpiderを使ってクローラを作成。

Python初めてだし、日本語の情報も少なく、細かい設定しようとするとはまって結構時間かかりました。

#基本的にはLinkExtractorのコールバックでElasticSearchへ送るITEMを作成
#それ以外は割愛・・
def parse_items(self, response):
....
       return item


#items.py には下記属性を定義
#pathには、urlのディレクトリパスを第二階層まで格納→各階層ごとのページボリュームを計算したかったため
    url = scrapy.Field()
    host = scrapy.Field()
    path = scrapy.Field()
    referer = scrapy.Field()
    title = scrapy.Field()
    description = scrapy.Field()
    word_count = scrapy.Field()
    status = scrapy.Field()

scrapyを実行して、elasticsearchにインデックスされるか確認

$ scrapy crawl myspider
$ curl -XGET 'localhost:9200/scrapy-2017/_search'
#クロールされたページ情報が表示されれば成功

Scrapyでサイトをクロールし、ElasticSearchでサイトの概要を把握する (1)

15:48

サイトリニューアルなどで、既存のサイト状況を把握するためのツールとして

サイトをScrapyでクロールし、その統計情報などをElasticSearchにインデックスしてkibanaで解析することを思いついたので、やってみた。

CentOS6へセットアップします


ElasticSearch のインストール

今回は最新の5.x系を利用した。

# rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
# vi /etc/yum.repos.d/elasticsearch.repo
[elasticsearch-5.x]
name=Elasticsearch repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

# yum search java | grep 1.8.0-openjdk
# yum install java-1.8.0-openjdk
# alternatives --config java
# service elasticsearch start

動作確認

こんなのがかえってきたらOK

$ curl -XGET 'localhost:9200/?pretty'
{
  "name" : "UPusCPa",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "YRaDwsW4SIG0SraDDKMPTQ",
  "version" : {
    "number" : "5.2.1",
    "build_hash" : "db0d481",
    "build_date" : "2017-02-09T22:05:32.386Z",
    "build_snapshot" : false,
    "lucene_version" : "6.4.1"
  },
  "tagline" : "You Know, for Search"
}

日本語解析用プラグインインストール

コマンド一発でインストールしてくれて便利。

$ cd /usr/share/elasticsearch
#kuromoji 日本語形態素解析エンジン
$ sudo bin/elasticsearch-plugin install analysis-kuromoji
# 大文字小文字全角半角の正規化ができるプラグイン
$ sudo bin/elasticsearch-plugin install analysis-icu

#プラグインを有効化
$ sudo service elasticsearch restart

#設定ファイルの場所はここ
/etc/elasticsearch/elasticsearch.yml

クロールした情報を可視化するためにKibanaをインストール

# vi /etc/yum.repos.d/kibana.repo
[kibana-5.x]
name=Kibana repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

$ sudo yum install kibana
$ sudo vi /etc/kibana/kibana.yml

server.host: "192.168.33.11"
~
elasticsearch.url: "http://localhost:9200"

$ sudo service kibana start

接続確認

http://192.168.33.11:5601/


python2.7のインストール

$ sudo yum install centos-release-scl-rh
$ sudo yum install python27

#有効化
$ scl enable python27 bash

#恒久的有効化
source /opt/rh/python27/enableを.bashrcに書いておく

Scrapy+ES接続用モジュールインストール

# pip install --upgrade pip //pip 7から9にアップグレードしないとエラーが出た
# pip install lxml
# pip install cryptography
# pip install ScrapyElasticSearch

これで一通り準備完了