Python
スクレイピング
Scrapy
クローラー
More than 1 year has passed since last update.

Advent Calendar 2017 クローラー/Webスクレイピング の2日目が空いておりましたので、駆け込みで参加させていただきました!

目的

直近のニュースの概要をスクレイピングし1つのファイルにまとめる

動作環境

Ubuntu16.04でプログラムを作成しております。

NAME="Ubuntu"
VERSION="16.04 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
UBUNTU_CODENAME=xenial

Install方法

今回は pip でインストールしました。

pip install Scrapy

Project作成

今回はニュースを収集するので、「news」という命名でプロジェクトを作成します。

任意のフォルダで以下のコマンドを入力し、プロジェクトを作成します。

scrapy startproject news

そうすると news フォルダが作成されますので、

cd news

と入力し、プロジェクトフォルダ内に移動します。
以下の作業はすべてプロジェクトフォルダ内で実施します。

Project構造

作成したプロジェクトフォルダは下記のような構造になっています。

news/ ← 現時点ではココがカレントフォルダ
    scrapy.cfg
    news/
        __init__.py
        items.py
        pipelines.py
        settings.py
        middlewares.py
        spiders/
            __init__.py

クロール設定の変更

news/settings.py/ というファイルを編集します。

デフォルトだとダウンロード間隔が0になっているため、クロール先に大きな負荷がかかります。
そのためクロール時に5秒間隔を空けるように以下のように設定を変更します。

DOWNLOAD_DELAY = 5

itemの設定

itemはスクレイピングしたデータを格納するオブジェクトです。
今回はプログラム実行時にどのようなデータが格納されているかを確認するために設定しています。

news/items.py/ というファイルを編集します。

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/items.html

import scrapy


class NewsItem(scrapy.Item):
    title = scrapy.Field()
    category = scrapy.Field()
    url = scrapy.Field()
    summary = scrapy.Field()

Source

news/spider/ 配下にスクレイピングのソースファイルを配置します。
今回は scraping.py というファイルを作成しました。

ソースは以下です。

# -*- coding: utf-8 -*-
import scrapy

# itemを使用するためにはImportが必要
from news.items import NewsItem

class YnewsSpider(scrapy.Spider):
    # Spiderの名前です。プログラム実行時にここで設定した名前を使用します。
    name = "ynews"
    # クローリングの始点URLです。ここからクローリングを開始します。
    start_urls = ['https://news.yahoo.co.jp/list']

    # スクレイピング処理
    def parse(self, response):
        # 欲しい情報が内包されているニュースのリスト表示部分を取得します
        for selectElements in response.css("div.listArea ul.list li"):
            # 設定したアイテムに情報を格納したいので、インスタンスを生成します
            item = NewsItem()
            # 欲しい要素を取得し、対応するフィールドに格納します
            item['title'] = selectElements.css(".ttl::text").extract_first()
            item['category'] = selectElements.css(".cate::text").extract_first()
            item['url'] = selectElements.css("a::attr('href')").extract_first()
            # 別ページに遷移して取得するデータはrequestとして受け取って、itemに格納し遷移後に再度スクレイピングします
            request = scrapy.Request(item['url'], callback=self.getArticle)
            request.meta['item'] = item
            yield request

    # ニュース概要スクレイピング処理
    def getArticle(self, response):
        item = response.meta['item']
        item['summary'] = response.css("div.headlineTxt p.hbody::text").extract_first()
        # 値を格納したitemを返します
        yield item

実行

以下のコマンドでクローリング・スクレイピングを実行します。

scrapy crawl ynews -o news.json

実行完了後、コマンド実行ディレクトリと同フォルダに news.json が生成されます。

最後に

上記の方法はPython、クローリング、スクレイピングの初心者が、試行錯誤しながら行った方法です。
もっともっと良い方法があると思いますので、おかしい点や改善点などドンドンご指摘いただければと思います!

参考サイト

Scrapy 1.4 documentation
ScrapyでWebサイトからデータ収集してみた