Skip to main content
Version: 3.6

第一个爬虫

在这节课中,你将构建你的第一个网络爬虫。但在开始之前,让我们简要介绍一下涉及到该过程的Crawlee类。

Crawlee的工作原理

Crawlee中有3个主要的爬虫类可供使用。 CheerioCrawlerPuppeteerCrawlerPlaywrightCrawler。我们稍后会讨论它们之间的区别。现在,让我们谈谈它们共同拥有的特点。

每个网络爬虫的基本思想是访问一个网页,打开它,在页面上执行一些操作,保存一些结果,然后继续到下一个页面,并重复这个过程直到爬虫完成其工作。因此,网络爬虫总是需要解决两个问题:我应该去哪里?我应该在那里做什么? 解决这两个问题,我们需要提供必要的设置。至于其他,爬虫都有适合的默认值。

爬什么 - Request and RequestQueue

所有的网络爬虫都使用Request类的实例来确定它们需要向哪里发出请求。每个请求可能包含大量信息,但至少必须包含一个URL —— 一个要打开的网页。但是只有一个URL对于爬行来说是意义不大。有时你可能拥有自己想访问的预先存在的URL列表,也许有一千个。甚至有时候,你需要在爬行过程中动态构建这个列表,并不断向列表添加更多URL。大多数情况下,你会同时出现这两种情况。

所有的请求存储在RequestQueue中,这是一个包含了Request实例的动态队列。你可以用起始URL来初始化,并且在爬虫运行时不断添加更多的请求。这就让爬虫先打开一个页面,然后不断将你感兴趣的URL,例如同一域名下的其他页面链接,添加到队列(称为 enqueuing ) ,并重复此过程来构建几乎无限数量的URL队列。

做什么 - requestHandler

requestHandler中,你告诉爬虫在访问每个页面时该做什么。你可以使用它从页面提取数据、处理数据、保存数据、调用API、进行计算等操作。

requestHandler是一个用户定义的函数,由爬虫自动调用每个来自队列RequestQueueRequest。它总是返回一个参数 —— 一个CrawlingContext。其属性根据使用的爬虫类而变化,但它始终包括 request 属性,该属性代表当前被抓取的URL和相关元数据。

构建一个网络爬虫

让我们把理论付诸实践,从一些简单的例子开始。访问一个页面并获取其HTML标题。在本教程中,你将爬取Crawlee网站https://crawlee.dev,但代码也同样适用于任何网站。

info

我们在示例中使用了一个名为Top-level await的JavaScript功能。要能够使用它,你可能需要一些额外的设置。换句话说,它需要使用ECMAScript模块,这意味着你需要将"type": "module"添加到你的package.json文件中,或者为你的文件使用*.mjs扩展名。此外,如果你在一个TypeScript项目中,则需要将编译器选项设置为 ES2022 或更高版本。

将请求添加到爬取队列

之前学到爬虫要使用一个请求队列作为其要抓取的URL来源。让我们来创建一个队列,并添加第一个请求。

src/main.js
import { RequestQueue } from 'crawlee';

// 首先,你需要创建请求队列实例。
const requestQueue = await RequestQueue.open();
// 然后你可以添加一个或多个请求。
await requestQueue.addRequest({ url: 'https://crawlee.dev' });

函数 requestQueue.addRequest() 会自动将带有 url 字符串的对象转换为 Request 实例。 现在你有一个 requestQueue 队列,包含一个指向 https://crawlee.dev 的请求。

tip

以上的代码是为了说明请求队列概念。很快你将会学习到crawler.addRequests()方法,它允许你跳过这个初始化代码,并且支持添加批量的请求。

构建一个 CheerioCrawler

Crawlee有三个主要的爬虫类:CheerioCrawlerPuppeteerCrawlerPlaywrightCrawler。你可以在快速开始课程中阅读它们的简单介绍。

除非你有充分的理由选择其他方式开始,否则你应该首先尝试构建一个CheerioCrawler。它是一个带有HTTP2支持、防屏蔽功能和集成HTML解析器 —— Cheerio 的HTTP爬虫。它快速、简单、运行成本低,并且不需要复杂的依赖关系。唯一的缺点是对于需要JavaScript渲染的网站无法直接使用。但实际上,你可能根本不需要JavaScript渲染,因为许多现代网站都采用服务器端渲染。

让我们继续之前的 RequestQueue 示例。

src/main.js
// 引入 CheerioCrawler
import { RequestQueue, CheerioCrawler } from 'crawlee';

const requestQueue = await RequestQueue.open();
await requestQueue.addRequest({ url: 'https://crawlee.dev' });

// 创建爬虫,使用我们的URL队列
// 用一个requestHandler来处理页面
const crawler = new CheerioCrawler({
requestQueue,
// `$` 参数是 Cheerio 对象
// 并包含了网站上要解析的HTML
async requestHandler({ $, request }) {
// 使用Cheerio提取<title>文本。
// 请查看 Cheerio 文档以获取 API 文档。
const title = $('title').text();
console.log(`The title of "${request.url}" is: ${title}.`);
}
})

// 启动爬虫并等待其完成。
await crawler.run();

当你运行示例时,你将在日志中看到 https://crawlee.dev 的标题打印出来。实际发生的是 CheerioCrawler 首先向 https://crawlee.dev 发送 HTTP 请求,然后使用 Cheerio 解析接收到的HTML,并将其作为 requestHandler$ 参数可用。

The title of "https://crawlee.dev" is: Crawlee · The scalable web crawling, scraping and automation library for JavaScript/Node.js | Crawlee.

更快地添加请求

早些时候我们提到过会使用crawler.addRequests()方法来跳过请求队列的初始化。很简单。每个爬虫都有一个隐式的RequestQueue实例,你可以使用crawler.addRequests()方法向其添加请求。事实上,你甚至可以更加简化,只需使用crawler.run()的第一个参数!

src/main.js
// 你不再需要导入RequestQueue
import { CheerioCrawler } from 'crawlee';

const crawler = new CheerioCrawler({
async requestHandler({ $, request }) {
const title = $('title').text();
console.log(`The title of "${request.url}" is: ${title}.`);
}
})

// 提供URL直接启动爬虫
await crawler.run(['https://crawlee.dev']);

当你运行此代码时,你将看到与之前的示例完全相同的输出。其实RequestQueue 仍然存在,只是由爬虫自动管理。

下一节

在下一课中你将学习如何爬取更多链接。这意味着在你爬取的页面上找到新的URL,并将它们不断添加到 RequestQueue 中,供爬虫访问。