九二、node+cheerio爬虫学习
爬虫基础
- 以
http://web.itheima.com/teacher.html
网站目标为例,最终目的是下载网站中所有老师的照片:
- 发送http请求,获取整个网页内容
- 通过cheerio库对网页内容进行分析
- 提取img标签的src属性
- 使用download库进行批量图片下载
发送一个HTTP请求
- 发送HTTP请求并获取相应
在爬虫之前,需要对HTTP请求充分了解,因为爬虫的原理就是发送请求到指定URL,获取响应后并处理
node官方api
node的核心模块 http模块即可发送请求,摘自node官网api:
由此可见只需要使用http.request()
方法即可发送http请求
发送http请求案例
也可以使用axios
库来代替
代码如下:
// 引入http模块
const http = require('http')
// 创建请求对象
let req = http.request('http://web.itheima.com/teacher.html', res => {// 准备chunkslet chunks = []res.on('data', chunk => {// 监听到数据就存储chunks.push(chunk)})res.on('end', () => {// 结束数据监听时讲所有内容拼接console.log(Buffer.concat(chunks).toString('utf-8'))})
})
// 发送请求
req.end()
得到的结果就是整个HTML网页内容
将获取的HTML字符串使用cheerio解析
- 使用cheerio加载HTML
- 回顾jQueryAPI
- 加载所有的img标签的src属性
cheerio库简介
这是一个核心api按照jquery来设计,专门在服务器上使用,一个微小、快速和优雅的实现
简而言之,就是可以再服务器上用这个库来解析HTML代码,并且可以直接使用和jQuery一样的api
官方demo如下:
const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')$('h2.title').text('Hello there!')
$('h2').addClass('welcome')$.html()
//=> <html><head></head><body><h2 class="title welcome">Hello there!</h2></body></html>
同样也可以通过jQuery的api来获取DOM元素中的属性和内容
使用cheerio库解析HTML
- 分析网页中所有img标签所在结构
- 使用jQuery API获取所有img的src属性
const http = require('http')
const cheerio = require('cheerio')
let req = http.request('http://web.itheima.com/teacher.html', res => {let chunks = []res.on('data', chunk => {chunks.push(chunk)})res.on('end', () => {// console.log(Buffer.concat(chunks).toString('utf-8'))let html = Buffer.concat(chunks).toString('utf-8')let $ = cheerio.load(html)let imgArr = Array.prototype.map.call($('.tea_main .tea_con .li_img > img'), (item) => 'http://web.itheima.com/' + $(item).attr('src'))console.log(imgArr)// let imgArr = []// $('.tea_main .tea_con .li_img > img').each((i, item) => {// let imgPath = 'http://web.itheima.com/' + $(item).attr('src')// imgArr.push(imgPath)// })// console.log(imgArr)})
})req.end()
使用download库批量下载图片
const http = require('http')
const cheerio = require('cheerio')
const download = require('download')
let req = http.request('http://web.itheima.com/teacher.html', res => {let chunks = []res.on('data', chunk => {chunks.push(chunk)})res.on('end', () => {// console.log(Buffer.concat(chunks).toString('utf-8'))let html = Buffer.concat(chunks).toString('utf-8')let $ = cheerio.load(html)let imgArr = Array.prototype.map.call($('.tea_main .tea_con .li_img > img'), (item) => encodeURI('http://web.itheima.com/' + $(item).attr('src')))// console.log(imgArr)Promise.all(imgArr.map(x => download(x, 'dist'))).then(() => {console.log('files downloaded!');});})
})req.end()
注意事项:如有中文文件名,需要使用base64编码
爬取新闻信息
爬取目标:http://www.itcast.cn/newsvideo/newslist.html
大部分新闻网站,现在都采取前后端分离的方式,也就是前端页面先写好模板,等网页加载完毕后,发送Ajax再获取数据,将其渲染到模板中。所以如果使用相同方式来获取目标网站的HTML页面,请求到的只是模板,并不会有数据
此时,如果还希望使用当前方法爬取数据,就需要分析该网站的ajax请求是如何发送的,可以打开network面板来调试
分析得出对应的ajax请求后,找到其URL,向其发送请求即可
代码如下:
// 引入http模块
const http = require('http')// 创建请求对象 (此时未发送http请求)
const url = 'http://www.itcast.cn/news/json/f1f5ccee-1158-49a6-b7c4-f0bf40d5161a.json'
let req = http.request(url, res => {// 异步的响应// console.log(res)let chunks = []// 监听data事件,获取传递过来的数据片段// 拼接数据片段res.on('data', c => chunks.push(c))// 监听end事件,获取数据完毕时触发res.on('end', () => {// 拼接所有的chunk,并转换成字符串 ==> html字符串// console.log(Buffer.concat(chunks).toString('utf-8'))let result = Buffer.concat(chunks).toString('utf-8')console.log(JSON.parse(result))})
})// 将请求发出去
req.end()
如果遇到请求限制,还可以模拟真实浏览器的请求头:
// 引入http模块
const http = require('http')
const cheerio = require('cheerio')
const download = require('download')// 创建请求对象 (此时未发送http请求)
const url = 'http://www.itcast.cn/news/json/f1f5ccee-1158-49a6-b7c4-f0bf40d5161a.json'
let req = http.request(url, {headers: {"Host": "www.itcast.cn","Connection": "keep-alive","Content-Length": "0","Accept": "*/*","Origin": "http://www.itcast.cn","X-Requested-With": "XMLHttpRequest","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36","DNT": "1","Referer": "http://www.itcast.cn/newsvideo/newslist.html","Accept-Encoding": "gzip, deflate","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8","Cookie": "UM_distinctid=16b8a0c1ea534c-0c311b256ffee7-e343166-240000-16b8a0c1ea689c; bad_idb2f10070-624e-11e8-917f-9fb8db4dc43c=8e1dcca1-9692-11e9-97fb-e5908bcaecf8; parent_qimo_sid_b2f10070-624e-11e8-917f-9fb8db4dc43c=921b3900-9692-11e9-9a47-855e632e21e7; CNZZDATA1277769855=1043056636-1562825067-null%7C1562825067; cid_litiancheng_itcast.cn=TUd3emFUWjBNV2syWVRCdU5XTTRhREZs; PHPSESSID=j3ppafq1dgh2jfg6roc8eeljg2; CNZZDATA4617777=cnzz_eid%3D926291424-1561388898-http%253A%252F%252Fmail.itcast.cn%252F%26ntime%3D1563262791; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1561389179,1563266246; qimo_seosource_22bdcd10-6250-11e8-917f-9fb8db4dc43c=%E7%AB%99%E5%86%85; qimo_seokeywords_22bdcd10-6250-11e8-917f-9fb8db4dc43c=; href=http%3A%2F%2Fwww.itcast.cn%2F; bad_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=f2f41b71-a7a4-11e9-93cc-9b702389a8cb; nice_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=f2f41b72-a7a4-11e9-93cc-9b702389a8cb; openChat22bdcd10-6250-11e8-917f-9fb8db4dc43c=true; parent_qimo_sid_22bdcd10-6250-11e8-917f-9fb8db4dc43c=fc61e520-a7a4-11e9-94a8-01dabdc2ed41; qimo_seosource_b2f10070-624e-11e8-917f-9fb8db4dc43c=%E7%AB%99%E5%86%85; qimo_seokeywords_b2f10070-624e-11e8-917f-9fb8db4dc43c=; accessId=b2f10070-624e-11e8-917f-9fb8db4dc43c; pageViewNum=2; nice_idb2f10070-624e-11e8-917f-9fb8db4dc43c=20d2a1d1-a7a8-11e9-bc20-e71d1b8e4bb6; openChatb2f10070-624e-11e8-917f-9fb8db4dc43c=true; Hm_lpvt_0cb375a2e834821b74efffa6c71ee607=1563267937"}
}, res => {// 异步的响应// console.log(res)let chunks = []// 监听data事件,获取传递过来的数据片段// 拼接数据片段res.on('data', c => chunks.push(c))// 监听end事件,获取数据完毕时触发res.on('end', () => {// 拼接所有的chunk,并转换成字符串 ==> html字符串// console.log(Buffer.concat(chunks).toString('utf-8'))let result = Buffer.concat(chunks).toString('utf-8')console.log(JSON.parse(result))})
})// 将请求发出去
req.end()
注意:请求头的内容,可以先通过真正的浏览器访问一次后获取
封装爬虫基础库
以上代码重复的地方非常多,可以考虑以面向对象的思想进行封装,进一步的提高代码复用率,为了方便开发,保证代码规范,建议使用TypeScript进行封装
以下知识点为扩展内容,需要对面向对象和TypeScript有一定了解!
执行tsc --init
初始化项目,生成ts配置文件
TS配置:
{"compilerOptions": {/* Basic Options */"target": "es2015", "module": "commonjs", "outDir": "./bin", "rootDir": "./src", "strict": true,"esModuleInterop": true },"include": ["src/**/*"],"exclude": ["node_modules","**/*.spec.ts"]
}
Spider抽象类:
// 引入http模块
const http = require('http')
import SpiderOptions from './interfaces/SpiderOptions'export default abstract class Spider {options: SpiderOptions;constructor(options: SpiderOptions = { url: '', method: 'get' }) {this.options = optionsthis.start()}start(): void {// 创建请求对象 (此时未发送http请求)let req = http.request(this.options.url, {headers: this.options.headers,method: this.options.method}, (res: any) => {// 异步的响应// console.log(res)let chunks: any[] = []// 监听data事件,获取传递过来的数据片段// 拼接数据片段res.on('data', (c: any) => chunks.push(c))// 监听end事件,获取数据完毕时触发res.on('end', () => {// 拼接所有的chunk,并转换成字符串 ==> html字符串let htmlStr = Buffer.concat(chunks).toString('utf-8')this.onCatchHTML(htmlStr)})})// 将请求发出去req.end()}abstract onCatchHTML(result: string): any
}export default Spider
SpiderOptions接口:
export default interface SpiderOptions {url: string,method?: string,headers?: object
}
PhotoListSpider类:
import Spider from './Spider'
const cheerio = require('cheerio')
const download = require('download')
export default class PhotoListSpider extends Spider {onCatchHTML(result: string) {// console.log(result)let $ = cheerio.load(result)let imgs = Array.prototype.map.call($('.tea_main .tea_con .li_img > img'), item => 'http://web.itheima.com/' + encodeURI($(item).attr('src')))Promise.all(imgs.map(x => download(x, 'dist'))).then(() => {console.log('files downloaded!');});}
}
NewsListSpider类:
import Spider from "./Spider";export default class NewsListSpider extends Spider {onCatchHTML(result: string) {console.log(JSON.parse(result))}
}
测试类:
import Spider from './Spider'
import PhotoListSpider from './PhotoListSpider'
import NewsListSpider from './NewsListSpider'let spider1: Spider = new PhotoListSpider({url: 'http://web.itheima.com/teacher.html'
})let spider2: Spider = new NewsListSpider({url: 'http://www.itcast.cn/news/json/f1f5ccee-1158-49a6-b7c4-f0bf40d5161a.json',method: 'post',headers: {"Host": "www.itcast.cn","Connection": "keep-alive","Content-Length": "0","Accept": "*/*","Origin": "http://www.itcast.cn","X-Requested-With": "XMLHttpRequest","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36","DNT": "1","Referer": "http://www.itcast.cn/newsvideo/newslist.html","Accept-Encoding": "gzip, deflate","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8","Cookie": "UM_distinctid=16b8a0c1ea534c-0c311b256ffee7-e343166-240000-16b8a0c1ea689c; bad_idb2f10070-624e-11e8-917f-9fb8db4dc43c=8e1dcca1-9692-11e9-97fb-e5908bcaecf8; parent_qimo_sid_b2f10070-624e-11e8-917f-9fb8db4dc43c=921b3900-9692-11e9-9a47-855e632e21e7; CNZZDATA1277769855=1043056636-1562825067-null%7C1562825067; cid_litiancheng_itcast.cn=TUd3emFUWjBNV2syWVRCdU5XTTRhREZs; PHPSESSID=j3ppafq1dgh2jfg6roc8eeljg2; CNZZDATA4617777=cnzz_eid%3D926291424-1561388898-http%253A%252F%252Fmail.itcast.cn%252F%26ntime%3D1563262791; Hm_lvt_0cb375a2e834821b74efffa6c71ee607=1561389179,1563266246; qimo_seosource_22bdcd10-6250-11e8-917f-9fb8db4dc43c=%E7%AB%99%E5%86%85; qimo_seokeywords_22bdcd10-6250-11e8-917f-9fb8db4dc43c=; href=http%3A%2F%2Fwww.itcast.cn%2F; bad_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=f2f41b71-a7a4-11e9-93cc-9b702389a8cb; nice_id22bdcd10-6250-11e8-917f-9fb8db4dc43c=f2f41b72-a7a4-11e9-93cc-9b702389a8cb; openChat22bdcd10-6250-11e8-917f-9fb8db4dc43c=true; parent_qimo_sid_22bdcd10-6250-11e8-917f-9fb8db4dc43c=fc61e520-a7a4-11e9-94a8-01dabdc2ed41; qimo_seosource_b2f10070-624e-11e8-917f-9fb8db4dc43c=%E7%AB%99%E5%86%85; qimo_seokeywords_b2f10070-624e-11e8-917f-9fb8db4dc43c=; accessId=b2f10070-624e-11e8-917f-9fb8db4dc43c; pageViewNum=2; nice_idb2f10070-624e-11e8-917f-9fb8db4dc43c=20d2a1d1-a7a8-11e9-bc20-e71d1b8e4bb6; openChatb2f10070-624e-11e8-917f-9fb8db4dc43c=true; Hm_lpvt_0cb375a2e834821b74efffa6c71ee607=1563267937"}
})
封装后,如果需要写新的爬虫,则可以直接继承Spider类后,在测试类中进行测试即可,仅需实现具体的爬虫类onCatchHTML方法,测试时传入url和headers即可。
而且全部爬虫的父类均为Spider,后期管理起来也非常方便!
最后推荐一个 js常用的utils合集,帮我点个star吧~
- github
- 文档
九二、node+cheerio爬虫学习相关推荐
- Python爬虫学习框架介绍
对于初学者来说,摸索清楚一个领域的知识体系往往比单纯学习某个技术要重要得多,因为技术总会跟随时代发生快速变化,而知识体系往往变化较小,今天我们以自学的角度来了解一下Python爬虫的知识体系吧. 一. ...
- nodejs爬虫 node + cheerio 爬取滚动加载页面
最近在学习nodejs,然后了解到nodejs也可以做爬虫就试了一试还可以就记录一下 爬取爱奇艺首页视频标题 用到的是node+cheerio,cheerio是jq核心功能的一个快速灵活而又简洁的实现 ...
- 爬虫学习笔记(七)——Scrapy框架(二):Scrapy shell、选择器
一.Scrapy shell scrapy shell的作用是用于调试,在项目目录下输入scrapy shell start_urls (start_urls:目标url)得到下列信息: scrapy ...
- python爬虫学习之路(二)re库的使用方法
python基础爬虫学习之路(二) 在上一篇文章中,我们已经学习了有关爬虫对URL的访问以及利用xpath表达式对网页中的信息进行提取,这一篇文章我们将从信息提取的角度来进一步学习爬虫. 正则表达式提 ...
- 爬虫学习(二)--爬取360应用市场app信息
欢迎加入python学习交流群 667279387 爬虫学习 爬虫学习(一)-爬取电影天堂下载链接 爬虫学习(二)–爬取360应用市场app信息 代码环境:windows10, python 3.5 ...
- python 爬虫学习:抓取智联招聘网站职位信息(二)
在第一篇文章(python 爬虫学习:抓取智联招聘网站职位信息(一))中,我们介绍了爬取智联招聘网站上基于岗位关键字,及地区进行搜索的岗位信息,并对爬取到的岗位工资数据进行统计并生成直方图展示:同时进 ...
- 记第一天使用node做爬虫——爬取猫眼电影票房总榜以及指定电影的豆瓣评论前200条
首先,我是一个做前端的应届生,今天朋友想让我帮忙爬取猫眼电影票房总榜的数据,但是我之前一点都没接触过爬虫,但我还是说:okk,我试试: 然后试试就逝世,各种坑接踵而来: 提前声明:这篇文章暂时只是获取 ...
- node 写爬虫,原来这么简单
作者:CarsonXu 原文地址:https://juejin.im/post/5eca37f951882543345e81df 前言 今天给大家带来的是node简单爬虫,对于前端小白也是非常好理解且 ...
- Python 爬虫学习 系列教程
Python爬虫 --- 中高级爬虫学习路线 :https://www.cnblogs.com/Eeyhan/p/14148832.html 看不清图时,可以把图片保存到本地在打开查看... Pyth ...
最新文章
- Debian 陷入尴尬,社区或群龙无首
- python填写excel-python读写Excel
- 【计算理论】自动机设计 ( 设计自动机 | 确定性自动机设计示例 | 确定性与非确定性 | 自动机中的不确定性 )
- 《啊哈!算法》笔记_Day02
- 实战|Python轻松实现动态网页爬虫(附详细源码)
- 从文件中读出数据显示在表格中_玩转表格:如何在Word表格中进行数据计算?...
- 用数学模型向你解释离婚
- django 传递中文_如何在Django中建立消息传递状态
- 命令模式--command模式
- la环球乐园里的机器人_北京环球度假区核心工程完工,你知道这里面都有什么主题景区吗?...
- 为GitHub项目加入Travis-CI的自动集成
- android item复用,RecyclerView item正确的复用方式
- 10个CSS技巧,极大提升用户体验
- 用友u8 如何配置文件服务器,用友u8配置服务器
- 万年历—Java计算感恩节是哪天?感恩节是哪一天?2017年感恩节是哪一天?
- 自己开发了一款视频播放器app
- 程序员的头符合好头的标准吗?
- mac 网络共享 wifi共享
- 转:HiRes高采样率的必要性
- 高德智慧交通地图空间可视化SDK设计与实现
热门文章
- 西门子200高数计数器读取增量式编码器数值
- 2021年二级c语言软件下载,二级c语言模拟考试软件下载 无忧考吧二级c语言考试系统 v2021.3官方安装版 下载-脚本之家...
- Matlab牛顿迭代法求方程的根(GUI)
- 美团点评开源 SQL 优化工具 SQLAdvisor 测试报告
- 打造一流品质,路虎与固特异轮胎强强联手
- 企业级JAVA编码规范
- Android 定位c++异常
- node判断文件是否存在
- 使用黑莓8900通过蓝牙连接华为交换机进行现场网络配置与操作(一)
- WPS匹配两列相同数据,函数 VLOOKUP