
機械学習が流行している近年において、データを集める手段としてのWebスクレイピングもまた注目を集めています。現在はWebスクレイピングのための便利なライブラリが揃っているので、実装は簡単です。今回はPythonのScrapyとSeleniumを使って、Webスクレイピングする手法を紹介します。
見出し
はじめに
WebクローリングはWebサイトからHTMLなどのソースコードを収集するプロセスのことであり、Webスクレイピングは収集したデータから必要な情報を抽出するプロセスのことです。この記事ではまとめてWebスクレイピングと表現します。Webスクレイピングを利用することでさまざまな使いみちに応用できますが、近年では機械学習のためのデータ収集の手段として使われています。
今回は、PythonのWebスクレイピングのライブラリであるScrapyとSeleniumを組み合わせて、Newsweekのサイトをスクレイピングしてみましょう。
Scrapyとは?
Scrapyとは、PythonでWebスクレイピングをするためのライブラリです。類似したライブラリにBeautiful Soupが知られています。Scrapyで作ったクローラーはScrapingHubというクラウド上にデプロイすることができ、自前でサーバーを準備することなくすぐに可動させられます。
XPathとは?
XPathとは、XML文章から任意の位置を指定するための構文です。Webスクレイピングのライブラリでは通常XPathが利用可能であるため、この構文を利用することでXML形式の文章であるHTMLを解析することができます。特に難しい構文でも無いため、書いていればすぐに理解できるでしょう。
Newsweekのサイトをスクレイピング
前提条件
以下を事前に準備しましょう。
- Python3がインストールされていること
- Chromeがインストールされていること
- Chromeのドライバーがインストールされていること
今回はブラウザにChromeを使用し、Seleniumで操作するのでChromeのドライバーも必要です。Macの場合、ChromeのドライバーはHomebrewでインストールできます。
$ brew cask install chromedriver
$ chromedriver --version
ChromeDriver 2.40.565386 (45a059dc425e08165f9a10324bd1380cc13ca363)
開発環境の準備
まずは、Scrapyのプロジェクトを準備しましょう。
$ python -m venv newsweek_crawler_env
$ source newsweek_crawler_env/bin/activate
$ pip install --upgrade pip
$ pip install ipython scrapy selenium
$ scrapy version
Scrapy 1.5.0
$ scrapy startproject newsweek_crawler
$ cd newsweek_crawler/
$ scrapy genspider newsweek www.newsweek.com
$ scrapy list
newsweek
$ scrapy crawl newsweek
...
2018-06-26 00:02:02 [scrapy.core.engine] DEBUG: Crawled (200) (referer: None)
...
$ scrapy shell http://www.newsweek.com/
...
In [1]: response
Out[1]:
In [2]: exit
SpiderはScrapyのメインのスクリプトであり、Webサイトと紐づけて作られています。Newsweekのサイトから200が返ってきているので、正しくSpiderが作られていることが確認できました。
Scrapyの設定
今回はクローラーっぽくUSER_AGENTとDOWNLOAD_DELAYを設定します。これによりブラウザっぽくアクセスできます。ROBOTSTXT_OBEYを無効にすることで、robots.txtへのアクセスしなくなり、無意味なリクエストを減らせます。COOKIES_ENABLEDを無効にすることでクッキーにクローラー対策を施してあるサイトに対処しています。
$ vi newsweek_crawler/settings.py
...
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 2
RANDOMIZE_DOWNLOAD_DELAY = True
COOKIES_ENABLED = False
...
その他にも、この設定ファイルはItemやPipelineを使う場合に設定を追加して利用します。
クローラーの作成
それではメインのコーディングをしましょう。今回はNewsweekの記事のタイトルと記事内容を取得してみましょう。
# -*- coding: utf-8 -*-
from time import sleep
from scrapy import Spider
from scrapy.selector import Selector
from scrapy.http import Request
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
class NewsweekSpider(Spider):
name = 'newsweek'
allowed_domains = ['www.newsweek.com']
start_urls = ['http://www.newsweek.com/']
def __init__(self, search_word, page_limit=10):
self.search_word = search_word
self.page_limit = int(page_limit)
def start_requests(self):
self.driver = webdriver.Chrome()
self.driver.get('http://www.newsweek.com/')
# Search a word
input_search_form = self.driver.find_element_by_xpath(
'//input[@name="search_block_form"]')
input_search_form.send_keys(self.search_word)
input_search_form.send_keys(Keys.RETURN)
# Avoid getting banned
sel = Selector(text=self.driver.page_source)
forbidden_page_title = sel.xpath('//title/text()').extract_first()
if '403' in forbidden_page_title:
self.logger.warn('Access is forbidden. Try again!')
input_search_form2 = self.driver.find_element_by_xpath(
'//input[@name="search_block_form"]')
input_search_form2.send_keys(self.search_word)
input_search_form2.send_keys(Keys.RETURN)
# Get articles
page_num = 0
while page_num < self.page_limit:
try:
sel = Selector(text=self.driver.page_source)
article_urls = sel.xpath('//article/div/h3/a/@href').extract()
for article_url in article_urls:
yield Request(url=article_url, callback=self.parse_article)
# Process a next page
next_page = self.driver.find_element_by_xpath(
'//li[contains(@class, "pager-next")]/a')
next_page.click()
sleep(3)
page_num = page_num + 1
except NoSuchElementException:
self.driver.quit()
break
else:
self.driver.quit()
def parse_article(self, response):
title = response.xpath(
'//h1[@itemprop="headline"]/text()').extract_first()
article_paragraphs = response.xpath(
'//div[@itemprop="articleBody"]/p').extract()
article_body = ''.join(article_paragraphs)
yield {'title': title, 'article_body': article_body}
今回は適当に記事の対象となるサイトを選んだのですが、運悪くNewsweekはクローラー対策が施されているサイトでした。そのため、クローラーだと判断されてしまった場合にもう一度検索することで対処しています。
クローラー対策が施されていないサイトの場合は、Scrapyだけでもすべてのデータを取得することが可能です。一応ですが、本来なら利用規約などを読んでWebスクレイピングが禁止されていないかを確認すべきですよ。
動作確認
それでは実行してみましょう。
$ scrapy crawl newsweek -a search_word='machine learning' -a page_limit=3 -o articles.csv
CSVファイルに3ページ分のタイトルと記事内容のHTMLが出力されました。
最後に
いかがでしたか?これでPythonでWebスクレイピングができるようになったと思います。Seleniumの知識も使えるのでE2Eテストをやっている人には取っ付き易いかもしれません。データを効率的に収集して、機械学習などに利用していきましょう。では。
環境
- Python : 3.6.5
- pip : 10.0.1
- Chrome : 67.0.3396.87
- ChromeDriver : 2.40.565386
- Scrapy : 1.5.0
- Selenium : 3.13.0


コメントを残す