基於Casperjs的網頁抓取技術【抓取豆瓣信息網絡爬蟲實戰示例】

CasperJS is a navigation scripting & testing utility for the PhantomJS (WebKit) and SlimerJS (Gecko) headless browsers, written in Javascript.

PhantomJS是基於WebKit內核的headless browsernode

SlimerJS則是基於Gecko內核的headless browserjquery

Headless browser: 無界面顯示的瀏覽器,能夠用於自動化測試,網頁截圖,JS注入,DOM操做等等方面,是一種很是新型的web應用工具。雖然這種瀏覽器沒有任何界面輸出,但在不少方面均可以有很是普遍的應用。整篇文章將會介紹使用Casperjs進行網頁抓取(網絡爬蟲)的應用,本文僅僅是起到一個拋磚引玉的做用,實際上headless browser技術的應用會很是普遍,甚至又可能深入影響web先後端技術的發展。web

本文用一個著名的網站【豆瓣網】「開刀」(僅僅是研究學習使用,但願該站不要找我麻煩Smile),來試驗一下強大的Headless Browser網頁抓取技術的強悍。sql

第一步,安裝Casperjs 打開CasperJS的官網http://casperjs.org/,下載最新穩定版本的CasperJS並安裝,官網有很是詳細的文檔,是學習CasperJS最好的第一手材料。固然了,若是安裝了npm,也能夠直接經過npm安裝。同時,這也是官方推薦的安裝方法。關於安裝就很少介紹了,官方文檔介紹得很是詳細。mongodb

  1 npm install casperjs
  2 node_modules/casperjs/bin/casperjs selftest
View Code

第二步,分析目標網站的列表頁的網頁結構 一般內容類網站都是分紅列表頁面和詳細內容頁面。豆瓣網也不例外,咱們先來看看豆瓣的列表頁長什麼樣。分析之後發現豆瓣電影網的列表頁是這樣的,首先能夠點排序的規則,翻頁不是像傳統的網站經過頁碼來翻頁,而是點擊最後面的加載更多,這樣的網頁,傳統的爬蟲程序每每就歇菜了,或者實現起來很是複雜。可是對於headless browser技術,這個都是小Case。經過分析網頁就能夠看到點擊這個【加載更多】這個位置就可以不斷得顯示跟多影片信息。數據庫

 

doubanlist

第三步,開始寫代碼獲取影片詳情頁的連接信息 咱們就不客氣了,模擬點擊這個地方,收集超鏈列表, 下面的代碼就是獲取連接的代碼。引用並建立casperJS對象,若是網頁須要插入腳本能夠在casper對象生成的時候在ClientScript部分引用要注入網頁的腳本,爲了加快網頁的加載速度,咱們禁止下載圖片和插件:npm

  1  pageSettings: {
  2         loadImages:  false,        // The WebPage instance used by Casper will
  3         loadPlugins: false         // use these settings
  4     },
View Code


完整的獲取詳情頁連接的代碼,這裏模擬點擊【加載更多】並循環50次。其實循環能夠進行改進,【判斷 while(沒有」加載更多」) then( stop)】,得到後用require('utils').dump(….)輸出連接列表。保存下面的代碼爲getDoubanList.js, 而後運行 casperjs getDoubanList.js 就可以得到並輸出該分類下全部的詳情頁連接。後端

  1 1 phantom.outputEncoding="uft8";
  2    var casper = require('casper').create({
  3        // clientScripts:  [
  4        //     'includes/jquery.js',      // These two scripts will be injected in remote
  5        //     'includes/underscore.js'   // DOM on every request
  6        // ],
  7        pageSettings: {
  8            loadImages:  false,        // The WebPage instance used by Casper will
  9            loadPlugins: false         // use these settings
 10       },
 11       logLevel: "info",              // Only "info" level messages will be logged
 12       verbose: false                  // log messages will be printed out to the console
 13   });
 14 
 15   casper.start("https://movie.douban.com/explore#!type=movie&tag=%E7%BB%8F%E5%85%B8&sort=recommend&page_limit=20&page_start=0", function () {
 16       this.capture("1.png");
 17   });
 18 
 19   casper.then(function () {
 20       this.click("a.more",10,10);
 21       var i = 0;
 22       do
 23       {
 24        i ++;
 25        casper.waitForText('加載更多', function() {
 26        this.click("a.more",10,10);//this.capture("2.png");   // read data from popup
 27       });
 28       }
 29       while (i<50);
 30   });
 31 
 32 
 33   casper.then(function () {
 34   require('utils').dump(this.getElementsAttribute('div.list-wp div.list a.item', 'href'));
 35   'href')));
 36   });
 37  casper.waitForText('加載更多', function() {
 38 this.capture("3.png");   // read data from popup
 39   });
 40   casper.run();
GetDoubanList.js

我使用了Nodejs來調用casperjs(用其餘的語言好比Python,Java調用也是能夠的,CasperJS並非一個完整的系統,因此多線程,文本處理,數據庫仍是須要依賴其餘的語言或者工具),並把結果輸出到文件裏保存,固然把結果放到數據庫裏也沒有問題,可是這裏爲了簡化,就不展開了(實際的應用中我是用的MongoDB)。Nosql數據庫很是適合存放抓取下來的非結構化數據存儲。瀏覽器

  1   //var fs = require("fs");
  2   //var S = require("string");
  3   var url = 'mongodb://localhost:27017/test';
  4   //var trim = require('trim.js');
  5   //include recode url module
  6    var record = require('./RecordUrl');
  7 
  8 
  9    ///Program running block/////////////////////////////////////////////////////////////////////
 10   const spawn = require('child_process').spawn;
 11   const urllist = spawn('casperjs', ['casper3_more.js']);
 12   var strUrls = "";
 13 
 14   urllist.stdout.on('data', (data) => {
 15     console.log(data.toString());
 16     strUrls = strUrls + data.toString();
 17 
 18   });
 19 
 20   urllist.stderr.on('data', (data) => {
 21     console.log(data);
 22   });
 23 
 24   urllist.on('exit', (code) => {
 25     console.log(`Child exited with code ${code}`);
 26     var urlData = JSON.parse(strUrls);
 27   	var content2 = "";
 28   	for(var key in urlData){
 29   		if (content2 != "") {
 30   	           content2 =  content2 + "\r\n" + urlData[key];
 31   	        }
 32   	    else {
 33   	       		content2 = urlData[key];
 34   	    	}
 35   	}
 36   	var recordurl = new record.RecordAllUrl();
 37   	recordurl.RecordUrlInText(content2);
 38   	console.log(content2);
 39   });
 40 
GetAllUrls

 

引用的RecordUrl模塊,存MongoDB這一部分沒有寫,你們能夠自行完成。
  1 exports.RecordAllUrl = RecordUrl;
  2 var fs = require('fs');
  3 function RecordUrl() {
  4 	var file = "d:/urllog.txt";
  5 	var RecordUrlInFile = function(theurl) {
  6 
  9 	    fs.appendFile(file, theurl, function(err){
 10 	        if(err)
 11 	            console.log("fail " + err);
 12 	        else
 13 	            console.log("寫入文件ok");
 14 	    });
 15 	};
 16 	var RecordUrlInMongo = function() {
 17 		console.log('Hello ' + name);
 18 	};
 19 	 return {
 20         RecordUrlInDB: RecordUrlInMongo,
 21         RecordUrlInText: RecordUrlInFile
 22     } ;
 23 };
RecordUrl

第四步,分析詳情頁面並編寫詳情頁面抓取程序網絡

到這一步你們就已經得到了要抓取的詳情頁面的列表了,如今咱們打開一個電影詳情頁來看看結構如何,分析下各個信息如何抓取。對於信息的抓取必需要綜合使用DOM,文本處理和JS腳本等技術。我想得到這部分的信息,包括導演,編劇,評分等等。在本文就不重複了,這裏僅抽取幾個信息項例子演示。

doubanDetail

1. 抓取導演列表:導演列表的DOM CSS selector  'div#info span:nth-child(1) span.attrs a' , 咱們使用了function getTextContent(strRule, strMesg)  這個方法去抓取內容。

  1 phantom.outputEncoding="GBK";
  2 var S = require("string");
  3 var casper = require('casper').create({
  4     clientScripts:  [
  5         'includes/jquery.js',      // These two scripts will be injected in remote
  6         'includes/underscore.js'   // DOM on every request
  7     ],
  8     pageSettings: {
  9         loadImages:  false,        // The WebPage instance used by Casper will
 10         loadPlugins: false         // use these settings
 11     },
 12     logLevel: "info",              // Only "info" level messages will be logged
 13     verbose: false                  // log messages will be printed out to the console
 14 });
 15 
 16 //casper.echo(casper.cli.get(0));
 17 var fetchUrl='https://movie.douban.com/subject/25662329/', fetchNumber;
 18 if(casper.cli.has('url'))
 19 	fetchUrl = casper.cli.get('url');
 20 else if(casper.cli.has('number'))
 21 	fetchNumber = casper.cli.get('number');
 22 casper.echo(fetchUrl);
 23 
 24 casper.start(fetchUrl, function () {
 25     this.capture("1.png");
 26     //this.echo("啓動程序....");   
 27     //this.echo(this.getHTML('div#info span:nth-child(3) a'));   
 28     //this.echo(this.fetchText('div#info span:nth-child(1) a')); 
 29 
 30     //抓取導演
 31     getTextContent('div#info span:nth-child(1) span.attrs a','抓取導演');
 32 
 33 
 34 });
 35 
 36 //get the text content of tag
 37 function getTextContent(strRule, strMesg)
 38 {
 39 	//給evaluate傳入參數
 40 	var textinfo = casper.evaluate(function(rule) {
 41     	var valArr = '';
 42     $(rule).each(function(index,item){
 43     valArr = valArr + $(this).text() + ',';
 44       });
 45     return valArr.substring(0,valArr.length-1);
 46 	}, strRule);
 47     casper.echo(strMesg);
 48     require('utils').dump(textinfo.split(','));
 49     return textinfo.split(',');
 50 };
 51 
 52 //get the attribute content of tag
 53 function getAttrContent(strRule, strMesg, Attr)
 54 {
 55 	//給evaluate傳入參數
 56 	var textinfo = casper.evaluate(function(rule, attrname) {
 57     	var valArr = '';
 58     $(rule).each(function(index,item){
 59     valArr = valArr + $(this).attr(attrname) + ',';
 60       });
 61     return valArr.substring(0,valArr.length-1);
 62 	}, strRule, Attr);
 63     casper.echo(strMesg);
 64     require('utils').dump(textinfo.split(','));
 65     return textinfo.split(',');
 66 };
 67 
 68 casper.run();
GetDirectors

2. 抓取製片國家和地區,這個信息使用CSS selector抓取會有困難,緣由分析網頁後就能夠發現,首先這個信息不是放在一個<span>標籤裏面, 並且「美國」這個文本直接在<div id=’info’>這個高層級的元素裏。對於這樣的信息咱們採用另一種方式,文本分析和截取,首先映入String模塊var S = require("string"); 這個模塊也是要另外安裝的。而後抓取整塊的信息,而後用文本截取:

info

  1    //影片信息全文字抓取
  2     nameCount = casper.evaluate(function() {
  3     	var valArr = '';
  4     $('div#info').each(function(index,item){
  5     valArr = valArr + $(this).text() + ',';
  6       });
  7     return valArr.substring(0,valArr.length-1);
  8 	});
  9 	this.echo("影片信息全文字抓取");
 10     this.echo(nameCount);
 11     //this.echo(nameCount.indexOf("製片國家/地區:"));
 12 
 13     //抓取國家
 14     this.echo(S(nameCount).between("製片國家/地區:","\n"));
GetCountry

其餘信息能夠相似獲取。

第五步,將抓取到的信息存儲並做爲分析的源,推薦使用MongoDB這類NoSql數據庫存儲,比較適合存放這樣的非結構數據,並且性能更優。

相關文章
相關標籤/搜索