ウェブページからのデータ取得

kaoru.nasuno 執筆者 那須野 薫

本チュートリアルでは、ウェブページからのデータ取得について説明します。
ウェブページからデータを取得するには主に、クローリングスクレイピングという技術を利用します。
クローリングはウェブサイトからHTMLや任意の情報を取得する技術・行為で、
スクレイピングは取得したHTMLから任意の情報を抽出する技術・行為のことです。

たとえば、あるブログの特徴を分析したい場合を考えてみましょう。
この場合、作業の流れは
1. そのブログサイトをクローリングする。
2. クローリングしたHTMLからタイトルや記事の本文をスクレイピングする。
3. スクレイピングしたタイトルや記事の本文をテキスト解析する。
というようになります。

本節では、この作業の流れの1.と2.について説明します。
クローリングの技術にはスクレイピングの技術が含まれるため、
ここでは、まず、HTMLについておさらいし、次に、スクレイピングについて、最後に、クローリングについて説明します。

HTMLのおさらい


HTMLはウェブサイトのページ構成を記述するための言語です。
具体的には下記のようなものを考えてみましょう。

<html>
  <head>
    <meta charset='utf-8' />
  </head>
  <body>
    <h1>クローリングとスクレイピング</h1>
    <div id="articleInfo">
      <p>
        <span class="timestamp">2014-2-11 14:33:31</span>
        <span class="author"><a href="https://github.com/KaoruNasuno">KaoruNasuno</a></span>
      </p>
    </div>
    <div id="articleText">
      本節では、ウェブサイトのクローリングとスクレイピングについて説明します。。。  

    </div>
  </body>
</html>

HTMLは<>を用いたタグというものを用いて記述されます。
特に、ウェブページの目に見える要素は<body>タグの間に記述されます。
idclassでHTML要素を指定したり、グループ分けします。
また、リンクは<a>タグでリンク先のURLはhref属性で指定します。

スクレイピング


スクレイピングはHTMLから任意の情報を取得する技術のことです。
スクレイピングをするにはいろいろな方法があり、ここでは、Beautiful Soupを使う方法、XPathを使う方法、正規表現を使う方法の3つを取り上げます。
それぞれについて、上記のHTMLから記事のタイトル、投稿時刻、著者、著者のリンク、記事本文を取得する方法について順に説明します。

1. Beautiful Soupを使う方法

Beautiful SoupはPythonのモジュールで、
HTMLを構文解析して要素の指定を手助けしてくれます。
具体的には下記のコードのように要素を指定して、値を抽出します。

#coding: utf-8
from bs4 import BeautifulSoup
from datetime import datetime

#変数htmlには上記のHTMLがstrで代入されているとします。
soup = BeautifulSoup(html)

#変数title, timestamp, author, author_link, bodyにそれぞれタイトル、投稿日時、著者、著者のリンク、記事本文が代入されます。
title = soup.h1.find(text=True)
timestamp = soup.find(id='articleInfo').find(class_='timestamp').find(text=True)
author = soup.find(id='articleInfo').find(class_='author').find('a').find(text=True)
author_link = soup.find(id='articleInfo').find(class_='author').find('a').get('href')
body = soup.find(id='articleText').find(text=True)

2. XPathを使う方法

XPathはXML形式の文書の要素を指定する言語です。
PythonでXPathを使う時はlxmlというモジュールを用います。
具体的には下記のコードのように要素を指定して、値を抽出します。

#coding: utf-8
import urllib2
import lxml.html

#変数htmlには上記のHTMLがstrで代入されているとします。
dom = lxml.html.fromstring(html)

#変数title, timestamp, author, author_link, bodyにそれぞれタイトル、投稿日時、著者、著者のリンク、記事本文が代入されます。
title = dom.xpath('//h1')[0].text
timestamp = dom.xpath('//*[@id="articleInfo"]//*[@class="timestamp"]')[0].text
author = dom.xpath   ('//*[@id="articleInfo"]//*[@class="author"]/a')[0].text
author_link = dom.xpath   ('//*[@id="articleInfo"]//*[@class="author"]/a')[0].attrib['href']
body = dom.xpath('//*[@id="articleText"]')[0].text

XPathの文法の説明は他の文献に譲ります(巻末の参考文献を参照)が、
任意のHTML要素のXPathはブラウザを使って簡単に知ることができます。
例えば、Chromeであれば、要素の検証 -> Elementsタブの任意の要素を右クリック -> Copy XPath でXPathを取得できます。
これにより得られるXPathは指定が細かいのでページの違いに対応できない可能性がありますが、簡単なページではこれで十分です。

3. 正規表現を使う方法

正規表現とは文字列から特定の部分文字列を抽出するために用いる言語です。上記の1. 2. と異なり、HTMLだけでなく一般的な文字列に対して使えるため適用範囲が広い反面、表記がやや煩雑です。
Pythonで正規表現を使う時はreというモジュールを用います。
具体的には下記のコードのように要素を指定して、値を抽出します。

#coding: utf-8
import re

#変数htmlには上記のHTMLがstrで代入されているとします。
#変数title, timestamp, author, author_link, bodyにそれぞれタイトル、投稿日時、著者、著者のリンク、記事本文が代入されます。

title = re.compile('\<h1\>(.+?)\<\/h1\>', re.MULTILINE|re.DOTALL).findall(html)[0]
timestamp = re.compile('\<div id="articleInfo"\>.+?\<span class="timestamp"\>(.+?)\<\/span\>', re.MULTILINE|re.DOTALL).findall(html)[0]
author_link, author = re.compile('\<div id="articleInfo"\>.+?\<span class="author"\>\<a href="(.+?)"\>(.+?)\<\/a\>\<\/span\>', re.MULTILINE|re.DOTALL).findall(html)[0]

body = re.compile('\<div id="articleText"\>(.+?)\<\/div\>', re.MULTILINE|re.DOTALL).findall(html)[0]

整理

本節では、スクレイピングの方法として、3つ取り上げました。
(私見に基づいた)それぞれの特徴は下記の通りで、自分が何をしたいのかによって学ぶ優先順位を変えたり、使い分けたりできると望ましいです。

手法 適用範囲 学習コスト 他言語での利用可能性
Beautiful Soup HTML 不可
XPath HTML
正規表現 文字列全般

クローリング


クローリングはウェブサイトからHTMLや任意の情報を取得する技術・行為のことです。
クローリングを行うプログラムのことをクローラといいます。

ここでは、mechanizeというモジュールを用いてクローラを作成する方法を説明します。
mechanizeを利用するメリットは下記が挙げられます。

  • メソッドの名前が直感的で分かりやすく、単純な処理も実装しやすくなる。
  • リンクのクリックやフォームのポスト、クッキーの維持などページ遷移の概念の扱いが容易になる。
  • クローリングのマナーを考慮してくれる。

ページのHTMLを取得する

mechanizeはブラウザのようにページを開いたり、ページのフォームに値を代入したり、ということをサポートするモジュールです。
mechanizeで指定のURLのHTMLを取得するクローラは下記のようなコードで実装できます。

#coding: utf-8
import mechanize

#変数urlには取得したいURLがstrで代入されているとする。
b = mechanize.Browser()
b.open(url)

#変数htmlには指定のURLのHTMLが代入される。
html = b.response().read()

ページ遷移を扱う

mechanizeはリンクのクリックやフォームのポストといった動作をサポートしており、擬似的にページ遷移を扱えます。

#coding: utf-8
import mechanize

b = mechanize.Browser()
b.open(url)

b.select_form(nr=0)
b.form['user_id'] = user_id
b.form['password'] = password

b.submit()

細かい設定や注意点

クローラをつくる際には下記が問題になることがあります。

  • ページによってはUser Agentの設定されていないアクセスを受け付けないことがあるので、User Agentを設定する。
  • mechanizeはJavaScriptを実行してくれないので、JavaScriptによるページ遷移はなるべく回避するか、JavaScriptを解読してページ遷移を再現する。

また、クローリングの際はウェブサイトの管理者のことを考えて行いましょう。

  • ページを繰り返し開く時はtime.sleep()などで一定時間以上の間隔を開け、サイトに負担をかけないようにします。
  • robots.txtに指定されているページへのクローリングは避けます(mechanizeのデフォルトの設定ではできないようになっています)。

課題



下記のいずれかのニュースサイトの興味のあるジャンルの過去1週間の記事について、
URL、HTML、記事のタイトル、本文、投稿時刻、著者、場所など記事の情報を取得・抽出して、MySQLなどのリレーショナルデータベースに保存せよ。
なお、手法は本節で紹介した以外のものを使っても構いません。

  • http://www.reuters.com/
  • http://techcrunch.com/

参考文献