JavaScrapt:用node轻松实现网络爬虫,提取豆瓣网页中的数据
一、准备工作:
下载node,会在终端上使用
创建文件夹,将此文件夹初始化为后端项目:
npm init -y
成功后,你的文件夹里会有一个项目描述文件package.json。
需要一个入口文件main.js,在文件中添加。
二、写爬虫思路:
- 发起 HTTP 请求:使用
request-promise
模块发起 HTTP 请求,获取豆瓣电影 Top250 页面的 HTML 内容。
安装 request-promise
模块
npm i request-promise
在package.json文件里有依赖就说明安装成功了
- 解析 HTML 内容:使用
cheerio
模块将获取到的 HTML 内容解析成 DOM 树,以便后续的数据提取。
安装cheerio
模块
npm i cheerio
-
提取电影信息:通过解析后的 DOM 树,从每个电影节点中提取电影的标题、信息和评分等内容。
-
处理分页:循环遍历每一页,调用
getPage
函数获取每一页的电影信息,并将结果存储到一个数组中。 -
写入 JSON 文件:将存储电影信息的数组写入到名为
output.json
的 JSON 文件中。
三、代码解析:
1. 首先,通过 require
关键字引入了需要的模块:
request-promise
用于发起 HTTP 请求,并以 Promise 形式返回响应数据。cheerio
是一个类似 jQuery 的库,用于在服务器端解析 HTML 和操作 DOM。fs
是 Node.js 的文件系统模块,用于读写文件。util
是 Node.js 内置模块,提供一些实用函数。
// 引入所需的模块
let request = require('request-promise') // 使用 request-promise 模块发送 HTTP 请求
let cheerio = require('cheerio') // 使用 cheerio 模块解析 HTML
let fs = require('fs') // 使用 fs 模块进行文件操作
const util = require('util') // 使用 util 模块提供的实用函数
2. 定义了一个空数组 movies
用于存储电影信息,以及一个变量 basicUrl
,表示豆瓣电影Top250页面的基本 URL。
// 存储电影信息的数组
let movies = []
// 豆瓣电影Top250页面的基本 URL
let basicUrl = 'https://movie.douban.com/top250'
3. 定义了一个名为 once
的函数,其作用是确保传入的回调函数只被调用一次。
// 确保回调函数只被调用一次的函数
let once = function (cb) {
let active = false
if (!active) {
cb()
active = true
}
}
4. 定义了一个名为 log
的函数,它接受一个参数 item
,在函数内部调用了 once
函数来确保 console.log(item)
只被调用一次。
// 打印信息,并确保只打印一次
function log(item) {
once(() => {
console.log(item)
})
}
5. 定义了一个名为 getMovieInfo
的函数,接受一个参数 node
,表示一个 HTML 节点,然后使用 cheerio 库解析该节点,从中提取电影的标题、信息和评分,并返回一个包含这些信息的对象。
// 解析电影信息的函数
function getMovieInfo (node) {
// 使用 cheerio 加载 HTML 节点
let $ = cheerio.load(node)
// 选择电影标题元素并获取文本内容
let titles = $('.info .hd span')
titles = ([]).map.call(titles, t => {
return $(t).text()
})
// 选择电影信息元素并获取文本内容
let bd = $('.info .bd')
let info = bd.find('p').text()
// 选择电影评分元素并获取文本内容
let score = bd.find('.star .rating_num').text()
// 返回包含电影信息的对象
return { titles, info, score }
}
6. 定义了一个名为 getPage
的异步函数,用于获取指定 URL 页面的 HTML 内容,并从中解析出电影信息。该函数接受两个参数:url
表示要爬取的页面 URL,num
表示当前页面的页码。在函数内部,使用 request-promise
发起 HTTP 请求,获取页面内容,然后使用 cheerio 加载页面,并通过选择器定位到电影信息所在的节点,最终返回一个包含电影信息的数组。
// 异步函数:获取指定 URL 页面的电影信息
async function getPage (url, num) {
// 发送 HTTP 请求获取页面内容
let html = await request({
url
})
// 打印连接成功的提示信息和当前页码
console.log('连接成功!', `正在爬取第${num+1}页数据`)
// 使用 cheerio 加载页面内容
let $ = cheerio.load(html)
// 选择电影节点元素
let movieNodes = $('#content .article .grid_view').find('.item')
// 将每个电影节点解析为电影信息,并存入数组中
let movieList = ([]).map.call(movieNodes, node => {
return getMovieInfo(node)
})
// 返回包含电影信息的数组
return movieList
}
7. 定义了一个名为 main
的异步函数,作为程序的入口。在 main
函数中,首先设定了 count
变量表示要爬取的页面数量(每页包含25部电影),然后通过循环依次爬取每一页的电影信息,将结果存入 list
数组中。最后,将 list
数组中的电影信息写入到一个名为 output.json
的 JSON 文件中,并在写入完成后打印提示信息。
async function main () {
// 要爬取的页面数量
let count = 25
let list = []
// 循环爬取每一页的电影信息
for (let i = 0 ; i < count ; i++) {
// 构造当前页的 URL
let url = basicUrl + `?start=${25*i}`
// 获取当前页的电影信息,并存入数组中
list.push(... await getPage(url, i))
}
// 打印获取的电影信息数量
console.log(list.length)
// 将电影信息写入 JSON 文件
fs.writeFile('./output.json', JSON.stringify(list), 'utf-8', () => {
console.log('生成json文件成功!')
})
}
8. 最后,调用 main
函数启动整个爬虫程序,开始爬取豆瓣电影Top250页面的电影信息。
// 启动爬虫程序 main()
9.运行成功后会有一个 output.json
文件
打开
output.json
文件:
四、整体代码:
let request = require('request-promise') // 需要安装
let cheerio = require('cheerio') // 需要安装
let fs = require('fs')
const util = require('util')
let movies = []
let basicUrl = 'https://movie.douban.com/top250'
let once = function (cb) {
let active = false
if (!active) {
cb()
active = true
}
}
function log(item) {
once(() => {
console.log(item)
})
}
function getMovieInfo (node) {
let $ = cheerio.load(node)
let titles = $('.info .hd span')
titles = ([]).map.call(titles, t => {
return $(t).text()
})
let bd = $('.info .bd')
let info = bd.find('p').text()
let score = bd.find('.star .rating_num').text()
return { titles, info, score }
}
async function getPage (url, num) {
let html = await request({
url
})
console.log('连接成功!', `正在爬取第${num+1}页数据`)
let $ = cheerio.load(html)
let movieNodes = $('#content .article .grid_view').find('.item')
let movieList = ([]).map.call(movieNodes, node => {
return getMovieInfo(node)
})
return movieList
}
async function main () {
let count = 25
let list = []
for (let i = 0 ; i < count ; i++) {
let url = basicUrl + `?start=${25*i}`
list.push(... await getPage(url, i))
}
console.log(list.length)
fs.writeFile('./output.json', JSON.stringify(list), 'utf-8', () => {
console.log('生成json文件成功!')
})
}
main()
转载自:https://juejin.cn/post/7369535515291156514