zoukankan      html  css  js  c++  java
  • node爬虫(转)

    我们先来看看今天的目标: mmjpg.com的美腿频道下的图片

    一、实现步骤

    使用superagent库来获取页面
    分析页面结构,使用cheerio 获取有效信息
    保存图片到本地
    开撸
    不断优化

    这儿我们用到了superagent cheerio fs-extra这三个库

    $ npm i superagent cheerio fs-extra --save
    

    1.superagent 是nodejs里一个非常方便的客户端请求代理模块
    2.cheerio:为服务器特别定制的,快速、灵活、实施的jQuery核心实现
    3.fs-extra: 丰富了fs模块,同时支持async/await

    二.请求URL获取HTML

    app.js 使用superagent发起请求并打印出页面内容

    const request = require('superagent')
    const cheerio = require('cheerio')
    const fs = require('fs-extra')
    
    let url = 'http://www.mmjpg.com/tag/meitui/'
    
    request.get(url + '1').then(function (res) {
        console.log(res.text)
    })
    // 你就可以看见HTML内容打印到了控制台
    

    三.分析页面结构

    使用cheerio来操作了,cheerio语法和jQuery基本一样。

    找到class为pic那个div下的列表中,现在我们就可以使用cheerio来获取

    async function getUrl() {
        const res = await request.get(url + 1)
        const $ = cheerio.load(res.text)
        $('.pic li').each(function(i, elem) {
            const href = $(this).find('a').attr('href')
            const title = $(this).find('.title').text()
            console.log(title, href)
        })
    }
    
    getUrl()
    
    /*
    $ node app.js
    长腿美女白一晗巨胸丝袜写真精美图片 http://www.mmjpg.com/mm/1255
    大美女尹菲开档网袜写真令人眼花缭乱 http://www.mmjpg.com/mm/1230
    宅男女神丰满诱人的胴体令人想入非非 http://www.mmjpg.com/mm/1164
    性感美女浴室写真高耸的酥胸诱惑十足 http://www.mmjpg.com/mm/1162
    长相清纯甜美的97年妹子苗条美腿图片 http://www.mmjpg.com/mm/1157
    丽质美女柔美修长美腿带给你曼妙感受 http://www.mmjpg.com/mm/1153
    容貌似杨幂的美女馨怡美腿极致诱惑图 http://www.mmjpg.com/mm/1148
    丝袜美腿诱惑!甜美女神杨晨晨私房套图 http://www.mmjpg.com/mm/1130
    性感美女刘钰儿透视内衣私密照真撩人 http://www.mmjpg.com/mm/1127
    肤白貌美的模特李晨晨十分惹人怜爱 http://www.mmjpg.com/mm/1126
    萌妹林美惠子穿黑丝浴室私房写真福利 http://www.mmjpg.com/mm/1124
    美女赵小米修长双腿丝袜写真能玩几年 http://www.mmjpg.com/mm/1111
    微博高颜值美胸萌妹子少女映画写真集 http://www.mmjpg.com/mm/1107
    性感妹子修长的双腿真是让人爱不释目 http://www.mmjpg.com/mm/1106
    翘臀妹子绮里嘉饱满胸部美的夺人心目 http://www.mmjpg.com/mm/1103
    */
    

    四.分析URL地址

    在很多时候我们都需要分析URL,就像点击不同的页码观察URL变化 http://www.mmjpg.com/home/2,我们可以很容易发现页码对应为URL最后的数字。查看mmjpg.com的美腿频道我们可以发现它一共有10页内容,我们就不写代码判断页数了直接写死为10。当然了这儿你可以自己实现动态判断总页数,就当是留的小练习吧。

    async function getUrl() {
        let linkArr = []
        for (let i = 1; i <= 30; i++) {
            const res = await request.get(url + i)
            const $ = cheerio.load(res.text)
            $('.pic li').each(function(i, elem) {
                let link = $(this).find('a').attr('href')
                linkArr.push(link)
            })
        }
        console.log(linkArr);
        return linkArr;
    }
    

      

    五.获取图片地址

    async function getPic(url) {
      const res = await request.get(url)
      const $ = cheerio.load(res.text)
      // 以图集名称来分目录
      const dir = $('.article h2').text()
      console.log(`创建${title}文件夹`)
      await fs.mkdir(path.join(__dirname, '/mm', title))
      const pageCount = parseInt($('#page .ch.all').prev().text())
      for (let i = 1; i <= pageCount; i++) {
        let pageUrl = url + '/' + i
        const data = await request.get(pageUrl)
        const _$ = cheerio.load(data.text)
        // 获取图片的真实地址
        const imgUrl = _$('#content img').attr('src')
        download(dir, imgUrl) // TODO
      }
    }
    

    六.保存图片到本地

    现在我们就来实现下载保存图片的方法,这儿我们使用了stream(流) 来保存图片
    
    function download(dir, imgUrl) {
      console.log(`正在下载${imgUrl}`)
      const filename = imgUrl.split('/').pop()  
      const req = request.get(imgUrl)
        .set({ 'Referer': 'http://www.mmjpg.com' }) // mmjpg.com根据Referer来限制访问
      req.pipe(fs.createWriteStream(path.join(__dirname, 'mm', dir, filename)))
    }
    ok,现在我们就来把之前写的各个功能的函数连起来
    
    async function init(){
      let urls = await getUrl()
      for (let url of urls) {
        await getPic(url)
      }
    }
    
    init()
    

     

    代码:

    const request = require('superagent')
    const cheerio = require('cheerio')
    const fs = require('fs-extra')
    const path = require('path')
    
    let url = "http://www.u-cpc.com/product/list.html";
    
    // http://www.u-cpc.com/Product/List.html?page=3&cid=0&bid=0
    
    // request.get(url).then(function(res){
    //   console.log(res.text);
    // })
    
    // async function getUrl(){
    //     const res = await request.get(url);
    //     const $ = cheerio.load(res.text);
    //     $(".product_ul li").each(function(i, elem){
    //         const href = $(this).find("img").attr("data-original")
    //         const title = $(this).find(".productlist_title").text();
    //         console.log("标题是: " + title)
    //         console.log("地址是: " + href)
    //     })
    // }
    // getUrl();
    
    /**
     * 获取图集的URL
     */
    async function getUrl(){
        let linkArr = [];
        for(let i = 0; i <= 10; i++){
          const res = await request.get(url);
          const $ = cheerio.load(res.text);
    
          $(".product_ul li").each(function(i, elem){
              const href = $(this).find("img").attr("data-original")
              const title = $(this).find(".productlist_title").text();
              linkArr.push(href)
          })
        }
    }
    async function getPic(url) {
      const res = await request.get(url)
      const $ = cheerio.load(res.text)
      // 以图集名称来分目录  
      const dir = $('.curr').text()
      console.log(`创建${dir}文件夹`)
      await fs.mkdir(path.join(__dirname, '/images', dir))
      for(let i = 1; i <= 10; i++){
        let pageUrl = url + '?page=' + i + '&cid=0&bid=0';
        const data = await request.get(pageUrl)
        const _$ = cheerio.load(data.text)
        _$(".product_ul li").each(function(i, elem){
          const imgUrl = $(this).find("img").attr("data-original");
          download(dir, imgUrl)
        })
      }
    }
    
    // 下载图片
    function download(dir, imgUrl) {
      console.log(`正在下载${imgUrl}`)
      const filename = imgUrl.split('/').pop()
      const req = request.get(imgUrl)
          .set({
              'Referer': 'http://www.u-cpc.com/product/list.html'
          })
      req.pipe(fs.createWriteStream(path.join(__dirname, 'images', dir, filename)))
    }
    
    async function init() {
      getUrl();
      getPic(url)
    }
    // init();
    download();
    function download() {
      const url2 = "https://codeload.github.com/cheeriojs/cheerio/zip/master"
      const filename = url2.split('/').pop() + ".zip"
      const req = request.get(url2)
      req.pipe(fs.createWriteStream(path.join(__dirname, 'images', filename)))
    }

     七: 解决爬取gb2312网页出现乱码

    使用superagent爬取网站内容,当网页编码不是utf-8编码时,中文就会返回乱码,原因是superagent只支持utf-8的网页编码,我们可以使用其扩展的一个npm模块superagent-charset

    $ npm i superagent-charset
    

    使用.charset(encoding)方法,就可以指定编码,详细如下:

    const charset = require('superagent-charset');
    const request = charset(require('superagent'));
    superagent
        .get(url) // 获取网页内容
        .charset('gb2312') // 转码-将gb2312格式转成utf-8
        .then(function(err, res){ 
        });
    

    // 爬公司的器材, 生成json文件

    const request = require('superagent')
    const cheerio = require('cheerio')
    const fs = require('fs-extra')
    const path = require('path')
    
    let url = "http://www.u-cpc.com/product/list.html";
    
    var toptens = [];  // 初始化json数组
    
    /**
     * 获取图集的URL
     */
    
    async function getUrl(){
        let linkArr = [];
        for(let i = 1; i <= 10; i++){
          const res = await request.get(url);
          const $ = cheerio.load(res.text);
          let pageUrl = url + '?page=' + i + '&cid=0&bid=0';
          linkArr.push(pageUrl);
        }
        return linkArr;
    }
    async function getPic(newUrl) {
      const topten = { // 设定爬取的json数组
        info: []
      };
      const res = await request.get(newUrl)
      const $ = cheerio.load(res.text)
      // 以图集名称来分目录  
      const dir = $('.curr').text()
      console.log(`创建${dir}文件夹`)
      await fs.mkdir(path.join(__dirname, '/images', dir))
      $(".product_ul li").each(function(i, elem){
        i += 1;
        const imgUrl = $(this).find("img").attr("data-original");
        const imgText = $(this).find(".productlist_title").text();
        topten.info.push({
          number: i,
          title: imgText,
          images: imgUrl
        })
        download(dir, imgUrl);
      });
    
      toptens.push({topten: topten}); // 从原有的json数据之前追加json数据
      const json = JSON.stringify(toptens); // json格式解析,这步也是一定要有
      downloadJson(json);
    }
    
    // 创建json文件
    function downloadJson(json){
      const filename = "toptens.json";
      const file = path.join(__dirname, filename)
      fs.outputFile(file, json, function(err) {
        //file has now been created, including the directory it is to be placed in 
      });
    }
    
    // 下载图片
    function download(dir, imgUrl) {
      console.log(`正在下载${imgUrl}`)
      const filename = imgUrl.split('/').pop()
      const req = request.get(imgUrl)
          .set({
              'Referer': 'http://www.u-cpc.com/product/list.html'
          })
      req.pipe(fs.createWriteStream(path.join(__dirname, 'images', dir, filename)))
    }
    
    async function init() {
      const getUrls = await getUrl();
      for(let i = 0; i < getUrls.length; i++){
        await getPic(getUrls[i])
      }
    }
    init();
    

    八:注意并发的,毕竟爬取频率会有限制

    问: err:Error: connect ETIMEDOUT 博主做爬虫的时候有没有遇到报这个错的,我按你的代码写爬我们的网站,好多页面都报这个错误

    答: 并发爬取的数量太高 或者爬的太快。你爬的网站有做爬虫保护限制。把并发量调低 把 延迟调大 就可以了。


    PHP, Python, Node.js 哪个比较适合写爬虫?
    1.PHP对多线程、异步支持较差,不建议采用。PHP 4.3.0以后引入的PHP Command Line特性,实现了让PHP脱离Web服务器,和Phthon一样独立运行。因此用来写爬虫也是非常方便的。
    2.NodeJS:对一些垂直网站爬取倒可以,但由于分布式爬取、消息通讯等支持较弱,根据自己情况判断。
    3.Python:强烈建议,对以上问题都有较好支持。尤其是Scrapy框架值得作为第一选择, 专门用来做爬虫的一个框架。优点诸多:支持xpath;基于twisted,性能不错;有较好的调试工具;
    4.C、C++虽然性能不错,但不推荐,尤其是考虑到成本等诸多因素;

     

    参考: https://github.com/ogilhinn/mm-spider

  • 相关阅读:
    C++STL——vector
    大数常用计算模板及例题
    在线算法&离线算法
    线段树——hdu1166敌兵布阵
    C++STL——堆栈
    C++STL——优先队列
    C++STL——队列
    图的建立——图的两种存储结构
    Manacher算法——最长回文子串
    HttpClient的几个实现类
  • 原文地址:https://www.cnblogs.com/alantao/p/8446613.html
Copyright © 2011-2022 走看看