都2023年了还不会Node.js爬虫?快学起来!

爬虫简介

什么是爬虫

爬虫(Web Crawler)是一种自动化程序,可以在互联网上自动抓取网页,并从中提取有用的信息。

爬虫可以模拟人类浏览器的行为,自动访问网站、解析网页、提取数据等。

通俗来说,爬虫就像是一只蜘蛛,它会沿着网页上的链接不断爬行,把整个网站的内容都爬取下来。

这样,我们就可以从大量的网页中获取到我们需要的数据。

爬虫的应用场景

爬虫在很多领域都有广泛的应用,比如:

  • 数据采集:爬虫可以自动抓取各种网站上的数据,比如商品信息、新闻、股票数据等等。
  • SEO优化:通过爬虫可以获取到竞争对手网站的信息,从而进行SEO优化。
  • 网络安全:爬虫可以扫描网站漏洞、检测恶意软件等等。
  • 数据分析:通过爬虫获取到的数据可以进行各种分析,比如情感分析、舆情监测等等。
  • 总之,爬虫是一种非常强大的工具,可以帮助我们更好地利用互联网上的信息资源。

爬虫的优缺点

优点:

  1. 自动化:爬虫可以自动化地从互联网上抓取数据,大大节省了人力成本和时间成本。
  2. 数据量大:爬虫可以从大量的网页中抓取数据,可以获取到丰富的信息,这对于一些数据分析和挖掘工作非常有用。
  3. 实时性:爬虫可以实时地抓取数据,可以获取到最新的信息,这对于一些需要实时监控的应用非常有用。
  4. 可扩展性:爬虫可以很容易地扩展到其他网站,只需要修改一些配置参数即可。
  5. 隐私性:爬虫可以在不需要登录的情况下抓取数据,保护了用户的隐私。

缺点:

  1. 法律风险:爬虫可能会侵犯他人的版权、隐私等权益,如果不合法使用可能会面临法律风险。
  2. 网站负担:爬虫可能会对网站的服务器造成一定的负担,如果抓取频率过高可能会导致网站崩溃。
  3. 数据质量:由于网页的结构和格式不一定统一,爬虫可能会抓取到一些不规范、不准确或者重复的数据。
  4. 反爬虫机制:为了保护自己的权益,一些网站可能会采取反爬虫机制,比如验证码、IP封锁等等,这会给爬虫带来一定的困难。
  5. 维护成本:由于网页结构和格式的变化,爬虫需要不断地进行维护和更新,这会增加一定的开发成本和维护成本。

为什么要使用Node.js来编写爬虫

Node.js是一种基于Chrome V8引擎的JavaScript运行环境,它可以在服务器端运行JavaScript代码。

相比于其他编程语言,Node.js具有以下优点:

  • 与前端技术栈相似:Node.js是基于JavaScript的运行时环境,而JavaScript是前端开发中最常用的编程语言之一。因此如果你是一名前端工程师,使用Node.js编写爬虫可以让你更快地上手,不需要学习新的编程语言。
  • 易用性:JavaScript是一种非常流行的编程语言,很多人都已经掌握了它的基础知识。
  • 生态系统:Node.js有非常丰富的第三方模块和工具,可以帮助我们更快速地开发爬虫。比如,Cheerio可以帮助我们更方便地进行HTML解析,Request可以帮助我们更方便地进行网络请求等等。
  • 高效性:Node.js使用事件驱动、非阻塞I/O模型,可以处理大量并发请求。这对于爬虫来说非常重要,因为爬虫需要频繁地进行网络请求和数据解析。
  • 跨平台:Node.js可以在多个平台上运行,包括Windows、Mac、Linux等等。这意味着,你可以在你喜欢的操作系统上编写爬虫,并且不需要担心兼容性问题。

因此,使用Node.js来编写爬虫是非常合适的选择。

爬虫的基本流程

爬虫的基本流程通常包括以下几个步骤:

  • 发送网络请求:爬虫首先需要发送网络请求,获取网页的HTML代码。Node.js有很多第三方模块可以帮助我们进行网络请求,比如Request、Axios等等。这些模块可以帮助我们发送HTTP请求,并且可以设置请求头、请求参数等等。
  • 解析HTML页面:爬虫需要解析HTML页面,提取出有用的信息,比如标题、正文、链接等等。在Node.js中,我们可以使用Cheerio模块来解析HTML页面。Cheerio是一个类似于jQuery的库,可以帮助我们更方便地进行HTML解析和DOM操作。
  • 存储数据:爬虫需要把提取出来的数据存储到数据库或者文件中,以便后续的使用。Node.js有很多第三方模块可以帮助我们进行数据存储,比如MongoDB、MySQL、Redis等等。
  • 遍历链接:如果需要爬取整个网站,爬虫需要遍历网页中的链接,不断地进行网络请求和HTML解析。

OK经过上面的简单介绍,现在大家大概已经知道了爬虫是什么

但是纸上得来终觉浅,接下来我会用多个爬虫实例带你完全入门Node爬虫

Node爬虫多案例实战

百度首页

我们先来从百度下手,啊不是,是从百度开始

我们来写一个简单的爬虫 demo,它可以爬取百度首页的标题和链接:

// 爬取百度首页的标题和链接
const http = require('http');
const cheerio = require('cheerio');

const url = 'http://www.baidu.com';

// 使用 Node.js 的 http 模块来发起 GET 请求
http.get(url, (res) => {
  let html = '';

  res.on('data', (chunk) => {
    html += chunk;
  });

  res.on('end', () => {
    // 使用 cheerio 模块来解析 HTML,将 HTML 转换成一个类似于 jQuery 的对象 $
    const $ = cheerio.load(html);
    const links = [];

    // 使用 $ 来查找页面中所有的链接
    $("a").each((i, el) => {
      const title = $(el).text();
      const href = $(el).attr("href");
      links.push({ title, href });
    });

    // 最后输出到控制台
    console.log(links);
  });
});

上面这段代码使用了 Node.js 的 http 模块来发起 GET 请求,并使用 cheerio 模块来解析 HTML

它首先获取百度首页的 HTML,然后使用 cheerio 将 HTML 转换成一个类似于 jQuery 的对象 $

接着使用 $ 来查找页面中所有的链接,并将它们的标题和链接保存到一个数组中

最后将这个数组输出到控制台上,我们来看看控制台会输出什么:

在这里插入图片描述

百度热搜

接着我们来试试看爬一下百度热搜,我们要拿到中间这部分的一个热搜列表,我们来看一下对应的网页结构,我们想拿到每一条热搜的标题以及后面的热度指数:

在这里插入图片描述

OK,知道了标题和热度的HTML结构,拿到这个元素就很简单了,接下来我们来直接实现爬虫的逻辑:

const https = require('https');
const cheerio = require('cheerio');
const fs = require('fs')

const url = 'https://top.baidu.com/board?tab=realtime';

https.get(url,{ rejectUnauthorized: false }, (res) => {
  let html = '';

  res.on('data', (chunk) => {
    html += chunk;
  });

  res.on('end', () => {
    const $ = cheerio.load(html);
    const news = [];

    $('.container-bg_lQ801 div:nth-child(2) .category-wrap_iQLoo.horizontal_1eKyQ').each(function () {
        const title = $('.c-single-text-ellipsis', this).text().trim();
        const hot = '热度指数:' + $('.hot-index_1Bl1a', this).text().trim();
        news.push({ title, hot });
      });

    // console.log(news);
    //将数据写入文件中
    fs.writeFile("./baidu1.json", JSON.stringify(news), function (err, data) {
      if (err) {
        throw err;
      }
      console.log("文件保存成功");
    });
  });
});

这段代码我做了两个地方的改进:

  1. 将http模块换成了https模块,因为百度热搜页面用的是https协议
  2. 我们不直接将news打印在控制台,而是使用了fs模块将爬取到的数据写入JSON文件中

我们来看看生成的JSON文件:

[
  { "title": "重要主场外交拉开帷幕", "hot": "热度指数:4966106" },
  { "title": "杜苏芮登陆 猛烈暴雨将席卷南北多省", "hot": "热度指数:4918080" },
  { "title": "外交部网站更新王毅部长致辞", "hot": "热度指数:4836982" },
  { "title": "相聚盛会 成就梦想", "hot": "热度指数:4796643" },
  { "title": "台风致泉州39人受轻伤 超50万户停电", "hot": "热度指数:4674698" },
  { "title": "普里戈任自叛乱后首次在俄露面", "hot": "热度指数:4592932" },
  { "title": "东方甄选直播间被关原因曝光", "hot": "热度指数:4456629" },
  { "title": "媒体:要求孩子10小时不哭闹不妥", "hot": "热度指数:4301611" },
  { "title": "#杨紫琼与相恋19年男友结婚#", "hot": "热度指数:4247090" },
  { "title": "体育馆受损 泉州五月天演唱会咋办", "hot": "热度指数:4119379" },
  { "title": "微信已把帐号改为账号", "hot": "热度指数:4031742" },
  { "title": "厦门暴雨似飞瀑倾泄而下", "hot": "热度指数:3926578" },
  { "title": "双台风来了!第6号台风卡努生成", "hot": "热度指数:3872443" },
  { "title": "广东省防风应急响应调整为Ⅳ级", "hot": "热度指数:3727314" },
  { "title": "出纳挪用2500多万理财7年获利6400", "hot": "热度指数:3637531" },
  { "title": "敦煌景区骆驼“罢工”?驼户回应", "hot": "热度指数:3546268" },
  { "title": "黄晓明这回真听劝了", "hot": "热度指数:3496660" },
  { "title": "整条街的胶带都因台风卖没了", "hot": "热度指数:3363853" },
  { "title": "汉庭如家,房价直逼香格里拉", "hot": "热度指数:3214298" },
  { "title": "这一次,苏州反超深沪", "hot": "热度指数:3177049" },
  { "title": "台风来临瞬间:晋江鱼排剧烈抖动", "hot": "热度指数:3005969" },
  { "title": "林嘉欣发文宣布离婚", "hot": "热度指数:2966910" },
  { "title": "李玟丈夫:不会参与财产分配", "hot": "热度指数:2807755" },
  { "title": "薇娅夫妇公司被起诉侵权", "hot": "热度指数:2738958" },
  { "title": "杨洋:谢谢大家的包容陪伴批评建议", "hot": "热度指数:2650834" },
  { "title": "3000多名官兵紧急出动支援厦门", "hot": "热度指数:2520075" },
  { "title": "杜苏芮最大风力已提升至17级", "hot": "热度指数:2423179" },
  { "title": "海员记录在杜苏芮影响下航行遭遇", "hot": "热度指数:2314662" },
  { "title": "云南一医院设置野生毒菌展示柜", "hot": "热度指数:2294344" },
  { "title": "车队未经批准穿越保护区 3死1失踪", "hot": "热度指数:2134253" },
  { "title": "世界跆拳道大赛 中国队跳“僵尸舞”", "hot": "热度指数:2078125" }
]

豆瓣TOP250

既然提到了爬虫,怎么能少得了豆瓣呢!豆瓣:烦不烦,每次都爬我!
和之前一样,分析一下我们想爬的网页结构,提取列表中的我们想要的信息(标题、评分、图片链接):

在这里插入图片描述

代码实现:

const https = require('https')
const cheerio = require('cheerio')
const fs = require('fs')

//获取页面的html结构
https.get('https://movie.douban.com/top250', function (res) {
    let html = ''
    res.on('data', function (chunk) {
        //得到数据流,通过字符串拼接得到html结构
        html += chunk
    })

    res.on('end', function () {
        // 获取html中的数据
        const $ = cheerio.load(html)
        let movies = []
        $('li .item').each(function () {
            const title = $('.title', this).text()
            const star = $('.info .bd .rating_num', this).text()
            const pic = $('.pic img', this).attr('src')
            //数据以对象的形式存放在数组中
            movies.push({
                title: title,
                star: star,
                pic: pic
            })
        })
        //将数据写入文件中
        fs.writeFile('./douban.json', JSON.stringify(movies), function (err, data) {
            if (err) {
                throw err
            }
            console.log('文件保存成功');
        })
    })
})

看一下我们生成的JSON文件,是不是和我们期望爬取的数据一样:

[
  {
    "title": "肖申克的救赎 / The Shawshank Redemption",
    "star": "9.7",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p480747492.jpg"
  },
  {
    "title": "霸王别姬",
    "star": "9.6",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2561716440.jpg"
  },
  {
    "title": "阿甘正传 / Forrest Gump",
    "star": "9.5",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2372307693.jpg"
  },
  {
    "title": "泰坦尼克号 / Titanic",
    "star": "9.5",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2889314814.jpg"
  },
  {
    "title": "这个杀手不太冷 / Léon",
    "star": "9.4",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p511118051.jpg"
  },
  {
    "title": "千与千寻 / 千と千尋の神隠し",
    "star": "9.4",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2557573348.jpg"
  },
  {
    "title": "美丽人生 / La vita è bella",
    "star": "9.6",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2578474613.jpg"
  },
  {
    "title": "辛德勒的名单 / Schindler's List",
    "star": "9.6",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p492406163.jpg"
  },
  {
    "title": "星际穿越 / Interstellar",
    "star": "9.4",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2614988097.jpg"
  },
  {
    "title": "盗梦空间 / Inception",
    "star": "9.4",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p513344864.jpg"
  },
  {
    "title": "楚门的世界 / The Truman Show",
    "star": "9.4",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p479682972.jpg"
  },
  {
    "title": "忠犬八公的故事 / Hachi: A Dog's Tale",
    "star": "9.4",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2587099240.jpg"
  },
  {
    "title": "海上钢琴师 / La leggenda del pianista sull'oceano",
    "star": "9.3",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2574551676.jpg"
  },
  {
    "title": "三傻大闹宝莱坞 / 3 Idiots",
    "star": "9.2",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p579729551.jpg"
  },
  {
    "title": "放牛班的春天 / Les choristes",
    "star": "9.3",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2884280708.jpg"
  },
  {
    "title": "机器人总动员 / WALL·E",
    "star": "9.3",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p1461851991.jpg"
  },
  {
    "title": "无间道 / 無間道",
    "star": "9.3",
    "pic": "https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2564556863.jpg"
  },
  {
    "title": "疯狂动物城 / Zootopia",
    "star": "9.2",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2614500649.jpg"
  },
  {
    "title": "控方证人 / Witness for the Prosecution",
    "star": "9.6",
    "pic": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p1505392928.jpg"
  },
  {
    "title": "大话西游之大圣娶亲 / 西遊記大結局之仙履奇緣",
    "star": "9.2",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2455050536.jpg"
  },
  {
    "title": "熔炉 / 도가니",
    "star": "9.4",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p1363250216.jpg"
  },
  {
    "title": "教父 / The Godfather",
    "star": "9.3",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p616779645.jpg"
  },
  {
    "title": "触不可及 / Intouchables",
    "star": "9.3",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p1454261925.jpg"
  },
  {
    "title": "当幸福来敲门 / The Pursuit of Happyness",
    "star": "9.2",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2614359276.jpg"
  },
  {
    "title": "龙猫 / となりのトトロ",
    "star": "9.2",
    "pic": "https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2540924496.jpg"
  }
]

CSDN首页

我们最后来爬一个CSDN首页的文章列表

在这里插入图片描述

我们这次想拿到列表里每篇文章的标题、简介和作者

在这里插入图片描述

来看实现代码:

const https = require('https');
const cheerio = require('cheerio');
const iconv = require('iconv-lite');
const fs = require('fs')

const url = 'https://blog.csdn.net/nav/web';

https.get(url, { rejectUnauthorized: false }, (res) => {
  let html = '';

  res.on('data', (chunk) => {
    html += iconv.decode(chunk, 'utf-8');
  });

  res.on('end', () => { 
    const $ = cheerio.load(html);
    const courses = [];

    $('.Community .active-blog').each(function () {
      const title = $('.blog-text', this).text().trim();
      const desc = $('.desc', this).text().trim();
      const author = $('.operation-c a span', this).text().trim();
      courses.push({ title, desc, author });
    });

    // console.log(courses);
    //将数据写入文件中
    fs.writeFile('./csdn.json', JSON.stringify(courses), function (err, data) {
      if (err) {
        throw err
      }
      console.log('文件保存成功');
    })
  });
});

最后生成的JSON文件如下,文章的标题、简介、作者都成功拿到了:

[
  {
    "title": "“前端已死”",
    "desc": "我再说一遍,希望大家不要嫌啰嗦,使用工具的能力,并不能作为核心竞争力,因为现在学习资料很丰富,社区很活跃,什么问题都可以找到解决方案,你能做到的别人也能做到,没有任何优势,不属于竞争力。甚至是工作以外的特长都可以,我是钓鱼大佬,我是跑步达人,我是综艺专家,我是健身狂人,都可以,因为一个人能坚持自己的爱好并做到出众,也是不简单的。人是趋利性的动物,就算你眼光独到,命运垂怜,抢得先机,但数年之后呢?没有任何吸引人的信息,给人感觉,就是个普通的前端从业人员,领导安排个需求,然后接受,排期,完成开发,上线,这种。",
    "author": "作者:夜栩"
  },
  {
    "title": "html网页设计小作业(个人主页)",
    "desc": "简易的个人网页小作业,只用了html+css 布局制作,没啥好说的,直接上图!!!",
    "author": "作者:Space-oddity-fang"
  },
  {
    "title": "别找了!前端那些好用的网站都在这里了!【文末送书】",
    "desc": "🍀一、渐变神器Gradient🍀二、两款阴影工具在线网站🍀三、网站UI配色宝藏网站UI Design Daily🍀四、Small Dev tools实用工具合集🍀五、Glass Morphism在线制作 CSS 玻璃风格神器🍀六、Keyframes 在线动画、阴影和颜色🍀七、BGJar 在线SVG 背景",
    "author": "作者:在下周周ovo"
  },
  {
    "title": "VSCode安装配置使用教程(最新版超详细保姆级含插件)一文就够了",
    "desc": "Visual Studio Code 是一个轻量级功能强大的源代码编辑器,支持语法高亮、代码自动补全(又称 IntelliSense)、代码重构、查看定义功能,并且内置了命令行工具和 Git 版本控制系统。适用于 Windows、macOS 和 Linux。它内置了对 JavaScript、TypeScript 和 Node.js 的支持,并为其他语言和运行时(如 C++、C#、Java、Python、PHP、Go、.NET)提供了丰富的扩展生态系统。",
    "author": "作者:神兽汤姆猫"
  },
  {
    "title": "【实战】React 必会第三方插件 —— Cron 表达式生成器(qnn-react-cron)",
    "desc": "qnn-react-cron 可以看做 react-cron-antd 的升级版(具体“渊源”可见文档),现有功能如下:- 🎉 全面支持 cron:秒、分、时、日、月、周、年- 🎉 日及周条件互斥,自动改变响应值- 🎉 支持反解析 cron 表达式到 UI- 🎉 可结合此组件与 Antd 的下拉及输入组件封装成下拉输入框- 🎉 国际化支持- 🎉 TypeScript 支持",
    "author": "作者:程序边界"
  },
  {
    "title": "40个web前端实战项目,练完即可就业,从入门到进阶,基础到框架,html_css【附视频+源码】",
    "desc": "40个web前端实战项目,练完即可就业,从入门到进阶,基础到框架,html_css【附视频+源码】",
    "author": "作者:兔子的编程日记"
  },
  {
    "title": "两小时快速入门 TypeScript 基础(一)工作流、基本类型、高级类型",
    "desc": "本文介绍了TypeScript的工作流、基本类型和高级类型,并对一些类型做了举例说明,最后对本文章进行了总结...",
    "author": "作者:前端杂货铺"
  },
  {
    "title": "【前端|CSS系列第3篇】CSS盒模型、浮动及定位",
    "desc": "CSS盒模型是用来描述HTML元素在页面中所占空间的模型。每个元素都被看作是一个矩形的盒子,包含内容区域、内边距、边框和外边距四个部分。理解盒模型对于控制元素的大小、边距和布局非常重要。浮动是一种布局方式,可以使元素脱离文档流,向左或向右移动,其他元素则围绕其周围进行布局。常用于实现多栏布局或图文混排等效果。定位是一种布局方式,用于控制元素在页面中的精确位置。常用的定位方式有相对定位、绝对定位和固定定位。通过本篇博客的学习,我们详细介绍了CSS的盒模型、浮动与清除浮动以及定位与层叠等常用样式属性。",
    "author": "作者:程序员小豪"
  },
  {
    "title": "2023年总结的web前端学习路线分享(学习导读)",
    "desc": "以上是博主自学一年前端总结的一些经验,虽然不是特别的准确但是至少能够给刚入学的前端小白一个借鉴的分享,我也会把一些人可能问到的问题总结如下,给大家分享一下我的经历:前端难不难?前端相较于后端语言来说还是比较简单的,不用考虑太多的算法方面知识,如果你的编程能力不是特别的优秀又想从事编程行业,前端可以说是一个不错的选择。前端好就业吗?近几年由于培训机构输出大量的程序员,导致现在前端有一点饱和,人数的众多导致企业对人才的要求也提高了,如果你想从事前端,建议早最准备学习多项技术才能让自己不被淘汰下去。",
    "author": "作者:亦世凡华、"
  },
  {
    "title": "vue面试题八股文简答大全 让你更加轻松的回答面试官的vue面试题",
    "desc": "此时我们就需要先根据真实dom生成虚拟dom, 当虚拟dom某个节点的数据改变后会生成有一个新的Vnode, 然后新的Vnode和旧的Vnode作比较,发现有不一样的地方就直接修改在真实DOM上,然后使旧的Vnode的值为新的Vnode。这种方法比直接操作真实DOM要快得多。从源码中可以知道,Vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永 远认为这是两个相同的节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。",
    "author": "作者:奶糖 肥晨"
  },
  {
    "title": "微信小程序--》从零实现小程序项目案例",
    "desc": "🏍️作者简介:大家好,我是亦世凡华、渴望知识储备自己的一名在校大学生🚲座右铭:人生亦可燃烧,亦可腐败,我愿燃烧,耗尽所有光芒。👀引言⚓经过web前端开发的学习,相信大家对于前端开发有了一定深入的了解,今天我开设了微信小程序专栏,主要想从移动端开发方向进一步发展,而对于我来说写移动端博文的第一站就是小程序开发,希望看到我文章的朋友能对你有所帮助。今天借助黑马的本地生活案例,加强一下自己对小程序的学习,并将学习过程分享出来,希望能和以前学习的知识相互印证。",
    "author": "作者:亦世凡华、"
  },
  {
    "title": "15套前端经典实战项目大合集,小白练手必备实战项目",
    "desc": "15套前端经典实战项目大合集,悄悄练习,你会惊艳所有人。今日我以内卷为荣,明日内卷以我为荣,不管学习哪门语言都要做出实际的东西来,这个实际的东西就是项目。这里整理了15前端经典实战项目,每套都有完整且详细的视频教程和源码,你可从中选择自己喜欢的项目做参考练手,也可以从中寻找灵感去做自己的项目。",
    "author": "作者:编程小老太"
  },
  {
    "title": "vue3 vite Uncaught (in promise) ReferenceError: Cannot access ‘xx‘ before initialization",
    "desc": "Uncaught (in promise) ReferenceError: Cannot access 'xxx' before initialization",
    "author": "作者:qq_18872627"
  },
  {
    "title": "Element table组件动态设置expand展开项以及同时只展开一项",
    "desc": "场景一:table表格展开项过多,界面数据太繁杂影响查看。场景二:Element Table加载的数据发生变化时,会重新渲染界面,之前的展开项会自动关闭,用户需要手动去打开展开项,频繁的手动操作会极大的影响客户体验。",
    "author": "作者:lucky_fd_"
  },
  {
    "title": "js如何获取对象中的所有属性",
    "desc": "并逐个输出它们的值;每次执行时,都会将一个属性名赋值给所定义的变量。该方法返回一个数组,数组内包含对象自身所有可枚举属性值。,该方法返回一个数组,数组内包括对象内可枚举属性。方法三:Object.values()方法二:Object.keys()方法一:for...in。所有属性(属性名和属性值)",
    "author": "作者:翻滚的露西"
  },
  {
    "title": "VUE登录注册页面,完整vue,直接复制",
    "desc": "VUE登录注册页面,直接复制全部",
    "author": "作者:good_good_study5"
  },
  {
    "title": "CSS3模拟小仓鼠一直奔跑的动画特效",
    "desc": "今天就通过CSS3来实现一只一直奔跑着的小仓鼠。",
    "author": "作者:经海路大白狗"
  },
  {
    "title": "别找了诸位 【十二款超级好用的谷歌插件都在这】(确定不来看看?)",
    "desc": "十二款超级好用的谷歌插件《🌇第一款、油猴插件🌇第二款、Adblock Plus - 免费的广告拦截器🌇第三款、谷歌清理大师(CleanMaster)🌇第四款、google翻译🌇第五款、OneTab🌇第六款、infinity新标签页🌇第七款:SimilarSites(发现类似网站)🌇第八款、Talend API Tester - Free Edition🌇第九款、Octotree (为GitHub而生)🌇第十款、XPath Helper🌇第十一》............",
    "author": "作者:在下周周ovo"
  },
  {
    "title": "Vue框架--Ruoyi解析(前端)",
    "desc": "Vue框架--Ruoyi(前端)",
    "author": "作者:开发那点事儿~"
  },
  {
    "title": "vue3 antd项目实战——table表格的自定义筛选【纯前端filters过滤、自定义筛选table表格数据】",
    "desc": "vue3 ant design vue搭建的后台管理系统中,使用filters属性自定义筛选table表格中大量的数据。文章将通过三个实例详细介绍如何实现table表格的表头自定义筛选",
    "author": "作者:Dorable_Wander"
  }
]

通用爬虫模版抽取

经过上面五个爬虫案例的学习,有没有发现其实我们的代码结构很相似,只需要分析好我们想爬取的网页DOM元素结构,然后修改一些代码里获取的HTML元素即可。

我们来把代码抽成一个通用的爬虫模块:

// 根据爬取的页面自行选择是http还是https
// const http = require('http'); 
const https = require('https');
const cheerio = require('cheerio');
const iconv = require('iconv-lite');
const fs = require('fs')

const url = '你想要爬取的网页地址';

https.get(url, { rejectUnauthorized: false }, (res) => {
  let html = '';

  res.on('data', (chunk) => {
    html += iconv.decode(chunk, 'utf-8');
  });

  res.on('end', () => { 
    const $ = cheerio.load(html);
    const list = [];

    // 这里是解析HTML的重点,根据网页结构分析自行修改即可
    $('').each(function () {
      const title = $('', this).text().trim();
      // const desc = $('', this).text().trim();
      list.push({ title, desc });
    });

    // console.log(courses);
    //将数据写入文件中
    fs.writeFile('./文件名称.json', JSON.stringify(list), function (err, data) {
      if (err) {
        throw err
      }
      console.log('文件保存成功');
    })
  });
});

经过刚刚上面五个爬虫案例的实测,我们使用这个模版可以简单爬取大部分没有反爬机制的静态页面

但是一些动态渲染出来的页面或者有反爬机制的页面我们没有办法用这个模版爬取

爬虫总结

一般来说,以下类型的页面比较难爬:

  1. 动态页面:动态页面是指页面内容是通过 JavaScript 或者其他脚本语言动态生成的页面,这种页面往往需要使用浏览器来执行脚本才能获取到完整的页面内容,因此比较难以爬取。
  2. 验证码页面:验证码页面是指需要用户输入验证码才能访问的页面,这种页面需要使用 OCR 技术或者人工识别验证码才能获取到完整的页面内容,因此比较难以爬取。
  3. 反爬虫页面:反爬虫页面是指采用了一系列技术手段来防止爬虫访问的页面,如 IP 封禁、请求频率限制、请求头识别等,这种页面需要使用一些反爬虫技巧才能获取到完整的页面内容,因此比较难以爬取。
  4. 除此之外,还有一些其他的原因会导致页面难以爬取,如:页面结构复杂、数据分散在多个页面中、数据需要登录才能访问等。

针对这些问题,需要使用一些技巧和工具来解决:

  • 使用正则表达式或者 XPath 来解析页面结构
  • 使用分布式爬虫来爬取多个页面
  • 使用模拟登录的方式来获取登录后的数据
  • 使用代理可以帮助我们隐藏真实IP地址,从而避免被封锁
  • 伪装请求头中的信息或延迟请求

之后我会再写一篇爬虫进阶,用 Puppeteer 带你实现如何爬取动态渲染页面

总结

本篇博客我们主要介绍了以下内容:

  • 爬虫简介:介绍了什么是爬虫、爬虫的应用场景、爬虫的优缺点、为什么要使用Node.js来编写爬虫等
  • 爬虫实战:通过五个爬虫案例(百度首页、百度热搜、豆瓣TOP250、CSDN)实战带大家完全入门了Node.js爬虫,并且我们抽取了一套适用静态页面的通用爬虫模版

总之,爬虫是一门综合性很强的技术,需要掌握多种技巧和工具,并且需要不断地学习和实践才能掌握!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/48551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

x3daudio1 7.dll丢失怎么修复,哪个修复方法更推荐使用

最近我的电脑出现了一个错误提示,说缺少一个名为x3daudio1_7.dll的文件。这个错误导致我无法运行一些游戏和应用程序。为了解决这个问题,我开始寻找修复方法,在这个过程中,我了解到x3daudio1_7.dll是一个与音频相关的库文件&#…

vs2013 编译wxwidgets界面库

首先进入官网下载,本人再git上下载的基本都编译失败 https://www.wxwidgets.org/ 在网站里面找最新的就可以,下载之后放在一个目录,找到vs的目录 然后找到wx_vc12.sln,打开编译即可 Debug、Release编译出来的是静态库 DLL Deb…

【C++】【自用】选择题 刷题总结

文章目录 【类和对象】1. 构造、拷贝构造的调用2. 静态成员变量3. 初始化列表4. 成员函数:运算符重载5. 友元函数、友元类55. 特殊类设计 【细节题】1. 构造 析构 new \ deletet、new[] \ delete[] 【类和对象】 1. 构造、拷贝构造的调用 #include using namespace…

Python GDAL为具有多个波段的大量栅格图像绘制像素随时间变化走势图

本文介绍基于Python中的gdal模块,对大量长时间序列的栅格遥感影像文件,绘制其每一个波段中、若干随机指定的像元的时间序列曲线图的方法。 在之前的文章Python中GDAL批量绘制多时相栅格遥感影像的像元时间序列曲线图(https://blog.csdn.net/z…

C++,类和对象-多态,制作饮品

#include<iostream> using namespace std;//多态案例&#xff0c;制作饮品class AbstractDrinking { public://煮水virtual void Boil() 0;//冲泡virtual void Brew() 0;//倒入茶杯virtual void PourInCup() 0;//加入辅料virtual void PutSomething() 0;//制作饮品vo…

Codeforces Round 886 (Div. 4)F题解

文章目录 [We Were Both Children](https://codeforces.com/contest/1850/problem/F)问题建模问题分析1.分析到达的点与跳跃距离的关系2.方法1倍数法累计每个点所能达到的青蛙数代码 方法2试除法累计每个点能到达的青蛙数代码 We Were Both Children 问题建模 给定n个青蛙每次…

文本预处理——文本数据分析

目录 文本数据分析中文酒店评价语料获得训练集和验证集的标签数量分布获取训练集和验证集的句子长度分布获取训练集和验证集的正负样本长度散点分布获得训练集和验证集不同词汇总数统计获得训练集上正负的样本的高频形容词词云获得验证集上正负的样本的形容词词云 文本数据分析…

【Java】Spring关于Bean的存和取、Spring的执行流程以及Bean的作用域和生命周期

Spring项目的创建普通的存和取存储Bean创建Bean将Bean注册到容器中 获取并使用Bean获取Spring上下文获取并使用 更简单的存和取存储Bean配置扫描路径添加注解类注解Bean的命名规则五大注解的区别方法注解Bean方法注解要配合类注解使用重命名 Bean有参数的方法 获取Bean属性注入…

Redis三种模式——主从复制,哨兵模式,集群

目录 一、主从复制 1.1主从复制的概念 1.2Redis主从复制作用 1.2.1数据冗余 1.2.2故障恢复 1.2.3负载均衡 1.2.4高可用基石 1.3Redis主从复制流程 1.4部署Redis 主从复制 1.4.1.环境部署 1.4.2.所有服务器都先关闭防火墙 1.4.3.所有服务器都安装Redis 1.4.4修改Master主节点R…

【C#】微软的Roslyn 是个啥?

一、说明 Roslyn 是微软重写的C#编译器并开源。 Roslyn 是 C# 和 Visual Basic.NET 开源编译器的代号。以下是它如何在过去十年企业Microsoft的最黑暗中开始&#xff0c;并成为所有C#&#xff08;和VB&#xff09;的开源&#xff0c;跨平台&#xff0c;公共语言引擎&#xff0c…

el-upload上传图片和视频,支持预览和删除

话不多说&#xff0c; 直接上代码&#xff1a; 视图层&#xff1a; <div class"contentDetail"><div class"contentItem"><div style"margin-top:5px;" class"label csAttachment">客服上传图片:</div><el…

教育新花样?看智慧教育如何出“花样”

智慧教育是物联化、智能化、感知化、泛在化的新型教育形态和教育模式。数字孪生可视化作为智慧教育的应用之一&#xff0c;优化了教育发展形态。本文以智慧教育浙江大学项目为例&#xff0c;介绍智慧教育的具体应用场景。 一、项目背景 &#xff08;一&#xff09;政策背景 …

springboot自动装配

SPI spi &#xff1a; service provider interface &#xff1a; 是java的一种服务提供机制&#xff0c;spi 允许开发者在不修改代码的情况下&#xff0c;为某个接口提供实现类&#xff0c;来扩展应用程序 将实现类独立到配置文件中&#xff0c;通过配置文件控制导入&#xff…

基于Ko-time的Springboot单体化调用链追踪实践

目录 前言 一、关于Ko-Time 1、是什么&#xff1f; 2、ko-time更新时间线 二、Ko-time怎么用&#xff1f; 1、依赖引入 2、配置集成 3、权限放行 三、链路追踪 1、系统运行 2、链路追踪 3、长时间调用模拟 总结 前言 熟悉微服务的老司机一定了解&#xff0c;在微服务模…

【C++】特殊类的设计 | 类型转换

文章目录 1. 特殊类的设计单例模式饿汉模式具体代码 懒汉模式具体代码 懒汉模式和饿汉模式的优缺点 2. C的类型转换C语言的类型转换C的类型转换static_castreinterpret_castconst_castdynamic_cast 1. 特殊类的设计 单例模式 设计模式是 被反复使用 多数人知晓 经过分类的、代…

React之生命周期

React之生命周期 旧版本&#xff0c;函数组件是没有生命周期的。新版本中通过useEffect触发函数的生命周期 一、基于类组件的生命周期 React的组件生命周期分为挂载阶段、更新阶段和销毁阶段。因为React的state不具有Vue的响应式&#xff0c;所以并没有create阶段 1、挂载阶段&…

第八章:list类

系列文章目录 文章目录 系列文章目录前言list的介绍及使用list的介绍list的使用list的构造函数list的迭代器list的容量list的成员访问list的增删改查 list与vector的对比总结 前言 list是STL的一种链表类&#xff0c;可以在常数范围内在任意位置进行插入和删除的序列式容器。 …

专访伊士曼中国区高管赵志伟:以创新应对新能源汽车后市场变化

受访人&#xff1a;伊士曼高性能膜事业部中国区商务总监赵志伟 新能源汽车发展至规模化阶段&#xff0c;以贴膜、保养维修为主的后市场产业迎来快速崛起&#xff0c;新能源消费者在汽车贴膜、改装和养护领域也表现出比燃油车更高频的需求度。 作为一家全球特种材料公司&#x…

MySQL~DQL查询语句

一、DQL:查询语句 1、排序查询 语法&#xff1a; order by 子句 ​ order by 排序字段1 排序方式1 &#xff0c;排序字段2 排序方2... 排序方式&#xff1a; ASC&#xff1a;升序[默认] DESC&#xff1a;降序 在SQL语句中永远排序最后 注&#xff1a; 如果有多个排序条…

立创EDA学习

学习树莓派3B的板子发现有个扩展板比较好&#xff0c;自己最好画一个&#xff0c;反正免费。 学习视频&#xff1a;立创EDA&#xff08;专业版&#xff09;电路设计与制作快速入门。 下载专业版&#xff0c;并激活。【分专业版和标准版&#xff0c;专业版也是免费的】 手机…