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
数据库:适用于需要长期存储、查询的数据,适合小型的本地数据库需求。
我们将依次展示如何将爬取的数据保存为JSON
、CSV
文件,并将其插入SQLite
数据库中。
5. 完整代码实现
5.1 环境准备
首先,我们从 头条热榜 爬取数据,并将其分别保存为 JSON
、CSV
和 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
文件
我们创建了一个名为 saveToJso
n 的函数,将爬取的热榜数据保存为 JSON
格式。我们使用 Node.js
的 fs.writeFileSync
方法将数据写入 hotlist.json
文件。
保存数据为csv
文件
我们创建了 saveToCsv
函数,将热榜数据保存为 CSV
格式。使用了 json2csv
库,将数据转换为 CSV
格式并保存为 hotlist.csv
文件。
保存数据到SQLite
数据库
我们创建了 saveToSQLite
函数,将爬取的热榜数据保存到 SQLite
数据库。我们首先使用 sqlite3
库创建数据库文件(如果不存在则创建),然后创建一个名为 hotlist
的表,并将热榜内容插入到该表中。最后,我们查询并打印了数据库中存储的内容。
5.4 代码运行
保存输出内容如下
总结
这篇教程,我们学会了如何将爬取的数据保存到 JSON
、CSV
和 SQLite
数据库中。对于爬虫项目来说,数据存储是一个非常重要的环节,而 SQLite
提供了一个轻量级的本地数据库,非常适合小型项目的数据持久化需求。