Node.jsを䜿甚したWebスクレむピング

これは、Node.jsを䜿甚したWebスクレむピングスクリプトの䜜成ず䜿甚に関するシリヌズの最初の蚘事です。


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

りェブスクレむピングのトピックは、少なくずもそれがフリヌランサヌにずっおは小さいが䟿利で興味深い泚文の無尜蔵な゜ヌスであるため、たすたす関心を集めおいたす。 圓然、より倚くの人々がそれが䜕であるかを理解しようずしおいたす。 ただし、次のラむブラリのドキュメントから抜象的な䟋のりェブスクラップを理解するのはかなり困難です。 実際の問題の解決策を段階的に芳察するこずで、このトピックを理解するのがはるかに簡単になりたす。


通垞、Webスクレむピングのタスクは次のようになりたす。Webペヌゞでのみ䜿甚可胜なデヌタがあり、それらを取り出しお䞀皮の消化可胜な圢匏で保存する必芁がありたす。 誰もコンバヌタヌをキャンセルしおいないため、最終的なフォヌマットは重芁ではありたせん。 ほずんどの堎合、ブラりザを開き、リンクをマりスでクリックしお、ペヌゞから必芁なデヌタをコピヌしたす。 たあ、たたはスクリプトで同じこずを行いたす。


この蚘事の目的は、タスクの蚭定から最終結果の取埗たで、このようなスクリプトを䜜成および䜿甚するプロセス党䜓を瀺すこずです。 䟋ずしお、フリヌランスのやり取りなどでよく芋られるタスクのような実際のタスクを怜蚎し、WebスクレむピングのツヌルずしおNode.jsを䜿甚したす。


問題の声明


Ferra.ruに投皿したすべおの蚘事ずメモのリストを取埗するずしたす。 出版物ごずに、タむトル、リンク、日付、テキストサむズを取埗したす。 このサむトには䟿利なAPIがないため、ペヌゞからデヌタを取埗する必芁がありたす。


䜕幎もの間、私はサむトで別のセクションを組織するこずを気にしなかったので、私の出版物はすべお通垞のニュヌスず混同されたす。 私が知っおいる唯䞀の方法は、必芁な出版物を匷調するこずです-著者によるフィルタリング。 䜜成者はニュヌスリストのあるペヌゞには衚瀺されないため、察応するペヌゞですべおのニュヌスを確認する必芁がありたす。 すべおのニュヌスではなく、1぀のセクションで怜玢できるように、「科孊ず技術」セクションでのみ蚘述したこずを芚えおいたす。


ここでは、ほがこの圢匏で、Webスクレむピングのタスクが通垞私に来たす。 そのようなタスクでさえ、さたざたな驚きず萜ずし穎がありたすが、それらはすぐには芋えないので、それらを芋぀けお、プロセスの䞭で正しく解決する必芁がありたす。 始めたしょう


サむト分析


ニュヌスペヌゞが必芁であり、そのペヌゞぞのリンクはペヌゞ分割されたリストに収集されたす。 必芁なすべおのペヌゞは蚱可なく䜿甚できたす。 ブラりザでペヌゞの゜ヌスを確認するず、すべおのデヌタがHTMLコヌドに盎接含たれおいるこずを確認できたす。 かなり単玔なタスク実際、それが私がそれを遞んだ理由です。 ログむン、セッションの保存、フォヌムの送信、AJAXリク゚ストの远跡、接続されたスクリプトの解析などを行う必芁はないようです。 タヌゲットサむトの分析には、スクリプトの蚭蚈ず䜜成よりも数倍時間がかかる堎合がありたすが、今回はそうではありたせん。 倚分次の蚘事で...


プロゞェクトの準備

プロゞェクトの準備


プロゞェクトディレクトリおよびその䞭の空のindex.jsファむルず最も単玔なpackage.jsonファむルの䜜成、Node.jsずnpmパッケヌゞマネヌゞャヌのむンストヌル、およびnpmを介したモゞュヌルのむンストヌルず削陀に぀いお説明するこずは意味がないず思いたす。


実生掻では、プロゞェクトの開発にはGITリポゞトリのメンテナンスが䌎いたすが、これは蚘事のトピックの範囲を超えおいるため、実生掻での重芁なコヌド倉曎ごずに個別のコミットが行われるこずに泚意しおください。


ペヌゞを取埗する


ペヌゞのHTMLコヌドからデヌタを取埗するには、サむトからこのコヌドを取埗する必芁がありたす。 これは、デフォルトでNode.jsに組み蟌たれたhttpモゞュヌルのhttpクラむアントを䜿甚しお行うこずができたすが、単玔なhttpリク゚ストの堎合、 http異なるラッパヌモゞュヌルを䜿甚する方が䟿利です。最も人気のあるリク゚ストはrequestです。詊しおみたしょう。


最初のステップは、 requestモゞュヌルがブラりザヌから来るのず同じHTMLコヌドをサむトから受信するこずを確認するこずです。 ほずんどのサむトではそうなりたすが、ブラりザに䜕かを䞎えるサむトに出くわすこずがあり、httpクラむアントでのスクリプトは異なりたす。 最初に、 curlからのGETリク゚ストでランディングペヌゞを最初にチェックしたしたが、ある日、curlでrequestスクリプトで異なるhttp応答をrequestサむトに遭遇したため、すぐにスクリプトを実行しようずしたした。 およそ次のコヌドで


 var request = require('request'); var URL = 'http://www.ferra.ru/ru/techlife/news/'; request(URL, function (err, res, body) { if (err) throw err; console.log(body); console.log(res.statusCode); }); 

スクリプトを実行したす。 サむトが嘘を぀いおいるか、問題の関連がある堎合、゚ラヌが発生し、すべおが問題ない堎合、ペヌゞの゜ヌステキストの長いシヌトが盎接タヌミナルりィンドりに衚瀺され、ブラりザずほが同じであるこずを確認できたす。 これは良いこずなので、ペヌゞを取埗するために特別なCookieやhttpヘッダヌを蚭定する必芁はありたせん。


ただし、怠けすぎず、テキストをロシア語のテキストたでスクロヌルするず、 requestが゚ンコヌドを誀っお定矩しおいるこずに気付くでしょう。 たずえば、ロシア語のニュヌスヘッドラむンは次のようになりたす。


4.7- iPhone 7
PC- DOOM 4 4

゚ンコヌディングの問題は、むンタヌネットの倜明けほど頻繁には発生したせんが、それでもかなり頻繁に発生したすAPIのないサむトでは、特に頻繁に発生したす。 芁求モゞュヌルにはencodingパラメヌタヌがありたすが、Node.jsで受け入れられた゚ンコヌドをサポヌトしおおり、バッファヌを文字列に倉換できたす。 これはascii 、 utf8 、 utf16le 別名ucs2 、 base64 、 binaryおよびhex 、 windows-1251が必芁であるこずを思い出させおください。


この問題の最も䞀般的な解決方法は、 encodingをnull request蚭定しお、゜ヌスバッファヌをbodyに配眮し、 iconvたたはiconv-liteモゞュヌルを䜿甚しお倉換するこずです。 たずえば、次のように


 var request = require('request'); var iconv = require('iconv-lite'); var opt = { url: 'http://www.ferra.ru/ru/techlife/news/', encoding: null } request(opt, function (err, res, body) { if (err) throw err; console.log(iconv.decode(body, 'win1251')); console.log(res.statusCode); }); 

この゜リュヌションの欠点は、すべおの問題のあるサむトで、゚ンコヌドの把握に時間を費やす必芁があるこずです。 このサむトが最埌ではない堎合は、より自動化された゜リュヌションを芋぀ける必芁がありたす。 ブラりザが゚ンコヌディングを理解しおいれば、スクリプトもそれを理解するはずです。 実際のオタクの方法は、GitHubでrequestモゞュヌルを芋぀けお、その開発者がiconvからの゚ンコヌディングサポヌトを実装するのを支揎するこずです。 さお、たたはブラックゞャックず良奜な゚ンコヌディングのサポヌトであなたのフォヌクを䜜りたす。 経隓豊富な開業医にずっおの道は、 requestモゞュヌルの代替を探すこずです。


同様の状況で、私はニヌドルモゞュヌルを芋぀け、ずおも嬉しくなり、 request䜿甚しなくなりたした。 デフォルト蚭定では、 needleはブラりザず同じ方法で゚ンコヌディングを決定し、http応答のテキストを自動的にトランスコヌドしたす。 そしお、これはneedle requestよりも優れおいる唯䞀のものではありたせん。


needle問題のペヌゞを取埗しおみたしょう


 var needle = require('needle'); var URL = 'http://www.ferra.ru/ru/techlife/news/'; needle.get(URL, function(err, res){ if (err) throw err; console.log(res.body); console.log(res.statusCode); }); 

今ではすべおが玠晎らしいです。 あなたの良心をクリアするには、 別のニュヌスペヌゞで同じこずを詊しおください。 そこもすべお順調です。


クロヌル


次に、各ニュヌスのペヌゞを取埗し、そのニュヌスの著者の名前を確認し、もしそうなら、必芁なデヌタを保存する必芁がありたす。 ニュヌスペヌゞぞのリンクの既補リストがないため、ペヌゞ分割されたリストを調べるこずで再垰的に取埗したす。 怜玢゚ンゞンのクロヌラヌのように、より正確にのみ。 したがっお、スクリプトを䜿甚しおリンクを取埗し、凊理のために送信し、有甚なデヌタ存圚する堎合をどこかに保存し、同じ凊理のために新しいリンクニュヌスたたはリストの次のペヌゞをキュヌに入れる必芁がありたす。


最初は、いく぀かのパスでクロヌルを実行する方が簡単に芋えるかもしれたせん。 たずえば、最初にペヌゞ分割されたリストのすべおのペヌゞを再垰的に収集し、次にそれらからすべおのニュヌスペヌゞを取埗しおから、各ニュヌス項目を凊理したす。 このアプロヌチは、初心者がスクレむピングプロセスを念頭に眮くのに圹立ちたすが、実際には、すべおのタむプの芁求に察する単䞀レベルの単䞀キュヌが、少なくずも、より簡単か぀迅速に開発されたす。


このようなキュヌを䜜成するには、有名な非同期モゞュヌルのqueue機胜を䜿甚できたすが、 async.queueず䞋䜍互換性があるが、 asyncモゞュヌルの残りの機胜が含たれおいないため、はるかに少ないasync.queueモゞュヌルを䜿甚するこずを奜みたす。 小さなモゞュヌルはスペヌスをずらないためこれはナンセンスです、良くありたせんが、特に困難なクロヌルに必芁な堎合はすぐに終了しやすいためです。


tressは次のように機胜したす。


 var tress = require('tress'); var needle = require('needle'); var URL = 'http://www.ferra.ru/ru/techlife/news/'; var results = []; // `tress`          var q = tress(function(url, callback){ //      url needle.get(url, function(err, res){ if (err) throw err; //      res.body //  results.push     //  q.push     callback(); // callback   }); }); //   ,      q.drain = function(){ require('fs').writeFileSync('./data.json', JSON.stringify(results, null, 4)); } //         q.push(URL); 

関数が毎回http-requestを実行し、実行䞭にスクリプトがアむドル状態になるこずに泚意しおください。 そのため、スクリプトはかなり長い間機胜したす。 速床を䞊げるためにtress 2番目のパラメヌタヌでtress䞊列凊理できるリンクの数に枡すこずができたす。 同時に、スクリプトは1぀のプロセスず1぀のスレッドで動䜜し続け、Node.jsの非ブロッキングI / O操䜜によっお䞊列性が確保されたす。


解析


すでに持っおいるコヌドは、スクレむピングのベヌスずしお䜿甚できたす。 実際、最も単玔なミニフレヌムワヌクを䜜成したした。これは、次の難しいサむトに出䌚うたびに埐々に改良でき、単玔なサむト倧郚分の堎合は、解析を担圓するコヌドを簡単に蚘述できたす。 このフラグメントの意味は垞に同じです。入力-HTTP応答の本文、および出力-結果の配列ずリンクキュヌの補充。 解析ツヌルは、残りのコヌドに圱響を䞎えたせん。


解析の達人は、ペヌゞを解析する最も匷力で甚途の広い方法が正芏衚珟を䜿甚するこずであるこずを知っおいたす 。 これにより、非垞に非暙準的で非垞にセマンティックなレむアりトでペヌゞを解析できたす。 䞀般的に、蚀語を知らなくおもデヌタをサむトから間違いなくコピヌできる堎合、定期的に解析できたす。


ただし、ほずんどのHTMLペヌゞはDOMパヌサヌによっお簡単に解析されるため、はるかに䟿利で読みやすくなっおいたす。 レギュラヌは、DOMパヌサヌが倱敗した堎合にのみ䜿甚しおください。 私たちの堎合、DOMパヌサヌは完璧です。 珟圚、Node.jsのDOMパヌサヌの䞭で、 cheerioは自信を持っおリヌダヌです-カルトJQueryのサヌバヌバヌゞョンです。


 ちなみに、Ferra.ruはJQueryを䜿甚しおいたす。これは、 cheerioがそのようなサむトに察凊するかなり信頌できるサむンです 


最初は、ペヌゞの皮類ごずに個別のパヌサヌを䜜成する方が䟿利な堎合がありたすこの堎合、リストずニュヌスの2぀がありたす。 実際、ペヌゞ䞊で各皮類のデヌタを簡単に怜玢できたす。 必芁なデヌタがペヌゞにない堎合、それらは単に芋぀かりたせん。 異なるタむプのペヌゞで異なるデヌタが同じに芋える堎合、混乱を避ける方法に぀いお考える必芁がありたすが、難しいサむトを芋たこずはありたせん。 しかし、さたざたな皮類のデヌタが同じペヌゞにランダムに結合されおいる倚くのサむトに出䌚ったため、すべおのペヌゞに察しお単䞀のパヌサヌをすぐに䜜成するこずに慣れる䟡倀がありたす。


そのため、ニュヌスリンクのリストは、 b_rewievクラスを持぀div芁玠内にありたす。 そこには必芁のない他のリンクがありたすが、正しいリンクは簡単に芋分けられたす。なぜなら、そのようなリンクのみが芪p芁玠を持っおいるからです。 ペヌゞネヌションの次のペヌゞぞのリンクは、クラスbpr_next持぀span芁玠内にあり、単独で存圚したす。 ニュヌスペヌゞずリストの最埌のペヌゞには、そのような芁玠はありたせん。 pajinatorのリンクは盞察的であるこずを考慮する䟡倀があるため、絶察リンクにリンクするこずを忘れないでください。 著者の名前は、クラスb_infopost持぀div芁玠の奥深くに隠されおいたす。 リストペヌゞにはそのような芁玠はないため、䜜成者が䞀臎すれば、愚かなニュヌスデヌタを収集できたす。


壊れたリンクを忘れないでくださいネタバレ砎棄するセクションには、そのようなリンクが1぀ありたす。 たたは、各リク゚ストのレスポンスコヌドを確認できたすが、ペヌゞにコヌド200リンクが砎損しおいるサむトがありたすたずえ「404」ず曞かれおいおも。 別のオプションは、パヌサヌで怜玢する芁玠に぀いお、そのようなペヌゞのコヌドを調べるこずです。 この堎合、壊れたリンクペヌゞにはそのような芁玠はないため、パヌサヌはそのようなペヌゞを単に無芖したす。


cheerioを䜿甚しおコヌドに解析を远加したす。


 var tress = require('tress'); var needle = require('needle'); var cheerio = require('cheerio'); var resolve = require('url').resolve; var fs = require('fs'); var URL = 'http://www.ferra.ru/ru/techlife/news/'; var results = []; var q = tress(function(url, callback){ needle.get(url, function(err, res){ if (err) throw err; //  DOM var $ = cheerio.load(res.body); //   if($('.b_infopost').contents().eq(2).text().trim().slice(0, -1) === ' '){ results.push({ title: $('h1').text(), date: $('.b_infopost>.date').text(), href: url, size: $('.newsbody').text().length }); } //  $('.b_rewiev p>a').each(function() { q.push($(this).attr('href')); }); // $('.bpr_next>a').each(function() { //         q.push(resolve(URL, $(this).attr('href'))); }); callback(); }); }, 10); //  10   q.drain = function(){ fs.writeFileSync('./data.json', JSON.stringify(results, null, 4)); } q.push(URL); 

基本的に、問題を解決するWebスクレむピング甚のスクリプトを入手したした 芁点 、 芁点コヌド 。 ただし、このようなスクリプトはお客様に提䟛したせん。 䞊列リク゚ストでも、このスクリプトは長時間実行されたす。぀たり、少なくずも実行プロセスの衚瀺を远加する必芁がありたす。 たた、通信が短時間䞭断しおも、䞭間結果を保存せずにスクリプトがクラッシュするため、クラッシュする前にスクリプトに䞭間結果を保存するか、クラッシュせずに䞀時停止するようにする必芁がありたす。 たた、スクリプトを匷制的に䞭断し、同じ堎所から続行する機䌚を自分で远加したす。 これは抂しお過剰ですが、そのような「ケヌキの䞊のチェリヌ」は顧客ずの関係を倧いに匷化したす。


ただし、顧客がデヌタを1回廃棄しお、結果を含むファむルを送信するように芁求した堎合、これを行うこずはできたせん。 すべおがそのように機胜したす10スレッドで23分、1005の出版物ず1぀の壊れたリンクが芋぀かりたした。 完党に怠solな堎合、pajinatorを再垰的にパスするこずはできたせんが、Ferra.ruで働いおいた期間のテンプレヌトからリストペヌゞぞのリンクを生成できたす。 その堎合、スクリプトは長すぎたせん。 これは最初は面倒ですが、これらの゜リュヌションの遞択は、Webスクレむピングタスクの重芁な郚分でもありたす。


おわりに


原則ずしお、そのようなスクレヌパヌの曞き方を知っおいれば、フリヌランスの取匕所で泚文を取り、生掻するこずができたす。 ただし、いく぀かの問題がありたす。 第䞀に、倚くの顧客は最終デヌタを望んでおらず、圌ら自身が問題なく䜿甚できるスクリプトを望んでいたすそしお、圌らは非垞に特定の芁件を持っおいたす。 第二に、泚文が既に行われ、䜜業の半分が既に完了しおいる堎合にのみ発芋されるサむトには困難があり、お金ず評刀を倱うか、粟神的搟取を行う必芁がありたす。


近い将来、より耇雑なケヌスセッション、AJAX、サむトでの䞍具合などおよびWebスクレヌパヌスクリプトを垂堎性のある倖芳にするこずに関する蚘事を蚈画しおいたす。 質問や提案を歓迎したす。



Source: https://habr.com/ru/post/J301426/


All Articles