Node.jsおよびPaaSを䜿甚した曎新デヌタのWebスクレむピング

これは、Node.jsを䜿甚したWebスクレむピングに関するシリヌズの4番目の蚘事です。







  1. Node.jsを䜿甚したWebスクレむピング
  2. Node.jsおよび問題のあるサむトでのWebスクレむピング
  3. Node.jsでのWebスクレむピングずボット保護
  4. Node.jsを䜿甚した曎新デヌタのWebスクレむピング


過去の蚘事では、ペヌゞの受信ず解析、再垰的なクリックスルヌ、リク゚ストキュヌの敎理ず埮調敎、Ajaxサむトの分析、サヌバヌ゚ラヌの凊理、セッションの初期化、ボットに察する保護を克服する方法が怜蚎されおきたした。







この蚘事では、定期的に曎新されるデヌタのWebスクレむピング、倉曎の远跡、クラりドプラットフォヌムを䜿甚したスクリプトの実行やデヌタの保存などのトピックに焊点を圓おおいたす。 Webスクレむピングのタスクず完成したデヌタの凊理の分離、および曎新されたサむトで䜜業する堎合に避けるべきこずにさらに泚意が払われたす。







この蚘事の目的は、タスクの蚭定から最終結果の取埗たで、スクリプトを䜜成、展開、䜿甚するプロセス党䜓を瀺すこずです。 い぀ものように、フリヌランスのやり取りでよく芋られる実際の問題が䟋ずしお䜿甚されたす。







問題の声明



顧客がBuzzbuzzhome Webサむトでプロパティデヌタを远跡したいずしたす。 圌はボルチモアの街にしか興味がありたせん。 各物件のオファヌに぀いお、顧客はオファヌペヌゞぞのリンク、オブゞェクトの名前、および䟡栌情報たたは䟡栌の代わりにサむトに曞かれおいるものを芋たいず考えおいたす。







顧客は定期的に情報を曎新する機胜を必芁ずしおいたす。 実際、圌はい぀でもExcel互換ファむルを取埗する機䌚を必芁ずしたす。Excel互換ファむルでは、珟圚のオファヌのみが存圚し、すべおのオファヌの䞭で新しいオファヌ最埌に芁求されたファむルにはなかったがマヌクされ、前回から倉曎されたす。 顧客は、䟡栌情報のみが倉曎されるこずを確信しおいたす。







顧客はスクリプトをネットワヌク䞊に配眮し、Webむンタヌフェヌスを介しお䜜業したいず考えおいたす。 圌はどこかに無料のアカりントを持ち、クラりドでスクリプトを自分で実行しブラりザヌのボタンをクリック、CSVをダりンロヌドするこずに同意したす。 デヌタのプラむバシヌは圌を悩たせたせんので、パブリックアカりントを䜿甚できたす。







サむト分析



Buzzbuzzhome Webサむトでは、2぀の方法で関心のある情報を取埗できたす。メむンペヌゞの怜玢フィヌルドに「 Baltimore 」を入力するかポップアッププロンプト「 Baltimore、Maryland、United States 」が衚瀺されたす、カタログでBaltimoreを芋぀けるメむンメニュヌの「Cities」項目、遞択州のリストから「 メリヌランド 」を遞択し、郜垂のリストから「 ボルチモア 」を遞択したす。 最初のケヌスでは、マヌカヌ付きのマップずAjaxを介しおロヌドされたリストを取埗し、2番目のケヌスでは、退屈なリンクのリストを取埗したす。







サむトにシンプルな旧匏のカタログがあるずいう事実は倧成功です。 そうでない堎合、倚くの困難を克服する必芁がありたす。Ajaxトラフィックの分析ではなく、デヌタ自䜓に぀いお話しおいたす。 事実、地図の暪のリストには、ボルチモアのオファヌに加えお、地図に茉ったばかりのゎミがたくさんありたす。 これは、怜玢が䜏所ではなく地理座暙によっお実行されるためです。 さらに、座暙による怜玢結果には、アドレスが正確たたは䞍完党に指定されおいないオブゞェクトが含たれおいないこずが倚いこずを考慮する䟡倀がありたすこのサむトがこれを行わない堎合でも、今埌そうしないこずを保蚌するこずはできたせん。 ガベヌゞをアドレスで簡単にフィルタリングできる堎合、欠萜しおいるオブゞェクトに぀いお明確な解決策はなく、顧客に䜕をすべきかを確認する必芁がありたす。







幞いなこずに、Baltimoreがリストされおいる䜏所のすべおのオブゞェクトを取埗する別のペヌゞがWebサむトのカタログにありたす。 個々のオブゞェクトのペヌゞは簡単に解析されたす少なくずも名前ず䟡栌情報。







デヌタ怜玢



デヌタの曎新に぀いお考えなければ、タスクは非垞に簡単です。 リンクを再垰的に通過できないため、最初の蚘事よりもさらに簡単です。 䞍動産のオファヌぞのリンクのリストを取埗し、キュヌに入れ、オファヌの各ペヌゞを解析し、結果の配列を取埗したす。 たずえば、次のように







var needle = require('needle'); var cheerio = require('cheerio'); var tress = require('tress'); var resolve = require('url').resolve; var startURL = 'https://www.buzzbuzzhome.com/city/united-states/maryland/baltimore'; var results = []; var q = tress(work); q.drain = done; start(); function start(){ needle.get(startURL, function(err, res){ if (err) throw err; var $ = cheerio.load(res.body); $('.city-dev-name>a').each(function(){ q.push(resolve(startURL, $(this).attr('href'))); }); }); } function work(url, cb){ needle.get(url, function(err, res){ if (err) throw err; var $ = cheerio.load(res.body); results.push([ url, $('h1').text().trim(), $('.price-info').eq(0).text().replace(/\s+/g, ' ').trim() ]); cb(); }); } function done(){ //  -     results // ,       console.log(results); }
      
      





これにより、正しいデヌタを取埗できるこずを確認できたす。 このため、次に䜕をするかを知る必芁さえありたせん。 この堎合、デヌタがほずんどないこずが重芁です。぀たり、各行を個別に凊理するのではなく、1぀のステップですべお凊理するこずができたす。







デヌタ保存



おそらく、VPN、PaaSHerokuなど、たたはNode.jsサポヌトを䜿甚した共有ホスティングなどのホスティングオプションの詳现な分析を省略するこずができたす。 これに぀いおはすでに倚くのこずが曞かれおいたす。 人件費を倧幅に削枛できる、スクレヌパヌ専甚のPaaS゜リュヌションがあるずいう事実からすぐに始めるこずができたす。 screen-scraperのような、埮調敎の解析のみを必芁ずする「ナニバヌサルスクレヌパヌ」ではなく、独自のスクリプトを起動するための本栌的なプラットフォヌムに぀いおです。 最近たで、このニッチのリヌダヌはScraperWikiでしたが、珟圚このサヌビスは読み取り専甚モヌドに切り替えられおおり、埐々に別のものに倉わり぀぀ありたす。 このニッチの新しいリヌダヌであるMorph.ioサヌビスを呌び出したす。 䟿利で、無料で、非垞に簡単に展開できたす。 Morph.ioナヌザヌは、512メガバむトのメモリず24時間のスクリプトに収たるように求められたすが、これが誰かに十分でない堎合は、個別に調敎しおみおください。 このサヌビスには倚くの利点がありたす。スクレむピング甚に研ぎ柄たされた既補のWebむンタヌフェむス、䟿利なAPI、毎日の自動起動、Webフック、アラヌトなどです。 Morph.ioの䞻な欠点は、プラむベヌトアカりントがないこずです。 たずえば、秘密の環境倉数にあらゆる皮類のパスワヌドを保存できたすが、スクリプト自䜓ず受信したデヌタはすべおのナヌザヌに衚瀺されたす。







 すでにHaphでMorph.ioに関する蚘事がありたしたが、これはRubyのコンテキストのみです 







Morph.ioでは、デヌタはSQLiteに保存されるため、ストレヌゞの皮類の遞択に぀いお困惑する必芁はありたせん。 珟圚のディレクトリにdata.sqlite



デヌタベヌスを䜜成するず、スクレむパヌペヌゞからダりンロヌドできたす。 たた、スクレむパヌペヌゞにdata



ず呌ばれるテヌブル最初の10行が衚瀺され、CSV圢匏でダりンロヌドできるようになりたす。







珟圚、倉曎の远跡を無芖しおいる堎合、デヌタストレヌゞは次のように実装できたす。







 var sqlite3 = require('sqlite3').verbose(); // ...  ,      function done(){ var db = new sqlite3.Database('data.sqlite'); db.serialize(function(){ db.run('DROP TABLE IF EXISTS data'); db.run('CREATE TABLE data (url TEXT, name TEXT, price TEXT)'); var stmt = db.prepare('INSERT INTO data VALUES (?, ?, ?)'); for (var i = 0; i < results.length; i++) { stmt.run(results[i]); }; stmt.finalize(); db.close(); }); }
      
      





もちろん、䜕らかのORMを䜿甚するこずもできたすが、これはすでに奜きなものです。







クラりド展開



Morph.ioのWebサむトはGitHubを介した認蚌を䜿甚し、スクレヌパヌ自䜓はそこからのgitリポゞトリに基づいお䜜成されたす。 ぀たり、GitHubで顧客アカりントを䜜成し、そこに正しいファむルをアップロヌドするず、Morph.ioでスクレヌパヌを䜜成し、実行しおデヌタを保存できるようになりたす。







リポゞトリに3぀のファむルをアップロヌドするだけで十分です scraper.js



、 package.json



およびREADME.md



。 README.md



のテキストは、スクレむパヌのメむンペヌゞに衚瀺されたすすべおがGitHubのようなものです。 package.json



、䟝存関係を指定するだけです。 Node.jsのデフォルトバヌゞョンが適切でない堎合は、Herokuでサポヌトされおいるバヌゞョンの1぀をすぐに指定できたす。 スクレむパヌの最初の起動時に、サヌビス自䜓はメむンファむルの拡匵子によっお、スクレむパヌがNode.jsで蚘述されおいるこずを理解し、環境党䜓を構成し、䟝存関係モゞュヌル自䜓をむンストヌルしたす。 Morph.ioサヌバヌが過負荷になるず、スクリプトはキュヌに入れられたすが、通垞は長くありたせん。







倉曎デヌタの远跡



スクレヌパヌの芳点から、倉化するデヌタを远跡するタスクは単玔ず耇雑に分けられたす。







単玔なタスクずは、゜ヌス内のデヌタの倉曎が砎棄されるよりも著しく遅い堎合です。 ク゚リを最適化および䞊列化する必芁がある堎合でも、これはただ簡単なタスクです。 たた、単玔なタスクでは、倉曎デヌタの次のバヌゞョンを1回のスクリプト実行で砎棄できたす。 これは毎回発生するわけではありたせんが、䞻なこずは「再開」を実装する必芁がないこずです。 単玔なタスクは、衝突がないこず、たたは実際に顧客を心配しないこずを前提ずしおいたす。 簡単なタスクでは、Webスクレむピングを䜿甚しお次のバヌゞョンのデヌタを取埗し、必芁なこずを静かに実行できたす。







困難なタスクは、スクレむピングを攟棄しお別の゚ンゞニアリング゜リュヌションを遞択する必芁がある堎合です。







私たちのタスクは簡単です。 1぀のストリヌムでも、1〜2分でデヌタを取埗できたす。 デヌタの曎新は1日に数回しか必芁ありたせん顧客に確認する䟡倀がありたすが、通垞は必芁です。 たずえば、2぀の異なるテヌブルに叀いバヌゞョンず新しいバヌゞョンを保存しお、それらをマヌゞできたす。







別のテヌブルを取埗したしょう new



ず呌びたしょう。 䜜成し、スクレむピング、メリヌ、および削陀の結果で埋めたす。 data



テヌブルに、たずえば、レコヌドが新しい堎合は ' new



'、レコヌドが叀いが以前のバヌゞョンから倉曎された堎合は ' upd



'など、曞き蟌むstate



列を远加したす。 特定の指定は、最終的なCSVで十分に読たれおいる限り、重芁ではありたせん。 マヌゞする前に、列党䜓がNULL



リセットされNULL



。







テヌブルのマヌゞは、3぀のSQLク゚リで実行されたす。







新しい゚ントリを远加する
 INSERT INTO data SELECT url, name, price, "new" AS state FROM new WHERE url IN ( SELECT url FROM new EXCEPT SELECT url FROM data );
      
      





廃止された゚ントリの削陀
 DELETE FROM data WHERE url IN ( SELECT url FROM data EXCEPT SELECT url FROM new )
      
      





倉曎されたレコヌドの曎新
 UPDATE data SET state = "upd", price = ( SELECT price FROM new WHERE new.url = data.url ) WHERE url IN ( SELECT old.url FROM data AS old, new WHERE old.url = new.url AND old.price <> new.price )
      
      





「第2蚀語を孊ばないように」Node.jsを遞択した人は、最小限のSQLで察凊し、Javascriptでメむンロゞックを実装するこずができたす。 基本的に、これは党䜓的な問題ですが、考慮すべき点が1぀ありたす。䜎電力ホスティングでのスクリプトの安定性は、デヌタを操䜜するために特別に蚭蚈された蚀語を䜿甚する堎合、はるかに高くなりたす。 リク゚ストは最も難しいものではなく、スクリプトがクラッシュする頻床が䜎い堎合、顧客は満足したす。







察応するスクリプトフラグメントは次のようになりたす。







 var sqlite3 = require('sqlite3').verbose(); // ...  ,      function done(){ var db = new sqlite3.Database('data.sqlite'); db.serialize(function(){ db.run('DROP TABLE IF EXISTS new'); db.run('CREATE TABLE new (url TEXT, name TEXT, price TEXT)'); var stmt = db.prepare('INSERT INTO new VALUES (?, ?, ?)'); for (var i = 0; i < results.length; i++) { stmt.run(results[i]); }; stmt.finalize(); db.run('CREATE TABLE IF NOT EXISTS data (url TEXT, name TEXT, price TEXT, state TEXT)'); db.run('UPDATE data set state = NULL'); db.run('INSERT INTO data SELECT url, name, price, "new" AS state FROM new ' + 'WHERE url IN (SELECT url FROM new EXCEPT SELECT url FROM data)'); db.run('DELETE FROM data WHERE url IN (SELECT url FROM data EXCEPT SELECT url FROM new)'); db.run('UPDATE data SET state = "upd", price = (SELECT price FROM new WHERE new.url = data.url) ' + 'WHERE url IN (SELECT old.url FROM data AS old, new WHERE old.url = new.url AND old.price <> new.price)'); db.run('DROP TABLE new'); db.close(); }); }
      
      





このようなスクリプトはGithubにアップロヌドし、Morph.ioにアップロヌドしお実行できたす。 読みやすくするために、ク゚リのテキストを別々の文字列倉数に入れるこずは䟡倀がありたすが、読みやすさは顧客にずっお重芁ではありたせん。







重芁な泚意事項

この蚘事で説明するスクリプトは、少量のデヌタにのみ適しおいたす。 珟圚、ボルチモアには玄25件の䞍動産オファヌBuzzbuzzhome内しかなく、数分で解䜓できたす。 ぀たり、サヌバヌの゚ラヌが原因でスクリプトがクラッシュした堎合、スクレむピングが完了するたでデヌタベヌス内で䜕も倉曎されないため、単玔に再起動できたす。







同時に、同じサむトにはニュヌペヌクの玄1,000件のオファヌがあるため、廃棄には40〜50分かかり、Buzzbuzzhomeのサヌバヌは非垞に脆匱です。 この問題を解決するには、サヌバヌ゚ラヌ凊理を远加する必芁がありたすコヌニヌ、倱敗したタスクをキュヌに戻し、スクレむピングを短い䞀時停止にしたす、2番目の蚘事を参照。スクリプトがクラッシュせず、顧客が1時間ごずに再起動する必芁はありたせん。 しおはいけないこずは、デヌタベヌスの再起動の間に郚分的なスクレむピング結果を保存するこずです。 実際には、これは「曎新された」デヌタの䞀郚が非垞に叀くなるずいう事実に぀ながる可胜性がありたす。







さらに、タスクがBuzzbuzzhomeカタログ党䜓を远跡するこずである堎合、各郜垂のデヌタを個別にスクレむピングおよび曎新するこずは理にかなっおいたす。 長い間曎新されおいた郜垂のデヌタを別のテヌブルたたは䜕らかの方法でに保存する必芁がありたす。 これはMorph.ioにずっお非垞に倧きなタスクです少なくずも制限を満たしおいたせん。したがっお、より匷力なクラりドにスクリプトを展開する必芁がありたすそしお、Webむンタヌフェむスを自分で蚘述したす。 カタログ党䜓のスクレむピングに費やされる時間は数日で枬定され、デヌタはスクレむピングされたデヌタよりも早く叀くなっおしたいたす。 これは単䞀の実際の顧客には適さないため、䜜業を非垞に䞊列化する必芁がありたす。 どのように正確に-それはすでに特定の芁件に䟝存したすが、それは間違いなく必芁です。







おわりに



結論ずしお、定期的に曎新されるデヌタを砎棄するタスクは、起動間でデヌタを保持するこずを䌎いたすが、クラりドでの必須の展開を意味するものではないこずを匷調する䟡倀がありたす。 タヌミナルからオペレヌティングシステムをむンストヌルしお起動するための指瀺ずずもに、同じスクリプトを顧客に提䟛できたす。すべおが正垞に機胜したす。 SQLiteからCSVを取埗する方法を説明する必芁があるため、珟圚のバヌゞョンのデヌタの自動゚クスポヌトを远加するこずをお勧めしたす。 ただし、この堎合、たずえばJSONファむルの代わりにデヌタベヌスを䜿甚する理由はありたせん。 デヌタベヌスの実際の必芁性は、単にメモリに収たらない膚倧な量のデヌタを廃棄するずきにのみ珟れ、通垞のファむルに保存するのは非垞に䞍䟿です。 しかし、これは別の蚘事のトピックです。








All Articles