満月の夜の息抜き

誰かに話す必要がないことたち。

Webスクレイピング入門 - unixコマンド編

クローリング

スクレイピング

  • ダウンロードしたWeb Pageから必要な情報を抜き出す作業

Python

GNU WgetWget

  • HTTP通信やFTP通信を使って、サーバーからファイルやコンテンツをダウンロードするためのソフトウェア(=ダウンローダー)
  • 特徴として、クローリング機能がある

homebrew

Wgetは注意が必要

  • Wgetでリンクをたどってクローリングするには、再帰的にダウンロードするための -r オプションを使う。
  • -r を使うと、サーバーやネットワークに負荷がかかる
  • -l でリンクをたどる深さを制限
  • -w でダウンロードする間隔をあける
  • $ wget -r --no-parent -w 1 -l 1 --restrict-file-names=nocontrol http://sample.scraping-book.com/dp/
  • コマンドの解説がわかりやすいサイト↓
  • https://www.atmarkit.co.jp/ait/articles/1606/20/news024.html
  • サイトがダウンロードできたらtreeコマンドで確かめる
  • $ brew install tree
  • $ tree URL

Unixコマンドと正規表現スクレイピングする

  • ダウンロードしたHTMLファイルからUnixコマンドでスクレイピングする
  • HTMLファイルは複雑なデータを含む「テキストファイル」なので、Unixコマンドと正規表現を使う
  • Unixコマンドを複数組み合わせることで、複雑なテキスト処理を行える
  • データを抜き出す箇所は、正規表現という特殊な「文字列表現」で指定する

Unixコマンドの基本

  • コマンドが入力を受け取る=「標準入力」
  • それを加工して結果を出力する先=「標準出力」
  • エラー(補足情報)を出力する先=「標準エラー出力
  • 標準エラー出力、標準出力、標準入力の3つを合わせて「標準ストリーム」と呼ぶ
  • デフォルトは、キーボードからの入力、コンソール画面への表示になっているので、ファイルからの入力や、ファイルへの出力に変更する(=リダイレクト)
  • $ コマンド > ファイルのパス(標準出力をファイルへ保存する)
  • $ コマンド < ファイルのパス(ファイルの中身を標準入力として与える)

パイプ

  • $ cat yakei_kobe.csv | grep 六甲
  • あるコマンドの標準出力を、別のコマンドの標準入力に与えられる

cat

  • 引数で与えたファイルを出力する

grep

  • 一部の行を抜き出すために使う
  • 引数で指定した文字を含む行を抜き出す
  • 正規表現を引数として指定すれば、その正規表現にマッチする行を抜き出す

行と列は改行で覚える

  • 列はA、B、C・・・
  • 行は1行、2行、3行・・・

cut(よくわからん・・・)

  • 特定の文字で区切られたテキストの一部の「列」を抜き出す
  • $ cat yakei_kobe.csv | cut -d , -f 1,2
  • カンマで区切った1列目と2列目のみを出力できる

sed(むずい・・・)

  • 特定の条件にマッチする行を置換、削除できる
  • $ cat yakei_kobe.csv | sed 's/,/ /g'
  • カンマをスペースに置き換えて出力する

(文字列と文字)

  • (文字列というのが正しい表記なんだろうけど、文字って言ってくれたほうがわかりやすいときもある・・・)

正規表現

  • 特定のパターンの文字列を表現するための表現(笑)
  • パターンにマッチする文字列を検索するときに使う
  • -E オプションを付けることで、POSIX拡張性非表現(ERE)が使える

(ターミナルで実行中のプログラムを中止するためには)

  • (Control + c でした。。。)

正規表現つづき)

ファイル全体から目的の行を抜き出す

  • $ cat sample.scraping-book.com/dp.html | grep -E 'class="paging-number".*-'
  • 結果:

    <li class="paging-number">1 - 30 / 1395</li>

  • . は、なにか1文字
  • * は、直前の文字が0(無し)回以上連続する場合
  • 確かめる場合は --color オプションをつけることでハイライトされる
  • 正規表現の仕方がわかりやすく解説してあるサイト↓
  • https://www.mnet.ne.jp/~nakama/

行から目的の文字列を抜き出す

方策:sed を使う

  • sedについての解説サイト↓
  • https://www.atmarkit.co.jp/ait/articles/1610/17/news015.html
  • sed  s/置換前/置換後/g
  • [置換前]で指定した文字列にマッチした部分を[置換後]に置き換える。複数マッチした場合は先頭のみ置換、全てを置換したい場合は、「s/置換前/置換後/g」のように「g」オプションを指定する

方策1

  • $ echo abcdefgh | sed 's/.*(d.).*//'
  • ( ) は、()の中をグループ化して扱える、つまり2文字以上を扱うときに括る
  • s/.*(抜き出したい箇所にマッチする正規表現).*//というコマンドを引数に与えて、( )内だけを抜き出せる  
  •  は、オプション + ¥で入力
  • は、1つ目を参照する

方策2

  • $ echo '<li class="paging-number">1 - 30 / 1395</li>' | sed -E 's/<[^>]*>//g'
  • s/取り除きたい箇所にマッチする正規表現//g
  • 正規表現にマッチした箇所を取り除くことで、結果的に残った箇所を抜き出す
  • [ ]は集合を表す
  • [^ ] は、否定を表す。[^abc]だと、a,b,c以外の任意の1文字の意味
  • [^>] は、>以外の1文字
  • [^>]*  は、>以外の1文字を0回以上繰り返した2文字以上の文字列
  • 参考までに、^.*$  は、行全体を指す。(^先頭の.(任意の文字)から$最後の文字)

方策3:cutを使う

方策4:awkを使う

  • $ echo 'PID COMMAND %CPU TIME #TH #WQ #PORT MEM' | awk '{print $4}'
  • 結果:TIME
  • awkコマンドは、スペースで桁揃えされた文字列から、n番目の文字列を抜き出す
  • cutコマンドでは、区切り文字が1文字(さっきのは,)のみだが、スペースで区切られている場合に限定されるがawkコマンドが使える

結論:方策1を使う

  • $ cat sample.scraping-book.com/dp.html | grep -E "class=paging-number".*-' | sed -E 's@.*/ ([0-9]+).*@@'
  • [ - ]は、-で文字の範囲を表すことができる。[0-9]なら0〜9の中のいずれか1文字
  • + は、直前のパターンを1回以上繰り返す
  • * は、greedyなので、.*([0-9]+)</.*とすると、1文字しか得られない

wgetでクローリングをして、unixコマンドと正規表現スクレイピングを行うことはできるが、wgetではディレクトリ単位でしかクローリングできない、unixコマンドでは行単位になっていないデータを扱うのは苦手。Pythonであれば、クローリングやスクレイピングのための機能やライブラリが揃っている。

  • $ cat sample.scraping-book.com/dp.html | grep 'itemprop="name"' | sed -E 's@<br/>@ @' | sed -E 's/<[^>]*>//g' | sed -E 's/^ *//' | sed -E 's/&amp;/&/g'