爬取动态数据,爬取持久化数据

1. 动态内容与静态内容的区别

动态内容通常指通过 JavaScript 渲染或异步加载的数据。静态内容则是页面加载时直接可见的内容,通常通过 HTML 直接获取。很多现代网站都使用 JavaScript 动态渲染页面内容(例如 AJAX 请求)。

头条网站的热榜内容是动态加载的,页面最初只展示一个固定的热榜,而点击“换一换”按钮后,新的热榜内容会通过 JavaScript 异步加载。爬虫需要等待页面加载或等待某个元素(例如热榜内容)出现,才能获取这些数据。

2. 实现步骤

我们将按照以下步骤完成爬取任务:

  • • 打开今日头条网站并定位到热榜区域。

  • • 等待并抓取初始热榜内容。

  • • 点击“换一换”按钮,刷新热榜内容。

  • • 等待新内容加载并输出新的热榜数据。

3. 代码实现

3.1 步骤 1:创建 Playwright 脚本

首先,确保你已安装 Playwright

npm install playwright

然后,创建一个 crawler.js 文件,并将以下代码添加到该文件中:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });  // 启动浏览器
  const page = await browser.newPage();  // 新建一个页面

  // 打开今日头条网站
  await page.goto('https://toutiao.com');  

  // 等待热榜区域加载完成
  await page.waitForSelector('.home-hotboard');  // 热榜区域的容器(CSS 选择器)

  // 获取初始的热榜内容
  let hotList = await page.$$eval('.home-hotboard .news-title', items =>
    items.map(item => item.textContent.trim())  // 获取每个热榜项的文本内容
  );

  console.log('初始热榜内容:');
  console.log(hotList);

  // 点击“换一换”按钮
  const changeButton = await page.$('.refresh');  // 找到“换一换”按钮
  if (changeButton) {
    await changeButton.click();  // 点击按钮刷新热榜
  }

  // 等待新的热榜内容加载完成
  await page.waitForSelector('.home-hotboard');  // 等待热榜区域重新加载

  // 获取刷新后的热榜内容
  hotList = await page.$$eval('.home-hotboard .news-title', items =>
    items.map(item => item.textContent.trim())  // 获取新热榜项的文本内容
  );

  console.log('刷新后的热榜内容:');
  console.log(hotList);

  await browser.close();  // 关闭浏览器
})();

3.2 步骤 2:代码解析

启动浏览器和打开页面

const browser = await chromium.launch({ headless: true });  // 启动浏览器
const page = await browser.newPage();  // 新建一个页面
await page.goto('https://toutiao.com');  // 打开今日头条网站

这里我们启动了一个无头模式的 Chromium 浏览器并访问了头条网站。

等待热榜加载完成

await page.waitForSelector('.home-hotboard');  // 等待热榜区域加载完成

我们使用 waitForSelector 等待页面中的 热榜 区域(通过 CSS 选择器 .home-hotboard)加载完成。

获取初始热榜内容

  hotList = await page.$$eval('.home-hotboard .news-title', items =>
  items.map(item => item.textContent.trim())  // 获取每个热榜项的文本内容
);
console.log('初始热榜内容:');
console.log(hotList);

使用 $$eval 方法,我们选择了热榜区域内的所有 .news-title 元素,并输出它们的文本内容(即热榜的标题)。

点击“换一换”按钮并等待新内容加载

const changeButton = await page.$('.refresh');  // 找到“换一换”按钮
if (changeButton) {
  await changeButton.click();  // 点击按钮刷新热榜
}

await page.waitForSelector('.home-hotboard');  // 等待热榜区域重新加载

获取刷新后的热榜内容

  hotList = await page.$$eval('.home-hotboard .news-title', items =>
  items.map(item => item.textContent.trim())  // 获取新热榜项的文本内容
);
console.log('刷新后的热榜内容:');
console.log(hotList);

再次使用 $$eval 方法获取刷新后的热榜项内容,并将其输出到控制台。

关闭浏览器

await browser.close();  // 关闭浏览器

3.3 步骤 3:代码运行

在终端中运行以下命令来执行脚本:

node crawler.js

运行后,脚本会打印出 初始热榜内容 和 刷新后的热榜内容。

梳理

通过本教程,你学会了如何使用 Playwright 爬取动态加载的内容。在实际项目中,很多网站的数据都依赖于 JavaScript 动态渲染和异步加载,掌握如何处理这些动态内容,对于编写有效的爬虫非常重要。

4. 数据存储的需求

在爬取数据后,通常会有以下几种存储方式:

  • • JSON 格式:适合存储结构化的数据,并且便于与其他系统交换。

  • • CSV 格式:适用于存储表格数据,常用于数据分析和处理。

  • • SQLite 数据库:适用于需要长期存储、查询的数据,适合小型的本地数据库需求。
    我们将依次展示如何将爬取的数据保存为 JSONCSV 文件,并将其插入 SQLite 数据库中。

5. 完整代码实现

5.1 环境准备

首先,我们从 头条热榜 爬取数据,并将其分别保存为 JSONCSV 和 SQLite。首先确保你已经安装了 Playwright

npm install playwright

并且安装了 json2csv 的 Node.js 库:

npm install json2csv

并且安装了 SQLite 的 Node.js 库:

npm install sqlite3

还可以通过以下命令确认是否正确按照

npm list json2csv

5.2 完整代码

const { chromium } = require('playwright');
const fs = require('fs');
const { Parser } = require('json2csv');
const sqlite3 = require('sqlite3').verbose();

// 设置最大循环次数,避免无限点击
const MAX_CLICKS = 5;

(async () => {
  const browser = await chromium.launch({ headless: false });  // 启动浏览器
  const page = await browser.newPage();  // 新建一个页面

  // 打开今日头条网站
  await page.goto('https://toutiao.com');  

  // 等待热榜区域加载完成
  await page.waitForSelector('.home-hotboard');  // 热榜区域的容器(CSS 选择器)

  // 用来记录已经爬取的热榜内容,避免重复
  let allHotList = new Set();

  // 点击“换一换”按钮并获取新的热榜内容
  for (let i = 0; i < MAX_CLICKS; i++) {
    // 获取热榜内容
    let hotList = await page.$$eval('.home-hotboard .news-title', items =>
      items.map(item => item.textContent.trim())  // 获取每个热榜项的文本内容
    );

    console.log(`第 ${i + 1} 次刷新后的热榜内容:`);
    console.log(hotList);

    // 去重:将本次内容添加到 Set 中,自动去重
    hotList.forEach(item => allHotList.add(item));

    // 点击“换一换”按钮
    const changeButton = await page.$('.refresh');  // 找到“换一换”按钮
    if (changeButton) {
      await changeButton.click();  // 点击按钮刷新热榜
    }

    // 等待新的热榜内容加载完成
    await page.waitForSelector('.home-hotboard');  // 等待热榜区域重新加载
  }

  // 将去重后的热榜内容转为数组
  allHotList = Array.from(allHotList);

  // 保存到 JSON 文件
  saveToJson(allHotList);

  // 保存到 CSV 文件
  saveToCsv(allHotList);

  // 保存到 SQLite 数据库
  saveToSQLite(allHotList);

  await browser.close();  // 关闭浏览器

})();

// 保存为 JSON 文件
function saveToJson(data) {
  const filePath = './hotlist.json';
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
  console.log(`数据已保存到 ${filePath}`);
}

// 保存为 CSV 文件
function saveToCsv(data) {
  if (data.length === 0) {
    console.log('数据为空,无法保存为 CSV 文件');
    return;
  }

  const filePath = './hotlist.csv';

  // 定义 CSV 文件的字段(列名)
  const fields = ['title'];  // 这里只需要定义一个字段名称,假设每个项是字符串

  const csvParser = new Parser({ fields });

  // 转换数据为 CSV 格式
  const csv = csvParser.parse(data.map(item => ({ title: item })));  // 将数据转换为对象数组

  // 保存为 CSV 文件
  fs.writeFileSync(filePath, csv, 'utf-8');
  console.log(`数据已保存到 ${filePath}`);
}

// 保存到 SQLite 数据库
function saveToSQLite(data) {
  const db = new sqlite3.Database('./hotlist.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
    if (err) {
      console.error('数据库打开失败', err);
    } else {
      console.log('数据库打开成功');
    }
  });

  // 创建热榜表(如果不存在的话)
  db.run(`CREATE TABLE IF NOT EXISTS hotlist (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT UNIQUE,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
  )`, (err) => {
    if (err) {
      console.error('创建表失败', err);
    } else {
      console.log('热榜表已创建或已存在');
    }
  });

  // 插入数据,确保不重复
  const stmt = db.prepare('INSERT OR IGNORE INTO hotlist (title, timestamp) VALUES (?, ?)');
  const currentTimestamp = new Date().toISOString(); // 获取当前时间戳,ISO 格式

  data.forEach((item) => {
    stmt.run(item, currentTimestamp, (err) => {
      if (err) {
        console.error('插入数据失败', err);
      } else {
        console.log('插入数据成功', item);
      }
    });
  });
  stmt.finalize();  // 完成插入操作

  // 查询并打印保存的内容
  db.all('SELECT * FROM hotlist', (err, rows) => {
    if (err) {
      console.error('查询出错:', err);
    } else {
      console.log('从数据库查询到的热榜数据:');
      rows.forEach(row => {
        console.log(`${row.id}: ${row.title} (时间: ${row.timestamp})`);
      });
    }
  });

  // 关闭数据库连接
  db.close((err) => {
    if (err) {
      console.error('关闭数据库失败', err);
    } else {
      console.log('数据库连接已关闭');
    }
  });
}

5.3 代码解析

爬取头条热榜内容

以上代码使用了之前Playwright爬取页面上的热榜内容方法。先等待热榜区域加载完成,然后通过 $eval 获取每个热榜项的文本内容。

保存数据为json文件

我们创建了一个名为 saveToJson 的函数,将爬取的热榜数据保存为 JSON格式。我们使用 Node.js 的 fs.writeFileSync 方法将数据写入 hotlist.json 文件。

保存数据为csv文件

我们创建了 saveToCsv 函数,将热榜数据保存为 CSV 格式。使用了 json2csv 库,将数据转换为 CSV 格式并保存为 hotlist.csv 文件。

保存数据到SQLite数据库

我们创建了 saveToSQLite 函数,将爬取的热榜数据保存到 SQLite 数据库。我们首先使用 sqlite3 库创建数据库文件(如果不存在则创建),然后创建一个名为 hotlist 的表,并将热榜内容插入到该表中。最后,我们查询并打印了数据库中存储的内容。

5.4 代码运行

图片

保存输出内容如下

图片

总结

这篇教程,我们学会了如何将爬取的数据保存到 JSONCSV 和 SQLite 数据库中。对于爬虫项目来说,数据存储是一个非常重要的环节,而 SQLite 提供了一个轻量级的本地数据库,非常适合小型项目的数据持久化需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984981.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

rdiff-backup备份

目录 1. 服务器备份知识点 1.1 备份策略 1.2 备份步骤和宝塔面板简介 1.3 CentOS7重要目录 2. 备份工具 2.1 tar -g 备份演示 2. rsync 备份演示 3. rdiff-backup 备份演示 4. 差异和优缺点 3. rdiff-backup安装和使用 3.1 备份命令rdiff-backup 3.2 恢复命令--…

【6】字典树学习笔记

前言 WFLS 2023 寒假集训 Day2 大纲里字典树在数据结构里&#xff0c;但是个人认为应该属于字符串&#xff0c;就把它放到字符串里了 字典树 字典树&#xff0c;又称Trie树&#xff0c;字母树。每个顶点代表一个字符&#xff0c;从根节点到叶子节点的路径上所有的节点的字符…

Aim Robotics电动胶枪:机器人涂胶点胶的高效解决方案

在自动化和智能制造领域&#xff0c;机器人技术的应用越来越广泛&#xff0c;而涂胶和点胶作为生产过程中的重要环节&#xff0c;也逐渐实现了自动化和智能化。Aim Robotics作为一家专注于机器人技术的公司&#xff0c;其推出的电动胶枪为这一领域带来了高效、灵活且易于操作的…

Camel AI Owl + 阿里云QWQ 本地部署

在 Windows 环境下&#xff0c;部署 Camel AI Owl 并集成阿里云百炼 DeepSeek-R1 API。通过循序渐进的详细说明&#xff0c;你将轻松完成从环境配置到系统落地的全过程。 目录 环境准备Windows 下创建虚拟环境并安装依赖部署 Camel AI Owl配置阿里云百炼 DeepSeek-R1 API测试与…

基于django+pytorch(Faster R-CNN)的钢材缺陷识别系统

一、训练数据来源以及数据标注 数据来源于阿里云天池实验室公开数据集中的铝型材缺陷检测数据集APDDD 数据标注通过labelme进行标注&#xff0c;图片所有标注以转化为矩形标注&#xff0c;存放成json格式。 二、模型训练方式及结果 缺陷识别模型基于Faster R-CNN ResNet5…

Linux网络编程——UDP网络通信的简单实现

目录 UDP网络通信 简单的udpServer实现 头文件 封装udp服务 1、私有成员 2、构造函数和析构函数 3、 udp服务器初始化 4、udp服务器运行 5、完整代码 简单的udpClient实现 UDP实现最简单的公共聊天 完整代码 inet_ntoa()相关问题 UDP网络通信 在正式实现UDP网络通…

【Prometheus】层层解析prometheus如何监控k8s核心组件

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

【JavaEE】多线程进阶(2)

【JavaEE】多线程进阶&#xff08;2&#xff09; 一、JUC(java.util.concurrent) 的常⻅类1.1 Callable 接⼝1.2 ReentrantLock1.3 原子类原子类的特性&#xff1a;常见原子类&#xff1a;原子类的实例&#xff1a; 1.4 线程池1.5 信号量 Semaphore代码实例 1.6 CountDownLatch…

SpringBoot 如何调用 WebService 接口

前言 调用WebService接口的方式有很多&#xff0c;今天记录一下&#xff0c;使用 Spring Web Services 调用 SOAP WebService接口 一.导入依赖 <!-- Spring Boot Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId…

从Manus到OpenManus:多智能体协作框架如何重构AI生产力?

文章目录 Manus&#xff1a;封闭生态下的通用AI智能体OpenManus&#xff1a;开源社区的闪速复刻挑战与未来&#xff1a;框架落地的现实边界当前局限性未来演进方向 OpenManus使用指南1. 环境配置2. 参数配置3. 替换搜索引擎4. 运行效果 协作框架开启AI生产力革命 Manus&#xf…

1.5 双指针专题:有效三⻆形的个数(medium)

1.题目链接 611. 有效三角形的个数 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/valid-triangle-number/submissions/609232447/ 2.题目描述 给定⼀个包含⾮负整数的数组 nums &#xff0c;返回其中可以组成三⻆形三条边的三元组个数。 ⽰例 1: 输…

大数据学习(59)-DataX执行机制

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…

《面向对象程序设计-C++》实验一 熟悉Visual C++开发环境及上机过程

一、实验目的 了解和使用VC集成开发环境&#xff1b;熟悉VC环境的基本命令和功能键&#xff1b;熟悉常用的功能菜单命令&#xff1b;学习使用VC环境的帮助&#xff1b;学习完整的C程序开发过程&#xff1b;理解简单的C程序结构。 二、实验内容 使用Visual C 6.0集成环境来编…

Chebykan wx 文章阅读

文献筛选 [1] 神经网络&#xff1a;全面基础 [2] 通过sigmoid函数的超层叠近似 [3] 多层前馈网络是通用近似器 [5] 注意力是你所需要的 [6] 深度残差学习用于图像识别 [7] 视觉化神经网络的损失景观 [8] 牙齿模具点云补全通过数据增强和混合RL-GAN [9] 强化学习&#xff1a;一…

2025解决软件供应链安全,开源安全的版本答案:SCA+SBOM

GitHub&#xff1a; https://github.com/XmirrorSecurity/OpenSCA-cli/ Gitee&#xff1a; https://gitee.com/XmirrorSecurity/OpenSCA-cli/ OpenSCA官网&#xff1a; https://opensca.xmirror.cn/ 根据Sonatype 发布的《软件供应链现状》报告&#xff0c;其中强调软件供…

Linux 系统负载过高的排查思路

技术探讨&#xff1a;Linux系统负载过高的排查思路 在Linux服务器运行过程中&#xff0c;如果系统负载过高&#xff0c;可能会导致性能下降和服务不稳定。以下是针对Linux系统负载过高问题的排查思路和解决方法&#xff1a; 1. 查看系统负载&#xff1a; 使用uptime或top命令查…

typora高亮方案+鼠标侧键一键改色

引言 在typora里面有一个自定义的高亮, <mark></mark>>但是单一颜色就太难看了, 我使用人工智能, 搜索全网艺术家, 汇集了几种好看的格式,并且方便大家侧键一键 调用, 是不是太方便啦 ! 示例 午夜模式 春意盎然 深海蓝调 石墨文档 秋日暖阳 蜜桃宣言 使用方法 …

自然语言处理文本分析:从词袋模型到认知智能的进化之旅

清晨&#xff0c;当智能音箱准确识别出"播放周杰伦最新专辑"的模糊语音指令时&#xff1b;午间&#xff0c;企业舆情系统自动标记出十万条评论中的负面情绪&#xff1b;深夜&#xff0c;科研人员用GPT-4解析百万篇论文发现新材料线索——这些场景背后&#xff0c;是自…

基于SSM+Vue+uniapp的考研交流(带商城)小程序+LW示例参考

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

浙江大学:DeepSeek行业应用案例集(153页)(文末可下载PDF)

浙江大学&#xff1a;DeepSeek行业应用案例集&#xff08;153页&#xff09;&#xff08;文末可下载PDF&#xff09; 全文链接&#xff1a;浙江大学&#xff1a;DeepSeek行业应用案例集&#xff08;153页&#xff09;&#xff08;文末可下载PDF&#xff09; | AI探金 全文链接&…