2018年12月13日

コンテンツ

  • なぜ文字列操作が必要なのか?
  • 基本的な文字列操作(tidyr::separete
  • 文字列操作パッケージstringr
  • 文字列を別の型に変換する(readr
  • より高度に処理するための正規表現
  • 正規表現を用いた操作(tidyr::extract
  • おまけ(スクレイピングあれこれ)
    • 文字コードを調べる(サイトのエンコーディングを知る)
    • ページ切り替え
    • 結構重要なスクレイピング知識
    • DOMとは?
    • エンコーディングを推定する

なぜ文字列操作が必要なのか?

スクレイピングで取得できるデータは表示を優先していることが多いため、読み込んだだけでは処理に不適当なケースが多々あります。例えば MLBの試合結果 の場合、得点欄が複数行になっており
これをスクレイピングすると以下のようになり、得点欄をこのまま処理することはできません。

基本的な文字列操作

文字列を複数の列に分割するにはtidyrパッケージが便利です。tidry::separete関数はsepパラメータに指定されているセパレータで文字列を分割する関数です。これを用いると先ほどのデータは以下のように分割できます。ただし、分割数はコーディング側で指定する必要があります。

result %>% 
  tidyr::separate(col = X2, into = c("X2_1", "X2_2", "X2_3"))

文字列操作パッケージstringr

tidyr::separete関数で分割できない場合には文字列操作に特化したstringrパッケージが便利です。より細かな操作が可能になります。主な関数には以下のようなものがあります。

function description
stringr::str_sub 文字列を部分的に参照・変更する
stringr::str_c 複数の文字列を結合する
stringr::str_split 文字列を分割する(返り値はリスト型)
stringr::str_extract 指定したパターンにマッチした部分の文字列を取り出す
stringr::str_replace 指定したパターンにマッチした部分のみ置換する

文字型を別の型に変換する

Rでは数字区切りや単位がある数字は文字型データとして扱われます。このような場合、readrパッケージを用いると文字型を数値型(intger型やdouble型など)に簡単に変換できます。

function description
readr::parse_logical 「TRUE, FALSE」などを論理型に変換する
readr::parse_integer 数字文字列を整数型に変換する
readr::parse_double 数字文字列を実数型に変換する
readr::parse_number 数字文字列を数値に変換する
readr::parse_date 数字文字列を日付型に変換する(lubridate::as_dateと同様)

より高度に処理するための正規表現(1/5)

文字列を高度に処理する場合に欠かせないのが正規表現です。Rでは拡張正規表現(POSIX 1003.2)が使えます。正規表現は、大雑把にいうとメタ文字とリテラルを組み合わせてマッチさせる文字列を表現するための決まりです。

メタ文字

メタ文字は特殊な意味を持つ文字で、様々な表現(どちらか、繰り返し、除く、など)が可能になります。メタ文字自体を表現したい場合(リテラルとして扱う場合)はバックスラッシュ(\)を直前に配置します。

リテラル

リテラルとはメタ文字でない単なる文字列のことです。メタ文字と組み合わせることで多様な表現(パターン)を可能にします。

より高度に処理するための正規表現(2/5)

Operator Description
| どちらか(A|BABのどちらかに一致する)
* 0回以上の一致(最長一致)
*? 0回以上の一致(最短一致)
+ 1回以上の一致(最長一致)
+? 1回以上の一致(最短一致)
? 0回または1回の一致(1回が優先)
?? 0回または1回の一致(0回が優先)

より高度に処理するための正規表現(3/5)

Operator Description
. 任意の一文字
() 括弧内を一文字として扱う
{n} n回の繰り返し(n = 0, 1, 2, …)
{n, m} n回以上、m回未満の繰り返し(n < m, n = 0, 1, 2, …)
\ エスケープシーケンス

より高度に処理するための正規表現(4/5)

Set Expression Description
[...] []内に含まれる一文字に一致([abc]a, b, cの一文字に一致)
[...-...] []内に含まれる-範囲内の一文字に一致([a-z]は英小文字の一文字に一致)
[^...] []内に含まれる一文字とは異なる一文字に一致([^abc]!a or !b or !c

 
[]|を簡易に表記するイメージです。例えば[abc]a|b|cは正規表現としては等価の扱いになります。ただし、他の演算子と組み合わせる場合には注意してください。

grep("[^abc]", c("abc", "def", "xyz", "0ab"))
## [1] 2 3 4

より高度に処理するための正規表現(5/5)

頻繁に使われるパターンは名前付き文字クラスとして予め定義されています。代表的な名前付き文字クラスには以下のようなものがあります。

regexp description
[:alnum:] アルファベットと数値、[:alpha:] + [:digit:]
[:alpha:] 大小文字アルファベット、 [:lower:] + [:upper:]
[:digit:] 数値
[:punct:] パンクチュエーション文字 ! " # $ % & ' ( ) * + , - . /
[:space:] 空白文字、タブ、改行、水平タブ、給紙、キャリッジリターン、空白

正規表現を用いた操作

前述の正規表現を用いるとtidyr::extract関数のようにセパレータの代わりに正規表現を用いてより細かい文字列操作が可能になります。

tidyr::extract(data, col, into, regex = "([[:alnum:]]+)", ...)

 
このような関数は 大相撲の取組表 のようなデータを処理するのに適しています。

おまけ

文字コードを調べる(1/3)

最近ではあまりないようですが、サイトによっては文字コードにShift JISなどを使っていて、rvestパッケージで取得した情報が文字化けする場合があります。このような時にはヘッダーから文字コードの情報を取得(rvest::guess_encordingを使うより確実)してエンコードを変換します。
まず、ヘッダ(<head></head>で囲まれた部分)にある文字コードのメタ情報(<metaで始まりcharset=が記載されいる部分)をrvest::html_attrs関数を用いて取得します。

## [[1]]
##                 http-equiv                    content 
##             "Content-Type" "text/html; charset=UTF-8"

文字コードを調べる(2/3)

次に"charset="の後ろにある文字列の開始位置と終了位置をstringrパッケージを用いて取得して、切り出します。

meta %>% stringr::str_sub(start = str_locate(., pattern = "charset=")[2] + 1,
                          end =  stringr::str_locate(., pattern = '\\)')[1] - 2)
## [1] "UTF-8"

終了位置を「\)」で判定しているのは、rvest::html_attrs関数の返り値が属性値を持ったリスト型であるため実際の文字列部分が下記のように格納されているためです。

meta %>% as.character()
## [1] "c(\"Content-Type\", \"text/html; charset=UTF-8\")"

文字コードを調べる(3/3)

もしくは、単純に属性名で参照して以下のように取り出すことも可能です。

meta[[1]]["content"]
##                    content 
## "text/html; charset=UTF-8"
meta[[1]]["content"] %>% 
  stringr::str_sub(start = stringr::str_locate(., pattern = "charset=")[2] + 1,
                   end = stringr::str_length(.))
## [1] "UTF-8"

文字コードを決め打ちせずにメタ情報から取得することで、サイトのエンコーディングが変更されてもRのコードを変更することなくスクレイピングが可能になります。

ページ切り替え(1/2)

スクレイピング時にページ切り替えを行うにはRSeleniumのようなパッケージが必要なことも多いですが、リンクのURLをよくよく見るとコードの工夫でページを切り替えることができるサイトも多いです。例えば、Kabutanのサイトは下図のように表を切り替えるようになっています。

ページ切り替え(2/2)

表切り替えのリンクを調べるとpage=nとなっていることがわかります。つまり表を切り替えるためにはSeleniumのようなツールでリンクをクリックする必要はなくURLの指定を変更すれば良いことが分かります。

https://kabutan.jp/stock/kabuka?code=0000&ashi=day&page=2   # 2ページ目へのリンク
https://kabutan.jp/stock/kabuka?code=0000&ashi=day&page=3   # 3ページ目へのリンク

すなわち以下のようなコードで切り替えるページのURLを生成すれば、Seleniumなどを利用しなくてもスクレイピングが可能になります。

"https://kabutan.jp/stock/kabuka?code=0000&ashi=day&page=" %>% 
  paste0(., "n")
## [1] "https://kabutan.jp/stock/kabuka?code=0000&ashi=day&page=n"

※最初に処理対象の特徴を掴むことがスクレイピングのポイントです。

結構重要なスクレイピング知識

スクレイピングを実体験すると分かりますがテスト自動化にも使えます。この時に重要なポイントがCSSセレクタをどのように設計・実装するかです。テスト自動化を考慮しないで設計・実装すると画面構成の修正が発生するたびにセレクタ情報が変化してしまい、実装したスクレイピング・コードに手直が発生しテスト自動化が本末転倒な状態になってしまいます。

例えば Yahoo!スポーツの大相撲 では以下のようにID情報を用いることで画面構成に変化が出てもテーブル・セレクタの変更なしに取得できるようになっています。

"//*[@id=\"makuuchi\"]/table"   # 幕内取組一覧(テーブル)のCSSセレクタ
"//*[@id=\"juryo\"]/table"      # 十両取組一覧(テーブル)のCSSセレクタ

サイトの情報を見るとその会社がどの程度テスト自動化を考えているかが見えてきます。

DOMとは

Document Object Modelの略でHTMLドキュメントやXMLドキュメントをプログラムから利用するための仕組みです。 Web Hypertext Application Technology Working Group (WHATWG) が仕様を定義しています。

DOMの特徴

  • 対象をツリー構造(階層構造)として扱う
  • ツリー構造の分岐点(結節点)はノードと呼ばれる
  • 実装はベンダー任せ(多くは標準から拡張している)

エンコーディングを推定する

Webサイトに対するスクレイピングを行っていると取得したデータが文字化けを起こしている場合があります。多くはUTF-8とは異なるエンコーディング(文字セット)が使用されている場合であり正しくエンコード指定をすれば文字化けは解消します。rvestパッケージにはエンコードを推定するrvest::guess_encoding関数が用意されており、この関数がどの程度正確にエンコードを推定するかを確認してみます(最大七候補出力されますが画面の都合上、上位三候補のみ表示)。

気象庁

encoding language confidence
UTF-8 1.00
windows-1252 en 0.28
windows-1250 ro 0.19

日清オイリオ

encoding language confidence
Shift_JIS ja 0.69
GB18030 zh 0.37
Big5 zh 0.31

いすゞ自動車

encoding language confidence
UTF-8 1.00
Shift_JIS ja 0.59
GB18030 zh 0.38

価格コム

encoding language confidence
UTF-8 1.00
Shift_JIS ja 0.72
GB18030 zh 0.57

やまや

encoding language confidence
UTF-8 1.00
Shift_JIS ja 0.71
windows-1252 en 0.28

楽天

encoding language confidence
UTF-8 1.00
Shift_JIS ja 0.72
GB18030 zh 0.52

長府製作所

encoding language confidence
UTF-8 1.00
Shift_JIS ja 0.56
windows-1252 en 0.25

参考資料

Enjoy !

CC BY-NC-SA 4.0, Sampo Suzuki