使用Puppeteer进行数据抓取(一)

424 阅读3分钟

     前言:最近因为有了空闲,想要用Python爬一些数据,突然想起为什么不用Node.js 来爬数据呢,毕竟Python已经几年没有碰过了,于是乎就有了本文(纯属用于记录个人开发经历),但是目前市面上使用Node开发似乎不多,所以可能本文写得可能会有些许潦草,望阅读到得小伙伴多多见谅,也可以指出不足或是错误之处。

目前的项目采用 _VSCode_开发。

1.首先新建项目,执行 npm init 先对项目初始化。

2.安装 我们的主角puppeteer npm i puppeteer

3. 新建一个项目目录  src/index.js  (这个是本人开发的习惯)

4.先在index.js 引入puppetter

`const puppeteer = require('puppeteer')`

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 

接下来,我们就应该开始爬取我们的数据了。

await page.evaluate(options)  我们通过这个执行脚本,它类似控制台执行命令
 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的。

     所以我们这边 不是通过  (.mcona)来获取class,而是通过 ('.m-con a') 来获取 class,而是通过  ('#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();
    })();

下一篇,我们来讲讲怎么进入详情页爬取详情的数据,感谢小伙伴们的翻阅,有什么不足之处可以进行回复交聊。