2019年12月1日日曜日

htmlのタグを指定して値をsasデータセットに格納する話

sasでwebスクレイピングってあんまり事例がないと思うので紹介する話です.
スクレイピングしようとするとwebページをhttpプロシジャ使ってhtmlのタグ付きのテキストに変換して,そこから必要な分だけをデータセットに取り込むのがいいかなと記事投稿時点では考えています.

問題になるのが「必要な分だけデータセットに取り込む」の部分かと.vba等のほかの言語ならhtmlのclass名なりid名なりを指定して格納されている値を取れますが,タグ付きのテキストに変換しているのでそんな器用なことができません.所詮テキストです.かと言って取得時にhtmlのタグを使えないとお話にならないので,テキストは@を使えば読み込み箇所を動かせるのを利用して値を取得します.

例えば以下のようなタグ付きのテキストを仮に取得したとします.
今回欲しいのはその中でも「c-rating__val c-rating__val--strong list-rst__rating-val」classの値である「3.49」ですね.


 <p class="c-rating c-rating--xl c-rating--val30 list-rst__rating-total cpy-total-score">
   <i class="c-rating__star list-rst__rating-star">
   </i>
      <span class="c-rating__val c-rating__val--strong list-rst__rating-val">3.49
      </span> </p>
 <p class="list-rst__rvw-count is-highlight">

「3.49」を取るために以下の段階を踏みます.コードの全体は一番下にまとめてあります.
まずdlm="</"とすることで,sasの変数に</ごとに区切りを変えて値を取得できます.こうすることで上のテキストの緑の部分の前後は以下のようにsasデータセットに格納できます.1行が1obsに相当すると思ってください.

</i> <span class="c-rating__val c-rating__val--strong list-rst__rating-val">3.49
</span>
</p> <p class="list-rst__rvw-count is-highlight">

1つの変数に</i> <span class="c-rating__val c-rating__val--strong list-rst__rating-val">3.49と格納できたので,class="c-rating__val c-rating__val--strong list-rst__rating-val">が出てきたら読み込み位置を@で1つ進めて3.49を取得できます.
今回はclassタグを指定していますが,同じことをすればidタグも同様に指定して値が取れるはずです.と言うかidタグがユニークになるのでとれるならidの方がいいですね.

/*--コードの全体像*/
data OUT_P;
 infile F_OUT dlm="</" dsd missover  lrecl=30000 firstobs=1;
 input @ 'class="c-rating__val c-rating__val--strong list-rst__rating-val">' V_VAL ;

 /*--要らないレコードを削除*/  
 if V_VAL = . then delete ;
run;




/*--取得した値をデータセットに上書き ページ数分繰り返せばok*/
proc append base= V_OUT data= OUT_P ;
run ;

proc httpだとurlを指定するのでどうしてもテキストに変換するのは1ページごとになってしまいます.読み込みを複数ページにまたがってやりたい時はproc httpでページを読み込み→テキストをsasデータに変換→欲しい値をproc appendでデータに追加の3つをloopして行えばokと思います.ただし一気にwebページに複数回アクセスすると攻撃扱いされますので,loop内に適宜sleep関数を挟むのが必須です.