特定のリソースリストから情報を収集するという、Webマイニングの問題を解決した経験を共有したいと思います。 これは、独自の「検索エンジン」を作成しようとするものではないことに注意してください。これにはまったく異なるアプローチが使用されます。 Webマイニングの目標は、情報の一部を引き出すことです。 たとえば、リソースが「名刺」などの形式のマイクロフォーマットをサポートしている場合
実装について:正確にnode.jsなのはなぜですか? 確かに、特定のテクノロジーに制限はありませんでした。Java/ .NETを使用したC ++からPerl / Pythonまで、あらゆるものを使用できます。 node.jsを選んだ理由を説明します。
- 非同期IO操作。 他の言語で非同期性を整理することは可能ですが、時には非常に簡単です-F#には非同期ブロックがありますが、node.jsにはすぐに非同期性があり、操作を実行するための好ましい方法です。
- 最小限の冗長構造を持つ最も馴染みのある構文。 もちろん、アイテムは「全体的」ですが、実際にはjavascriptはF#やPythonよりもC / C ++、java、C#を使用した人に近いです。
- 追加のモジュールをインストールする必要なく、「すぐに使える」HTTPクライアントと正規表現のサポート。
- 実行速度。 V8には「弱点」がありますが、コンテキストの切り替えですが、このタスクでは「狭い首」ではなく、「線形」速度がより重要です。 そして、V8はこれを誇っています(NBは、この点を数字で証明するベンチマークを作成します)。
node.jsをインストールする
サーバー(FreeBSD、amd64)へのインストールはスムーズに進みました-「cd / usr / ports / www / node; make install」とnode.jsを使用する準備ができました。
Windowsプラットフォームの場合、最もアクセスしやすいインストールオプションはcygwinです。
純粋な.NETによるnode.jsの実装に遭遇しましたが、良い指示は見つかりませんでした。
Ubuntuの場合も、問題なく実行されます(たとえば、
適切な指示) 。
さらに素敵な
マニュアルを読んでください。 マニュアルは本当に素晴らしく見えますが、基本的な要素のみをカバーしており、Webマイナーを他のほとんどのクラスと同様にイベントをトリガーできるようにしたい場合、このマニュアルはまったく説明されていませんでした。 しかし、それについては後で。
ページアンローダー
http.Clientの例を使用して、ドキュメント全体がロードされるまで待機し、URLを解析して目的のリクエストをコンパイルすると、次の「クラス」が見つかりました。
var webDownloader = function (sourceUrl) {<br>
events.EventEmitter.call( this );<br>
this .load = function (sourceUrl) {<br>
var src = url.parse(sourceUrl);<br>
var webClient = http.createClient(src.port==undefined?80:src.port,src.hostname);<br>
var get = src.pathname+(src.search==undefined? '' :src.search);<br>
sys.log( 'loading ' +src.href);<br>
var request = webClient.request( 'GET' , get ,<br>
{ 'host' : src.hostname});<br>
request.end();<br>
var miner = this ;<br>
request.on( 'response' , function (response) {<br>
// console.log('STATUS: ' + response.statusCode); <br>
// console.log('HEADERS: ' + JSON.stringify(response.headers)); <br>
response.setEncoding( 'utf8' );<br>
var body = '' ;<br>
response.on( 'data' , function (chunk) {<br>
body += chunk;<br>
});<br>
response.on( 'end' , function () {<br>
miner.emit( 'page' ,body, src);<br>
});<br>
});<br>
};<br>
}<br>
sys.inherits(webDownloader, events.EventEmitter); <br>
<br>
* This source code was highlighted with Source Code Highlighter .
var webDownloader = function (sourceUrl) {<br>
events.EventEmitter.call( this );<br>
this .load = function (sourceUrl) {<br>
var src = url.parse(sourceUrl);<br>
var webClient = http.createClient(src.port==undefined?80:src.port,src.hostname);<br>
var get = src.pathname+(src.search==undefined? '' :src.search);<br>
sys.log( 'loading ' +src.href);<br>
var request = webClient.request( 'GET' , get ,<br>
{ 'host' : src.hostname});<br>
request.end();<br>
var miner = this ;<br>
request.on( 'response' , function (response) {<br>
// console.log('STATUS: ' + response.statusCode); <br>
// console.log('HEADERS: ' + JSON.stringify(response.headers)); <br>
response.setEncoding( 'utf8' );<br>
var body = '' ;<br>
response.on( 'data' , function (chunk) {<br>
body += chunk;<br>
});<br>
response.on( 'end' , function () {<br>
miner.emit( 'page' ,body, src);<br>
});<br>
});<br>
};<br>
}<br>
sys.inherits(webDownloader, events.EventEmitter); <br>
<br>
* This source code was highlighted with Source Code Highlighter .
var webDownloader = function (sourceUrl) {<br>
events.EventEmitter.call( this );<br>
this .load = function (sourceUrl) {<br>
var src = url.parse(sourceUrl);<br>
var webClient = http.createClient(src.port==undefined?80:src.port,src.hostname);<br>
var get = src.pathname+(src.search==undefined? '' :src.search);<br>
sys.log( 'loading ' +src.href);<br>
var request = webClient.request( 'GET' , get ,<br>
{ 'host' : src.hostname});<br>
request.end();<br>
var miner = this ;<br>
request.on( 'response' , function (response) {<br>
// console.log('STATUS: ' + response.statusCode); <br>
// console.log('HEADERS: ' + JSON.stringify(response.headers)); <br>
response.setEncoding( 'utf8' );<br>
var body = '' ;<br>
response.on( 'data' , function (chunk) {<br>
body += chunk;<br>
});<br>
response.on( 'end' , function () {<br>
miner.emit( 'page' ,body, src);<br>
});<br>
});<br>
};<br>
}<br>
sys.inherits(webDownloader, events.EventEmitter); <br>
<br>
* This source code was highlighted with Source Code Highlighter .
ここで興味深いのは、クラスがイベントのソースとして登録される方法です。
- まず、コンストラクターでEventEmitterに登録します。events.EventEmitter.call(this);
- EventEmitterからクラスを「継承」する
- emitメソッドを使用してイベントを「発行」する
EventEmitterでの作業はまだ文書化されていないので、少しグーグルする必要がありました。
これで、ページ読み込みイベント全体をサブスクライブできます。
var loader = new webDownloader();<br>
loader.on('page',vcardSearch);
vCardデータを検索する
現在、ページからvCardデータを取得するあまり面白くない関数です。 正しい実装に多くの時間を費やしたくなかったので、「額」でした-適切なクラスを持つ要素を検索しました。
ここでは、ページの解析にApricotモジュールを使用することを除いて、特に興味深いものはありません(ただし、htmlparserを使用すれば十分ですが、Apricotの方がはるかに高速になります)。 最初に、必要な要素を検索するためのCSSセレクターを構築し、Apricotの検索機能を使用しようとしました(これは検索にSizzleを使用します)が、判明したように、すべての要素の再帰的な走査が高速になりました。
その結果、次のような機能が得られました。
var vcardSearch = function (body,src) {<br>
sys.log( 'scaning ' +src.href);;<br>
Apricot.parse(body, function (doc) {<br>
var vcardClasses = [<br>
// required <br>
'fn' ,<br>
'family-name' , 'given-name' , 'additional-name' , 'honorific-prefix' , 'honorific-suffix' ,<br>
'nickname' ,<br>
// optional <br>
'adr' , 'contact' ,<br>
'email' ,<br>
'post-office-box' , 'extended-address' , 'street-address' , 'locality' , 'region' , 'postal-code' , 'country-name' ,<br>
'bday' , 'email' , 'logo' , 'org' , 'photo' , 'tel' <br>
];<br>
var vcard = new vCard();<br>
var scanElement = function (el) {<br>
if (el==undefined) return ;<br>
<br>
if (el.className != undefined && el.className!= '' ) {<br>
var classes = el.className.split( ' ' );<br>
for ( var n in classes) {<br>
if (vcardClasses.indexOf(classes[n])>=0) {<br>
var value = el.text.trim().replace(/<\/?[^>]+(>|$)/g, '' );<br>
if (value != '' ) vcard.Values[classes[n]] = value;<br>
}<br>
}<br>
}<br>
for ( var i in el.childNodes) scanElement(el.childNodes[i]);<br>
}<br>
scanElement(doc. document .body);<br>
if (!vcard.isEmpty())<br>
sys.log( 'vCard = ' +vcard.toString());<br>
else <br>
sys.log( 'no vCard found on ' +src.href);<br>
});<br>
} <br>
<br>
* This source code was highlighted with Source Code Highlighter .
var vcardSearch = function (body,src) {<br>
sys.log( 'scaning ' +src.href);;<br>
Apricot.parse(body, function (doc) {<br>
var vcardClasses = [<br>
// required <br>
'fn' ,<br>
'family-name' , 'given-name' , 'additional-name' , 'honorific-prefix' , 'honorific-suffix' ,<br>
'nickname' ,<br>
// optional <br>
'adr' , 'contact' ,<br>
'email' ,<br>
'post-office-box' , 'extended-address' , 'street-address' , 'locality' , 'region' , 'postal-code' , 'country-name' ,<br>
'bday' , 'email' , 'logo' , 'org' , 'photo' , 'tel' <br>
];<br>
var vcard = new vCard();<br>
var scanElement = function (el) {<br>
if (el==undefined) return ;<br>
<br>
if (el.className != undefined && el.className!= '' ) {<br>
var classes = el.className.split( ' ' );<br>
for ( var n in classes) {<br>
if (vcardClasses.indexOf(classes[n])>=0) {<br>
var value = el.text.trim().replace(/<\/?[^>]+(>|$)/g, '' );<br>
if (value != '' ) vcard.Values[classes[n]] = value;<br>
}<br>
}<br>
}<br>
for ( var i in el.childNodes) scanElement(el.childNodes[i]);<br>
}<br>
scanElement(doc. document .body);<br>
if (!vcard.isEmpty())<br>
sys.log( 'vCard = ' +vcard.toString());<br>
else <br>
sys.log( 'no vCard found on ' +src.href);<br>
});<br>
} <br>
<br>
* This source code was highlighted with Source Code Highlighter .
まとめ
結果の使用は簡単です:
loader.load('http://www.google.com/profiles/olostan');<br>
loader.load('http://www.flickr.com/people/olostan/');<br>
私はそれが最終的な、少し真面目な製品としてではなく、概念実証として、そしてnode.jsを感じるために考案されたとすぐに言いたいです。
完全なコード (Googleドキュメントにアップロードされ、Googleアカウントが必要な場合があります)
PSこれは、サンドボックスでの私の投稿からの再投稿です。 これが受け入れられない場合は申し訳ありませんが、コメントを聞くのは面白いでしょう。 招待してくれたロマチェフに感謝します。 テーマ別ブログへの投稿にはカルマがありません。