最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

利用Node.js制作爬取大眾點(diǎn)評(píng)的爬蟲

 更新時(shí)間:2016年09月22日 11:52:51   投稿:daisy  
相信每位用過大眾點(diǎn)評(píng)的人都知道,大眾點(diǎn)評(píng)上有很多美食餐館的信息,所以這篇文章給大家分享利用Node.js實(shí)現(xiàn)爬取大眾點(diǎn)評(píng)的爬蟲,正好可以拿來(lái)練練手Node.js。感興趣的可以參考借鑒。

前言

Node.js天生支持并發(fā),但是對(duì)于習(xí)慣了順序編程的人,一開始會(huì)對(duì)Node.js不適應(yīng),比如,變量作用域是函數(shù)塊式的(與C、Java不一樣);for循環(huán)體({})內(nèi)引用i的值實(shí)際上是循環(huán)結(jié)束之后的值,因而引起各種undefined的問題;嵌套函數(shù)時(shí),內(nèi)層函數(shù)的變量并不能及時(shí)傳導(dǎo)到外層(因?yàn)槭钱惒剑┑鹊取?/p>

一、 API分析

大眾點(diǎn)評(píng)開放了查詢餐館信息的API,這里給出了城市與cityid之間的對(duì)應(yīng)關(guān)系,

鏈接:http://m.api.dianping.com/searchshop.json?&regionid=0&start=0&categoryid=10&sortid=0&cityid=110

GET方式給出了餐館的信息(JSON格式)。

首先解釋下GET參數(shù)的含義:

     1、start為步進(jìn)數(shù),表示分步獲取信息的index,與nextStartIndex字段相對(duì)應(yīng);

     2、cityid表示城市id,比如,合肥對(duì)應(yīng)于110;

     3、regionid表示區(qū)域id,每一個(gè)id代表含義在start=0時(shí)rangeNavs字段中有解釋;

     4、categoryid表示搜索商家的分類id,比如,美食對(duì)應(yīng)的id為10,具體每一個(gè)id的含義參見在start=0時(shí)categoryNavs字段;

     5、sortid表示商家結(jié)果的排序方式,比如,0對(duì)應(yīng)智能排序,2對(duì)應(yīng)評(píng)價(jià)最好,具體每一個(gè)id的含義參見在start=0時(shí)sortNavs字段。

在GET返回的JSON串中l(wèi)ist字段為商家列表,id表示商家的id,作為商家的唯一標(biāo)識(shí)。在返回的JSON串中是沒有商家的口味、環(huán)境、服務(wù)的評(píng)分信息以及經(jīng)緯度的;

      因而我們還需要爬取兩個(gè)商家頁(yè)面:http://m.dianping.com/shop/<id>、http://m.dianping.com/shop/<id>/map。

通過以上分析,確定爬取策略如下(與dianping_crawler的思路相類似):

      1、逐步爬取searchshop API的取商家基本信息列表;

      2、通過爬取的所有商家的id,異步并發(fā)爬取評(píng)分信息、經(jīng)緯度;

      3、最后將三份數(shù)據(jù)通過id做聚合,輸出成json文件。

二、爬蟲實(shí)現(xiàn)

Node.js爬蟲代碼用到如下的第三方模塊:

      1、superagent,輕量級(jí)http請(qǐng)求庫(kù),模仿了瀏覽器登錄;

      2、cheerio,采用jQuery語(yǔ)法解析HTML元素,跟Python的PyQuery相類似;

      3、async,牛逼閃閃的異步流程控制庫(kù),Node.js的必學(xué)庫(kù)。

導(dǎo)入依賴庫(kù):

var util = require("util"); var superagent = require("superagent"); var cheerio = require("cheerio"); var async = require("async"); var fs = require('fs');

聲明全局變量,用于存放配置項(xiàng)及中間結(jié)果:

var cityOptions = { "cityId": 110, // 合肥 // 全部商區(qū), 蜀山區(qū), 廬陽(yáng)區(qū), 包河區(qū), 政務(wù)區(qū), 瑤海區(qū), 高新區(qū), 經(jīng)開區(qū), 濱湖新區(qū), 其他地區(qū), 肥西縣 "regionIds": [0, 356, 355, 357, 8840, 354, 8839, 8841, 8843, 358, -922], "categoryId": 10, // 美食 "sortId": 2, // 人氣最高 "threshHold": 5000 // 最多餐館數(shù) }; var idVisited = {}; // used to distinct shop var ratingDict = {}; // id -> ratings var posDict = {}; // id -> pos

判斷一個(gè)id是否在前面出現(xiàn)過,若object沒有該id,則為undefined(注意不是null):

function isVisited(id) { if (idVisited[id] != undefined) { return true; } else { idVisited[id] = true; return false; } }

采取回調(diào)函數(shù)的方式,實(shí)現(xiàn)順序逐步地遞歸調(diào)用爬蟲函數(shù):

function DianpingSpider(regionId, start, callback) { console.log('crawling region=', regionId, ', start =', start); var searchBase = 'http://m.api.dianping.com/searchshop.json?&regionid=%s&start=%s&categoryid=%s&sortid=%s&cityid=%s'; var url = util.format(searchBase, regionId, start, cityOptions.categoryId, cityOptions.sortId, cityOptions.cityId); superagent.get(url) .end(function (err, res) { if (err) return console.err(err.stack); var restaurants = []; var data = JSON.parse(res.text); var shops = data['list']; shops.forEach(function (shop) { var restaurant = {}; if (!isVisited(shop['id'])) { restaurant.id = shop['id']; restaurant.name = shop['name']; restaurant.branchName = shop['branchName']; var regex = /(.*?)(\d+)(.*)/g; if (shop['priceText'].match(regex)) { restaurant.price = parseInt(regex.exec(shop['priceText'])[2]); } else { restaurant.price = shop['priceText']; } restaurant.star = shop['shopPower'] / 10; restaurant.category = shop['categoryName']; restaurant.region = shop['regionName']; restaurants.push(restaurant); } }); var nextStart = data['nextStartIndex']; if (nextStart > start && nextStart < cityOptions.threshHold) { DianpingSpider(regionId, nextStart, function (err, restaurants2) { if (err) return callback(err); callback(null, restaurants.concat(restaurants2)) }); } else { callback(null, restaurants); } }); }

在調(diào)用爬蟲函數(shù)時(shí),采用asyncmapLimit函數(shù)實(shí)現(xiàn)對(duì)并發(fā)的控制;采用asyncuntil對(duì)并發(fā)的協(xié)同處理,保證三份數(shù)據(jù)結(jié)果的id一致性(不會(huì)因?yàn)椴l(fā)完成時(shí)間不一致而丟數(shù)據(jù)):

DianpingSpider(0, 0, function (err, restaurants) { if (err) return console.err(err.stack); var concurrency = 0; var crawlMove = function (id, callback) { var delay = parseInt((Math.random() * 30000000) % 1000, 10); concurrency++; console.log('current concurrency:', concurrency, ', now crawling id=', id, ', costs(ms):', delay); parseShop(id); parseMap(id); setTimeout(function () { concurrency--; callback(null, id); }, delay); }; async.mapLimit(restaurants, 5, function (restaurant, callback) { crawlMove(restaurant.id, callback) }, function (err, ids) { console.log('crawled ids:', ids); var resultArray = []; async.until( function () { return restaurants.length === Object.keys(ratingDict).length && restaurants.length === Object.keys(posDict).length }, function (callback) { setTimeout(function () { callback(null) }, 1000) }, function (err) { restaurants.forEach(function (restaurant) { var rating = ratingDict[restaurant.id]; var pos = posDict[restaurant.id]; var result = Object.assign(restaurant, rating, pos); resultArray.push(result); }); writeAsJson(resultArray); } ); }); });

其中,parseShopparseMap分別為解析商家詳情頁(yè)、商家地圖頁(yè):

function parseShop(id) { var shopBase = 'http://m.dianping.com/shop/%s'; var shopUrl = util.format(shopBase, id); superagent.get(shopUrl) .end(function (err, res) { if (err) return console.err(err.stack); console.log('crawling shop:', shopUrl); var restaurant = {}; var $ = cheerio.load(res.text); var desc = $("div.shopInfoPagelet > div.desc > span"); restaurant.taste = desc.eq(0).text().split(":")[1]; restaurant.surrounding = desc.eq(1).text().split(":")[1]; restaurant.service = desc.eq(2).text().split(":")[1]; ratingDict[id] = restaurant; }); } function parseMap(id) { var mapBase = 'http://m.dianping.com/shop/%s/map'; var mapUrl = util.format(mapBase, id); superagent.get(mapUrl) .end(function (err, res) { if (err) return console.err(err.stack); console.log('crawling map:', mapUrl); var restaurant = {}; var $ = cheerio.load(res.text); var data = $("body > script").text(); var latRegex = /(.*lat:)(\d+.\d+)(.*)/; var lngRegex = /(.*lng:)(\d+.\d+)(.*)/; if(data.match(latRegex) && data.match(lngRegex)) { restaurant.latitude = latRegex.exec(data)[2]; restaurant.longitude = lngRegex.exec(data)[2]; }else { restaurant.latitude = ''; restaurant.longitude = ''; } posDict[id] = restaurant; }); }

array的每一個(gè)商家信息,逐行寫入到j(luò)son文件中:

function writeAsJson(arr) { fs.writeFile( 'data.json', arr.map(function (data) { return JSON.stringify(data); }).join('\n'), function (err) { if (err) return err.stack; }) }

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容,希望本文能給學(xué)習(xí)或者使用node.js的朋友們帶來(lái)一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

  • 詳解node單線程實(shí)現(xiàn)高并發(fā)原理與node異步I/O

    詳解node單線程實(shí)現(xiàn)高并發(fā)原理與node異步I/O

    本篇文章主要介紹了node單線程實(shí)現(xiàn)高并發(fā)原理與node異步I/O ,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-09-09
  • 基于Node.js構(gòu)建一個(gè)靈活的CLI命令行工具

    基于Node.js構(gòu)建一個(gè)靈活的CLI命令行工具

    在軟件開發(fā)中,命令行界面(CLI)工具是必不可少的助手,本文主要介紹了如何使用Node.js構(gòu)建一個(gè)靈活的CLI工具,涵蓋從基礎(chǔ)命令處理到復(fù)雜的交互式問答和遠(yuǎn)程模板下載,需要的可以參考下
    2024-03-03
  • npm install常見錯(cuò)誤類型及對(duì)應(yīng)的解決方案

    npm install常見錯(cuò)誤類型及對(duì)應(yīng)的解決方案

    在前端開發(fā)中,npm是最常用的包管理工具,通過 npm install 命令,開發(fā)者可以輕松地安裝項(xiàng)目所需的依賴包,然而,在實(shí)際使用過程中,npm install 可能會(huì)因?yàn)楦鞣N原因而報(bào)錯(cuò),本文將詳細(xì)介紹一些常見的 npm install 錯(cuò)誤類型、發(fā)生原因及其對(duì)應(yīng)的解決方案,需要的朋友可以參考下
    2025-03-03
  • express中創(chuàng)建 websocket 接口及問題解答

    express中創(chuàng)建 websocket 接口及問題解答

    本文主要介紹了express中創(chuàng)建 websocket 接口及問題解答,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 干凈卸載Windows的Node.js環(huán)境的方法

    干凈卸載Windows的Node.js環(huán)境的方法

    這篇文章主要介紹了如何干凈卸載Windows的Node.js環(huán)境的方法,文中通過圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家刪除Node.js環(huán)境有一定的幫助,需要的朋友可以參考下
    2025-01-01
  • node中koa中間件機(jī)制詳解

    node中koa中間件機(jī)制詳解

    本篇文章主要主要介紹了node中koa中間件機(jī)制詳解,詳細(xì)的介紹了koa和兼容問題,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • 用node和express連接mysql實(shí)現(xiàn)登錄注冊(cè)的實(shí)現(xiàn)代碼

    用node和express連接mysql實(shí)現(xiàn)登錄注冊(cè)的實(shí)現(xiàn)代碼

    本篇文章主要介紹了用node和express連接mysql實(shí)現(xiàn)登錄注冊(cè)的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-07-07
  • nodejs redis 發(fā)布訂閱機(jī)制封裝實(shí)現(xiàn)方法及實(shí)例代碼

    nodejs redis 發(fā)布訂閱機(jī)制封裝實(shí)現(xiàn)方法及實(shí)例代碼

    這篇文章主要介紹了nodejs redis 發(fā)布訂閱機(jī)制封裝的相關(guān)資料,這里提供了實(shí)現(xiàn)方法,及實(shí)例代碼,具有參考價(jià)值,需要的朋友可以參考下
    2016-12-12
  • 基于nodejs實(shí)現(xiàn)微信支付功能

    基于nodejs實(shí)現(xiàn)微信支付功能

    這篇文章主要為大家詳細(xì)介紹了基于nodejs實(shí)現(xiàn)微信支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • node.js學(xué)習(xí)之事件模塊Events的使用示例

    node.js學(xué)習(xí)之事件模塊Events的使用示例

    Nodejs中不存在瀏覽器中冒泡,捕獲這些行為,Nodejs中實(shí)現(xiàn)了events這個(gè)模塊,Nodejs中大多數(shù)模塊都集成了這個(gè)模塊,所以events是Nodejs中最重要的一個(gè)模塊。這篇文章主要給大家介紹了關(guān)于node.js學(xué)習(xí)教程之事件模塊Events的相關(guān)資料,需要的朋友可以參考下。
    2017-09-09

最新評(píng)論

定结县| 上虞市| 三亚市| 富平县| 石狮市| 夏河县| 上犹县| 铁岭市| 崇信县| 黄龙县| 会宁县| 赣州市| 卢湾区| 巴马| 莱芜市| 高雄市| 壶关县| 正镶白旗| 讷河市| 乌兰察布市| 泰安市| 巴彦淖尔市| 教育| 囊谦县| 岐山县| 凤阳县| 隆回县| 稷山县| 赤峰市| 万宁市| 延川县| 广宁县| 观塘区| 三都| 德昌县| 棋牌| 崇礼县| 巧家县| 吉林市| 永和县| 繁峙县|