用Puppeteer做一个简单的数据爬虫

用Puppeteer做一个简单的数据爬虫

本项目仅做参考,提供给新手的一个入门示例。此项目是利用puppeteer来爬取本人一个博客系统的数据

  • 先看演示

因为禁用了headless,所以会弹出浏览器窗口,也是为了容易调试看效果

注意:因为是我自己的个人博客,服务器又是在国外,在国内访问较慢。如运行本demo出现超时,属于正常现象,这种情况下建议开启科学上网(搞技术的这就不多说了吧),开启全局代理试下

目录结构

  • /data: 抓取数据存储位置(有条件的可以直接存在数据库中)
  • /utils: 工具类
  • index.js: 入口文件(核心代码)
  • config.js: 配置文件(抓取地址与抓取页面数配置)

项目启动

  • 安装依赖(进入项目目录)
    npm install
    
  • 普通启动
    npm start
    
  • 调试启动
    npm run dev
    

核心代码

  • 首先引入puppeteerconfig.js配置文件,并用禁用headless启动,以便观察

puppeteer依赖Chromium,每个页面进程的权限被限制在一个沙盒内,即使某一个页面被恶意软件攻击了,只要将其关掉,就不会对操作系统或者其他的页面产生影响。

import puppeteer from "puppeteer";
import config from "./config";

var ALL_PAGES = config.allPages; //抓取的目标网站 [String]
var ORIGIN = config.origin; //抓取的页数 [Number]

(async () => {
    // 启动puppeteer
	var browser = await puppeteer.launch({
		headless: false,
		devtools: false,
		defaultViewport: {
			width: 1200,
			height: 1000
		}
	});
    // 初始化一个页面
	var page = await browser.newPage();
    

    // 这里写爬取数据逻辑代码

	await browser.close();
})();

  • 在写逻辑代码之前,先看一下咱们今天爬取的目标网站进行分析

    打开首页就可以看到一个全部博文的区域

    分析:

    1. 每页都有6篇文章
    2. 每页下面都有页码,点击下一页之后,地址变成http://blog.fe-spark.cn/page/页数/, 而且依然有全部博文
    3. 每篇文章信息需要点击进去才能得到完整的博文
  • 得到这几条分析结果之后,开始实践

    1. 首先要进行循环操作,那循环谁呢?从目标网站可以看出一共五页,那就循环这五页
    2. 内部还要进行循环,循环的当然就是每页的博文了~因为只有通过循环每个博文之后,才能获取到博文的内容呀~
for (var i = 0; i <= ALL_PAGES - 1; i++) {
    //先假设有一个loadPage方法,来获取每页的所有博文数据
    var a = await loadPage(i);
    
    // 获取每页的数据进行储存
    writeFileSync(
        resolve(__dirname, "./data/page_" + (i + 1) + ".json"),
        JSON.stringify(a),
        "utf-8"
    );
}
  • 继续编写loadPage方法
async function loadPage(i) {
    // goto方法是跳转指定页面
    await page.goto(
        // 这里判断一下,因为首页直接就是第一页,从第二页开始后面才要拼接`page/页数/`
        `${ORIGIN}/${i == 0 ? "" : "page/" + parseInt(i + 1) + "/"}`,
        {
            waitUntil: "networkidle0",
            timeout: 60000
        }
    );
    // 获取每个博文的跳转链接,用数组存放起来
    var href = await page.$eval(".spark-posts", (dom) => {
        _dom = Array.from(dom.querySelectorAll(".post-card>a"));
        return _dom.map((item) => {
            return item.getAttribute("href");
        });
    });
    // 等待页面加载完毕,获取本页所有博文的标题,简介,及其他需要的数据
    var result = await page.evaluate(() => {
        var links = [];
        var parent = document.querySelector(".spark-posts");
        var list = parent.querySelectorAll(".post-card");
        if (list.length > 0) {
            Array.prototype.forEach.call(list, (item) => {
                var article_title = item.querySelector("a .title")
                    .innerText;
                var description = item.querySelector("a .excerpt")
                    .innerText;
                var mate = item.querySelector(".metadata .date").innerText;
                var tags = Array.prototype.map.call(
                    item.querySelectorAll(".metadata>div a"),
                    (item) => {
                        return item.innerText;
                    }
                );

                links.push({
                    article_title,
                    description,
                    mate,
                    tags
                });
            });
            return links;
        }
    });
    // 以上操作并没有获取博文的内容,下面要进到每篇博文里面,提取博文内容
    for (var i = 0; i < href.length; i++) {
        await page.goto(`${ORIGIN}${href[i]}`, {
            waitUntil: "networkidle0",
            timeout: 60000
        });
        
        var content = await page.evaluate(() => {
            return $(".post-wrapper").text();
        });
        result[i].content = content;
    }

    // 最后将组装好的结果返回出去接收
    return result;
}

最终结果

爬取的数据并没有去除空格,有兴趣的同学可以自己改一下

上面代码使用的es2016,此项目已配置babel,直接运行即可

自行下载运行看效果吧

点击这里去看项目

参考

Puppeteer: https://github.com/puppeteer/puppeteer

爬虫利器 Puppeteer 实战https://www.jianshu.com/p/a9a55c03f768

转载请注明出处,原文地址>>