執筆者 那須野 薫
本チュートリアルでは、ウェブページからのデータ取得について説明します。
ウェブページからデータを取得するには主に、クローリングとスクレイピングという技術を利用します。
クローリングはウェブサイトからHTMLや任意の情報を取得する技術・行為で、
スクレイピングは取得したHTMLから任意の情報を抽出する技術・行為のことです。
たとえば、あるブログの特徴を分析したい場合を考えてみましょう。
この場合、作業の流れは
1. そのブログサイトをクローリングする。
2. クローリングしたHTMLからタイトルや記事の本文をスクレイピングする。
3. スクレイピングしたタイトルや記事の本文をテキスト解析する。
というようになります。
本節では、この作業の流れの1.と2.について説明します。
クローリングの技術にはスクレイピングの技術が含まれるため、
ここでは、まず、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>
タグの間に記述されます。
id
やclass
でHTML要素を指定したり、グループ分けします。
また、リンクは<a>
タグでリンク先のURLはhref
属性で指定します。
スクレイピングはHTMLから任意の情報を取得する技術のことです。
スクレイピングをするにはいろいろな方法があり、ここでは、Beautiful Soupを使う方法、XPathを使う方法、正規表現を使う方法の3つを取り上げます。
それぞれについて、上記のHTMLから記事のタイトル、投稿時刻、著者、著者のリンク、記事本文を取得する方法について順に説明します。
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)
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は指定が細かいのでページの違いに対応できない可能性がありますが、簡単なページではこれで十分です。
正規表現とは文字列から特定の部分文字列を抽出するために用いる言語です。上記の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
を利用するメリットは下記が挙げられます。
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()
クローラをつくる際には下記が問題になることがあります。
また、クローリングの際はウェブサイトの管理者のことを考えて行いましょう。
time.sleep()
などで一定時間以上の間隔を開け、サイトに負担をかけないようにします。mechanize
のデフォルトの設定ではできないようになっています)。
下記のいずれかのニュースサイトの興味のあるジャンルの過去1週間の記事について、
URL、HTML、記事のタイトル、本文、投稿時刻、著者、場所など記事の情報を取得・抽出して、MySQLなどのリレーショナルデータベースに保存せよ。
なお、手法は本節で紹介した以外のものを使っても構いません。