1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PrologAdvent Calendar 2017

Day 12

PrologでHTMLスクレイピング

Last updated at Posted at 2017-12-11

はじめに

HTMLスクレイピングの話題があったのでPrologでやってみようってつぶやいてたら尾崎さんに解かれてしまいました。

% Prolog load_html/3の実行例です

?- shell('cat >foo.html').
<html>
<body><pre class="text">
ABC
DEF
</pre></body>  
</html>
true.

?- load_html('foo.html',L,[]).
L = [element(html,[],[element(body,[],[element(pre,[class=text],['ABC\nDEF'])])])].

foo.html を作り、 load_html/3 述語を使ってロードできて、要素(element)のリストが帰ってくるので、それをあとは好きに使ったら良いようです。

shell使の入力終了は、ctrl+dを入力するとよいようです。
せっかくなのでもうちょっと掘り下げて使ってみましょう。

xpath を使ってみよう

xpath 使ってみてみたい。

?- load_html('foo.html',L,[]),xpath(L,//(html)/body/pre(text),R).
L = [element(html, [], [element(body, [], [element(pre, [class=text], ['ABC\nDEF'])])]), '\n\n;'],
R = 'ABC\nDEF' .

テキストの中身が取れてます。

?- load_html('foo.html',L,[]),xpath(L,//(pre(text)),R).
L = [element(html, [], [element(body, [], [element(pre, [class=text], ['ABC\nDEF'])])])],
R = 'ABC\nDEF' .

属性は

?- load_html('foo.html',L,[]),xpath(L,//(pre(@class)),R).
L = [element(html, [], [element(body, [], [element(pre, [class=text], ['ABC\nDEF'])])])],
R = text .

どこかのURLからとって来たい


?- use_module(library(http/http_client)).
?- http_get('http://google.com',R,[]).
R = '<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head>......

取れてます。しかし文字列からの読み込みはよくわかりません。ストリームかファイル名を指定する必要があるので、http_open/3 でストリームを開いて読み込みます:

?- http_open('http://google.com',R,[]),load_html(R,L,[]).
Correct to: "http_open:http_open('http://google.com',R,[])"? 
Please answer 'y' or 'n'? yes
R = <stream>(0x55a92d58acf0,0x55a92da698d0),
L = [element(html, [itemscope='', itemtype='http://schema.org/WebPage', lang=ja], [element(head, [], ....

xpathで //div[@style] を全部取り出すと:

?- http_open('http://google.com',R,[]),
   load_html(R,L,[]),
   findall(LL,xpath(L,//(div(@style)),LL),L2).
Correct to: "http_open:http_open('http://google.com',R,[])"? yes
R = <stream>(0x56305b056df0,0x56305b37da10),
L = [element(html, [itemscope='', itemtype='http://schema.org/WebPage', lang=ja], [element(head, [], [element(meta, [content='\220\¢\212\E\222\\206\\202\̂\240\\202\炤\202\鏮\225\񂰌\237\\215\����邽\202\߂̃c\201\[\203\\213\\202\𒱋\237\\202\µ\202\Ă¢\202\܂·\201\B\202\³\202\܂´\202\܂Ȍ\237\\215\����\\\202\ꈗp\202\µ\202\āA\202\¨\222\T\202\µ\202\̏\202\쩂\202\¯\202\Ă­\202\¾\202\³\202\¢\201\B', name=description], []), element(meta, [content=noodp, ... = ...], []), element(meta, [... = ...|...], []), element(meta, [...|...], []), element(..., ..., ...)|...]), element(body, [bgcolor='#fff'], [element(script, [], ['(function(){var src=\'/images/nav_logo229.png\';var iesg=false;document.body.onload = function(){window.n && window.n();if (document.images){new Image().src=src;}\nif (!iesg){document.f&&document.f.q.focus();document.gbqf&&document.gbqf.q.focus();}\n}\n})();']), element(div, [... = ...], [' '|...]), element(center, [], [...|...]), element(..., ..., ...)|...])])],
L2 = ['left:0', 'right:0', 'padding:28px 0 3px', 'font-size:83%;min-height:3.5em', 'height:110px;width:276px;background:url(/images/branding/googlelogo/1x/googlelogo_white_background_color_272x92dp.png) no-repeat', 'color:#777;font-size:16px;font-weight:bold;position:relative;top:70px;left:218px', 'height:32px;margin:4px 0', 'font-size:10pt', 'margin:19px auto;text-align:center'].

L2 に全部取れてます。
お行儀よく、ファイルを閉じるにはsetup_call_cleanup/3を使います。

?- setup_call_cleanup(
  http_open('http://google.com',R,[]),
  (load_html(R,L,[]),findall(LL,xpath(L,//(div(@style)),LL),L2)),
  close(R)).

こう書けばちゃんと閉じますけど、アクセスするかどうかは聞かました。

a.pl
:- use_module(library(http/http_open)).

:- setup_call_cleanup(
     http_open('http://google.com',R,[]),
     (load_html(R,L,[]),findall(LL,xpath(L,//(div(@style)),LL),L2)),
     close(R)),
   writeln(L2).
:- halt.

ファイルから読んでみるとこうなります。http_open/3を使う時はlibrary(http/http_open))use_moduleする必要があります。

HTMLの出力

出力もしたいですよね。html_write/3で出力できます。

output.pl
:- html_write(user_output,[element(html,[],[element(body,[],['test'])])],[]),nl.
:- halt.

実行は:

$ swipl output.pl
<html>
  <body>test</body>
</html>

user_output は標準出力です。 第一引数に標準出力を指定して出力しています。

参考URL

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?