前言:最近因为有了空闲,想要用Python爬一些数据,突然想起为什么不用Node.js 来爬数据呢,毕竟Python已经几年没有碰过了,于是乎就有了本文(纯属用于记录个人开发经历),但是目前市面上使用Node开发似乎不多,所以可能本文写得可能会有些许潦草,望阅读到得小伙伴多多见谅,也可以指出不足或是错误之处。
目前的项目采用 _VSCode_开发。
1.首先新建项目,执行 npm init 先对项目初始化。
2.安装 我们的主角puppeteer npm i puppeteer
3. 新建一个项目目录 src/index.js (这个是本人开发的习惯)
4.先在index.js 引入puppetter
5.因为我这里练手的 第一财经 的网站,所以就暂时拿来爬了。网站地址均可以按照自己的需求爬取。
进入正文
这里写的是一个 自执行函数 来运行项目
;(async ()=>{})();
具体的 puppetter 的基本写法 网上很多,这边就不具体详细介绍了,这边我就直接贴代码了。
const puppeteer = require('puppeteer')
const baseUrl = 'https://www.yicai.com';
; (async () => {
const browser = await puppeteer.launch({
headless: false, //浏览器界面启动
slowMo: 200, //放慢浏览器执行速度,方便测试观察
args: ['--no-sandbox'],
dumpio: false,
devtools: true, //开发模式
});
const page = await browser.newPage();
await page.goto(baseUrl, {
waitUntil: 'networkidle2'
});
await page.waitFor(2000);
// 等待加载更多按钮节点的加载
await page.waitForSelector('.u-btn')
})();
因为他们的页面分页功能,不像 pagination 这些分页器一样,所以暂时的策略是一口气点击加载更多数据出来
所以在当我等待加载更多的按钮出现时候,实现循环点击页面按钮
for (let index = 0; index < 1; index++) {
// 这边我的index<1 目前只爬取一页数据而已,具体要爬取多少就自行修改爬取的页数
await page.waitFor(2000); // 这里等待2s 是为了等他们数据返回回来,避免一直点击
await page.click('.u-btn')
}
下图就是具体 我们上面代码实现 点击按钮的地方,我们可以获取到 具体的class
接下来,我们就应该开始爬取我们的数据了。
const result = await page.evaluate(() => {
let $ = window.$;
// var items = $('.m-con a') // 不可用这个,由于页面是通过display:none or block来控制的,爬取的数据会是多个页面的叠加 var items = $('#headlist').children('a')
var links = []
if (items.length >= 1) {
items.each((index, item) => {
let it = $(item)
let articleTitle = it.find('h2').text()
let articleIntroduction = it.find('p').text()
let imageAddress = it.find('img').attr('src')
let createdTime = it.find('span').text()
let detailPage = it.attr('href')
links.push({
articleTitle,
articleIntroduction,
imageAddress,
createdTime,
detailPage: detailPage
})
})
}
return links
上面为什么我没有通过 _var items = $('.m-con a') _ 这个拿到我们的列表数据呢? 这个就要我们分析我们爬取的网页的元素结构了。 具体看下图分析
我自己的需求是拍头条Tab下的列表数据。但是当我在打日志的时候发现打印出来的数据却是75条,而我反复查看发现页面只有25条,所以我就 F12 再次走起,最后发现,他们是通过 dispaly:none 和 display:block 来控制标签,说明首次进入页面他们是一口气加载出来3个tab的,并且他们是各自有一个id的。
所以我们这边 不是通过 ('#headlist').children('a')获取到 headlist 下的 a 标签来处理数据。
当然,这个是具体情况具体分析,根据各自页面的元素决定的
好的,我们已经把脚本方法写完了,我们可以执行一下。。。。。。
node src/index.js 执行命令
好了这就是我们打印出来的数据了。
最后我把最后的代码贴给大家
const puppeteer = require('puppeteer')
const baseUrl = 'https://www.yicai.com';
; (async () => {
const browser = await puppeteer.launch({
headless: false, //浏览器界面启动
slowMo: 200, //放慢浏览器执行速度,方便测试观察
args: ['--no-sandbox'],
dumpio: false,
devtools: true, //开发模式
});
const page = await browser.newPage();
await page.goto(baseUrl, {
waitUntil: 'networkidle2'
});
await page.waitFor(2000); // 等待某个节点的加载
await page.waitForSelector('.u-btn')
for (let index = 0; index < 1; index++) {
await page.waitFor(2000);
await page.click('.u-btn')
}
const result = await page.evaluate(() => {
let $ = window.$;
// var items = $('.m-con a') // 不可用这个,由于页面是通过display:none or block来控制的,爬取的数据会是多个页面的叠加
var items = $('#headlist').children('a')
var links = []
if (items.length >= 1) {
items.each((index, item) => {
let it = $(item)
let articleTitle = it.find('h2').text()
let articleIntroduction = it.find('p').text()
let imageAddress = it.find('img').attr('src')
let createdTime = it.find('span').text()
let detailPage = it.attr('href')
links.push({
articleTitle,
articleIntroduction,
imageAddress,
createdTime,
detailPage: detailPage
})
})
}
return links
})
console.log(result);
await page.close();
})();