整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

HTML链接及实例

TML 使用超级链接与网络上的另一个文档相连。

几乎可以在所有的网页中找到链接。点击链接可以从一张页面跳转到另一张页面。

实例

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<p>

<a href="/index.html">本文本</a> 是一个指向本网站中的一个页面的链接。</p>

<p><a href="http://www.microsoft.com/">本文本</a> 是一个指向万维网上的页面的链接。</p>

</body>

</html>

[/demo]

本例演示如何在 HTML 文档中创建链接。

将图像作为链接

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<p>

您也可以使用图像来作链接:

<a href="/example/html/lastpage.html">

<img border="0" src="./imagecopy1234567890/test.gif" />

</a>

</p>

</body>

</html>

[/demo]

本例演示如何使用图像作为链接。

(可以在本页底端找到更多实例)

HTML 超链接(链接)

超链接可以是一个字,一个词,或者一组词,也可以是一幅图像,您可以点击这些内容来跳转到新的文档或者当前文档中的某个部分。

当您把鼠标指针移动到网页中的某个链接上时,箭头会变为一只小手。

我们通过使用 <a> 标签在 HTML 中创建链接。

有两种使用 <a> 标签的方式:

通过使用 href 属性 - 创建指向另一个文档的链接

通过使用 name 属性 - 创建文档内的书签

什么是超文本?

标记语言的真正威力在于其收集能力,它可以将收集来的文档组合成一个完整的信息库,并且可以将文档库与世界上的其他文档集合链接起来。

这样的话,读者不仅可以完全控制文档在屏幕上的显示,还可以通过超链接来控制浏览信息的顺序。这就是 HTML 和 XHTML 中的 “HT” - 超文本(hypertext),就是它将整个 Web 网络连接起来。

超文本的基础知识

超文本的基本特征就是可以超链接文档;你可以指向其他位置,该位置可以在当前的文档中、局域网中的其他文档,也可以在因特网上的任何位置的文档中。这些文档组成了一个杂乱的信息网。目标文档通常与其来源有某些关联,并且丰富了来源;来源中的链接元素则将这种关系传递给浏览者。

超链接可以用于各种效果。超链接可以用在目录和主题列表中。浏览者可以在浏览器屏幕上单击鼠标或在键盘上按下按键,从而选择并自动跳转到文档中自己感兴趣的那个主题,或跳转到世界上某处完全不同的集合中的某个文档。

超链接还可以向浏览者指出有关文档中某个主题的更多信息。例如,“如果您想了解更详细的信息,请参阅某某页面。”。作者可以使用超链接来减少重复信息。例如,我们建议创作者在每个文档中都签署上自己的姓名。这样就可以使用一个将名字和另一个包含地址、电话号码等信息的单独文档链接起来的超链接,而不必在每个文档中都包含完整的联系信息。

超链接(hyper text),或者按照标准叫法称为锚(anchor),是使用 <a> 标签标记的,可以用两种方式表示。锚的一种类型是在文档中创建一个热点,当用户激活或选中(通常是使用鼠标)这个热点时,会导致浏览器进行链接。浏览器会自动加载并显示同一文档或其他文档中的某个部分,或触发某些与因特网服务相关的操作,例如发送电子邮件或下载特殊文件等。锚的另一种类型会在文档中创建一个标记,该标记可以被超链接引用。

还有一些与超链接相关联的鼠标相关事件。这些事件与 JavaScript 结合使用可以产生一些令人激动的效果。

注释

锚的这两种类型都使用同样的标签;也许这就是它们拥有同样的名称的原因。但是我们发现,如果将它们区分开,把提供热点和超链接地址的锚看作“链接”,而用于标记文档的目标部分的锚称为“锚”,那么您将更容易理解这两种类型的锚。

HTML 链接语法

链接的 HTML 代码很简单。它类似这样:

<a href="url">Link text</a>

href 属性规定链接的目标。

开始标签和结束标签之间的文字被作为超级链接来显示。

实例

<a href="http://www.w3school.com.cn/">Visit W3School</a>

上面这行代码显示为:Visit W3School

点击这个超链接会把用户带到 W3School 的首页。

提示:"链接文本" 不必一定是文本。图片或其他 HTML 元素都可以成为链接。

HTML 链接 - target 属性

使用 Target 属性,你可以定义被链接的文档在何处显示。

下面的这行会在新窗口打开文档:

<a href="http://www.w3school.com.cn/" target="_blank">Visit W3School!</a>

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<a href="http://www.w3school.com.cn/" target="_blank">Visit W3School!</a>

<p>如果把链接的 target 属性设置为 "_blank",该链接会在新窗口中打开。</p>

</body>

</html>

[/demo]

HTML 链接 - name 属性

name 属性规定锚(anchor)的名称。

您可以使用 name 属性创建 HTML 页面中的书签。

书签不会以任何特殊方式显示,它对读者是不可见的。

当使用命名锚(named anchors)时,我们可以创建直接跳至该命名锚(比如页面中某个小节)的链接,这样使用者就无需不停地滚动页面来寻找他们需要的信息了。

命名锚的语法:

<a name="label">锚(显示在页面上的文本)</a>

提示:锚的名称可以是任何你喜欢的名字。

提示:您可以使用 id 属性来替代 name 属性,命名锚同样有效。

实例

首先,我们在 HTML 文档中对锚进行命名(创建一个书签):

<a name="tips">基本的注意事项 - 有用的提示</a>

然后,我们在同一个文档中创建指向该锚的链接:

<a href="#tips">有用的提示</a>

您也可以在其他页面中创建指向该锚的链接:

<a href="http://www.w3school.com.cn/html/html_links.asp#tips">有用的提示</a>

在上面的代码中,我们将 # 符号和锚名称添加到 URL 的末端,就可以直接链接到 tips 这个命名锚了。

基本的注意事项 - 有用的提示:

注释:请始终将正斜杠添加到子文件夹。假如这样书写链接:href="http://www.w3school.com.cn/html",就会向服务器产生两次 HTTP 请求。这是因为服务器会添加正斜杠到这个地址,然后创建一个新的请求,就像这样:href="http://www.w3school.com.cn/html/"。

提示:命名锚经常用于在大型文档开始位置上创建目录。可以为每个章节赋予一个命名锚,然后把链接到这些锚的链接放到文档的上部。如果您经常访问百度百科,您会发现其中几乎每个词条都采用这样的导航方式。

提示:假如浏览器找不到已定义的命名锚,那么就会定位到文档的顶端。不会有错误发生。

更多实例

在新的浏览器窗口打开链接

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<a href="http://www.w3school.com.cn/" target="_blank">Visit W3School!</a>

<p>如果把链接的 target 属性设置为 "_blank",该链接会在新窗口中打开。</p>

</body>

</html>

[/demo]

本例演示如何在新窗口打开一个页面,这样的话访问者就无需离开你的站点了。

链接到同一个页面的不同位置

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<p>

<a href="#C4">查看 Chapter 4。</a>

</p>

<h2>Chapter 1</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 2</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 3</h2>

<p>This chapter explains ba bla bla</p>

<h2><a name="C4">Chapter 4</a></h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 5</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 6</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 7</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 8</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 9</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 10</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 11</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 12</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 13</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 14</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 15</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 16</h2>

<p>This chapter explains ba bla bla</p>

<h2>Chapter 17</h2>

<p>This chapter explains ba bla bla</p>

</body>

</html>

[/demo]

本例演示如何使用链接跳转至文档的另一个部分

跳出框架

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<p>被锁在框架中了吗?</p>

<a href="/index.html"

target="_top">请点击这里!</a>

</body>

</html>

[/demo]

本例演示如何跳出框架,假如你的页面被固定在框架之内。

创建电子邮件链接

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<p>

这是邮件链接:

<a href="mailto:someone@microsoft.com?subject=Hello%20again">发送邮件</a>

</p>

<p>

<b>注意:</b>应该使用 %20 来替换单词之间的空格,这样浏览器就可以正确地显示文本了。

</p>

</body>

</html>

[/demo]

本例演示如何链接到一个邮件。(本例在安装邮件客户端程序后才能工作。)

创建电子邮件链接 2

[demo]

<html>

<head>

<meta charset="UTF-8">

</head>

<body>

<p>

这是另一个 mailto 链接:

<a href="mailto:someone@microsoft.com?cc=someoneelse@microsoft.com&bcc=andsomeoneelse2@microsoft.com&subject=Summer%20Party&body=You%20are%20invited%20to%20a%20big%20summer%20party!">发送邮件!</a>

</p>

<p>

<b>注意:</b>应该使用 %20 来替换单词之间的空格,这样浏览器就可以正确地显示文本了。

</p>

</body>

</html>

[/demo]

本例演示更加复杂的邮件链接。

HTML 链接标签

标签 描述

<a> 定义锚。

HTML 中的脚本必须位于 <script> 与 </script> 标签之间。

脚本可被放置在 HTML 页面的 <body> 和 <head> 部分中。

<script> 标签

如需在 HTML 页面中插入 JavaScript,请使用 <script> 标签。

<script> 和 </script> 会告诉 JavaScript 在何处开始和结束。

<script> 和 </script> 之间的代码行包含了 JavaScript:

<script>

alert("我的第一个 JavaScript");

</script>

您无需理解上面的代码。只需明白,浏览器会解释并执行位于 <script> 和 </script>之间的 JavaScript 代码

那些老旧的实例可能会在 <script> 标签中使用 type="text/javascript"。现在已经不必这样做了。JavaScript 是所有现代浏览器以及 HTML5 中的默认脚本语言。

<body> 中的 JavaScript

在本例中,JavaScript 会在页面加载时向 HTML 的 <body> 写文本:

实例

<!DOCTYPE html>

<html>

<body>

.

.

<script>

document.write("<h1>这是一个标题</h1>");

document.write("<p>这是一个段落</p>");

</script>

.

.

</body>

</html>

JavaScript 函数和事件

上面例子中的 JavaScript 语句,会在页面加载时执行。

通常,我们需要在某个事件发生时执行代码,比如当用户点击按钮时。

如果我们把 JavaScript 代码放入函数中,就可以在事件发生时调用该函数。

您将在稍后的章节学到更多有关 JavaScript 函数和事件的知识。

在 <head> 或者 <body> 的JavaScript

您可以在 HTML 文档中放入不限数量的脚本。

脚本可位于 HTML 的 <body> 或 <head> 部分中,或者同时存在于两个部分中。

通常的做法是把函数放入 <head> 部分中,或者放在页面底部。这样就可以把它们安置到同一处位置,不会干扰页面的内容。

<head> 中的 JavaScript 函数

在本例中,我们把一个 JavaScript 函数放置到 HTML 页面的 <head> 部分。

该函数会在点击按钮时被调用:

实例

<!DOCTYPE html>

<html>

<head>

<script>

function myFunction()

{

document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";

}

</script>

</head>

<body>

<h1>我的 Web 页面</h1>

<p id="demo">一个段落</p>

<button type="button" onclick="myFunction()">尝试一下</button>

</body>

</html>

<body> 中的 JavaScript 函数

在本例中,我们把一个 JavaScript 函数放置到 HTML 页面的 <body> 部分。

该函数会在点击按钮时被调用:

实例

<!DOCTYPE html>

<html>

<body>

<h1>我的 Web 页面</h1>

<p id="demo">一个段落</p>

<button type="button" onclick="myFunction()">尝试一下</button>

<script>

function myFunction()

{

document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";

}

</script>

</body>

</html>

外部的 JavaScript

也可以把脚本保存到外部文件中。外部文件通常包含被多个网页使用的代码。

外部 JavaScript 文件的文件扩展名是 .js。

如需使用外部文件,请在 <script> 标签的 "src" 属性中设置该 .js 文件:

实例

<!DOCTYPE html>

<html>

<body>

<script src="myScript.js"></script>

</body>

</html>

你可以将脚本放置于 <head> 或者 <body>中 实际运行效果与您在 <script> 标签中编写脚本完全一致。

myScript.js 文件代码如下:

function myFunction(){

document.getElementById("demo").innerHTML="我的第一个 JavaScript 函数";}

如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!

文讲解怎样用 Node.js 高效地从 Web 爬取数据。

前提条件

本文主要针对具有一定 JavaScript 经验的程序员。如果你对 Web 抓取有深刻的了解,但对 JavaScript 并不熟悉,那么本文仍然能够对你有所帮助。

  • ✅ 会 JavaScript
  • ✅ 会用 DevTools 提取元素选择器
  • ✅ 会一些 ES6 (可选)

你将学到

通过本文你将学到:

  • 学到更多关于 Node.js 的东西
  • 用多个 HTTP 客户端来帮助 Web 抓取的过程
  • 利用多个经过实践考验过的库来爬取 Web

了解 Node.js

Javascript 是一种简单的现代编程语言,最初是为了向浏览器中的网页添加动态效果。当加载网站后,Javascript 代码由浏览器的 Javascript 引擎运行。为了使 Javascript 与你的浏览器进行交互,浏览器还提供了运行时环境(document、window等)。

这意味着 Javascript 不能直接与计算机资源交互或对其进行操作。例如在 Web 服务器中,服务器必须能够与文件系统进行交互,这样才能读写文件。

Node.js 使 Javascript 不仅能够运行在客户端,而且还可以运行在服务器端。为了做到这一点,其创始人 Ryan Dahl 选择了Google Chrome 浏览器的 v8 Javascript Engine,并将其嵌入到用 C++ 开发的 Node 程序中。所以 Node.js 是一个运行时环境,它允许 Javascript 代码也能在服务器上运行。

与其他语言(例如 C 或 C++)通过多个线程来处理并发性相反,Node.js 利用单个主线程并并在事件循环的帮助下以非阻塞方式执行任务。

要创建一个简单的 Web 服务器非常简单,如下所示:

const http = require('http');
const PORT = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, () => {
  console.log(`Server running at PORT:${port}/`);
});

如果你已安装了 Node.js,可以试着运行上面的代码。Node.js 非常适合 I/O 密集型程序。

HTTP 客户端:访问 Web

HTTP 客户端是能够将请求发送到服务器,然后接收服务器响应的工具。下面提到的所有工具底的层都是用 HTTP 客户端来访问你要抓取的网站。

Request

Request 是 Javascript 生态中使用最广泛的 HTTP 客户端之一,但是 Request 库的作者已正式声明弃用了。不过这并不意味着它不可用了,相当多的库仍在使用它,并且非常好用。用 Request 发出 HTTP 请求是非常简单的:

const request = require('request')
request('https://www.reddit.com/r/programming.json', function (  error,
  response,
  body) {
  console.error('error:', error)
  console.log('body:', body)
})

你可以在 Github 上找到 Request 库,安装它非常简单。你还可以在 https://github.com/request/request/issues/3142 找到弃用通知及其含义。

Axios

Axios 是基于 promise 的 HTTP 客户端,可在浏览器和 Node.js 中运行。如果你用 Typescript,那么 axios 会为你覆盖内置类型。通过 Axios 发起 HTTP 请求非常简单,默认情况下它带有 Promise 支持,而不是在 Request 中去使用回调:

const axios = require('axios')

axios
 .get('https://www.reddit.com/r/programming.json')
 .then((response) => {
  console.log(response)
 })
 .catch((error) => {
  console.error(error)
 });

如果你喜欢 Promises API 的 async/await 语法糖,那么你也可以用,但是由于顶级 await 仍处于 stage 3 ,所以我们只好先用异步函数来代替:

async function getForum() {
 try {
  const response = await axios.get(
   'https://www.reddit.com/r/programming.json'
  )
  console.log(response)
 } catch (error) {
  console.error(error)
 }
}

你所要做的就是调用 getForum!可以在 https://github.com/axios/axios 上找到Axios库。

Superagent

与 Axios 一样,Superagent 是另一个强大的 HTTP 客户端,它支持 Promise 和 async/await 语法糖。它具有像 Axios 这样相当简单的 API,但是 Superagent 由于存在更多的依赖关系并且不那么流行。

用 promise、async/await 或回调向 Superagent 发出HTTP请求看起来像这样:

const superagent = require("superagent")
const forumURL = "https://www.reddit.com/r/programming.json"

// callbacks
superagent
 .get(forumURL)
 .end((error, response) => {
  console.log(response)
 })

// promises
superagent
 .get(forumURL)
 .then((response) => {
  console.log(response)
 })
 .catch((error) => {
  console.error(error)
 })

// promises with async/await
async function getForum() {
 try {
  const response = await superagent.get(forumURL)
  console.log(response)
 } catch (error) {
  console.error(error)
 }
}

可以在 https://github.com/visionmedia/superagent 找到 Superagent。

正则表达式:艰难的路

在没有任何依赖性的情况下,最简单的进行网络抓取的方法是,使用 HTTP 客户端查询网页时,在收到的 HTML 字符串上使用一堆正则表达式。正则表达式不那么灵活,而且很多专业人士和业余爱好者都难以编写正确的正则表达式。

让我们试一试,假设其中有一个带有用户名的标签,我们需要该用户名,这类似于你依赖正则表达式时必须执行的操作

const htmlString = '<label>Username: John Doe</label>'
const result = htmlString.match(/<label>(.+)<\/label>/)

console.log(result[1], result[1].split(": ")[1])
// Username: John Doe, John Doe

在 Javascript 中,match() 通常返回一个数组,该数组包含与正则表达式匹配的所有内容。第二个元素(在索引1中)将找到我们想要的 <label> 标记的 textContentinnerHTML。但是结果中包含一些不需要的文本( “Username: “),必须将其删除。

如你所见,对于一个非常简单的用例,步骤和要做的工作都很多。这就是为什么应该依赖 HTML 解析器的原因,我们将在后面讨论。

Cheerio:用于遍历 DOM 的核心 JQuery

Cheerio 是一个高效轻便的库,它使你可以在服务器端使用 JQuery 的丰富而强大的 API。如果你以前用过 JQuery,那么将会对 Cheerio 感到很熟悉,它消除了 DOM 所有不一致和与浏览器相关的功能,并公开了一种有效的 API 来解析和操作 DOM。

const cheerio = require('cheerio')
const $ = cheerio.load('<h2 class="title">Hello world</h2>')

$('h2.title').text('Hello there!')
$('h2').addClass('welcome')

$.html()
// <h2 class="title welcome">Hello there!</h2>

如你所见,Cheerio 与 JQuery 用起来非常相似。

但是,尽管它的工作方式不同于网络浏览器,也就这意味着它不能:

  • 渲染任何解析的或操纵 DOM 元素
  • 应用 CSS 或加载外部资源
  • 执行 JavaScript

因此,如果你尝试爬取的网站或 Web 应用是严重依赖 Javascript 的(例如“单页应用”),那么 Cheerio 并不是最佳选择,你可能不得不依赖稍后讨论的其他选项。

为了展示 Cheerio 的强大功能,我们将尝试在 Reddit 中抓取 r/programming 论坛,尝试获取帖子名称列表。

首先,通过运行以下命令来安装 Cheerio 和 axios:npm install cheerio axios

然后创建一个名为 crawler.js 的新文件,并复制粘贴以下代码:

const axios = require('axios');
const cheerio = require('cheerio');

const getPostTitles = async () => {
 try {
  const { data } = await axios.get(
   'https://old.reddit.com/r/programming/'
  );
  const $ = cheerio.load(data);
  const postTitles = [];

  $('div > p.title > a').each((_idx, el) => {
   const postTitle = $(el).text()
   postTitles.push(postTitle)
  });

  return postTitles;
 } catch (error) {
  throw error;
 }
};

getPostTitles()
.then((postTitles) => console.log(postTitles));

getPostTitles() 是一个异步函数,将对旧的 reddit 的 r/programming 论坛进行爬取。首先,用带有 axios HTTP 客户端库的简单 HTTP GET 请求获取网站的 HTML,然后用 cheerio.load() 函数将 html 数据输入到 Cheerio 中。

然后在浏览器的 Dev Tools 帮助下,可以获得可以定位所有列表项的选择器。如果你使用过 JQuery,则必须非常熟悉 $('div> p.title> a')。这将得到所有帖子,因为你只希望单独获取每个帖子的标题,所以必须遍历每个帖子,这些操作是在 each() 函数的帮助下完成的。

要从每个标题中提取文本,必须在 Cheerio 的帮助下获取 DOM元素( el 指代当前元素)。然后在每个元素上调用 text() 能够为你提供文本。

现在,打开终端并运行 node crawler.js,然后你将看到大约存有标题的数组,它会很长。尽管这是一个非常简单的用例,但它展示了 Cheerio 提供的 API 的简单性质。

如果你的用例需要执行 Javascript 并加载外部源,那么以下几个选项将很有帮助。

JSDOM:Node 的 DOM

JSDOM 是在 Node.js 中使用的文档对象模型的纯 Javascript 实现,如前所述,DOM 对 Node 不可用,但是 JSDOM 是最接近的。它或多或少地模仿了浏览器。

由于创建了 DOM,所以可以通过编程与要爬取的 Web 应用或网站进行交互,也可以模拟单击按钮。如果你熟悉 DOM 操作,那么使用 JSDOM 将会非常简单。

const { JSDOM } = require('jsdom')
const { document } = new JSDOM(
 '<h2 class="title">Hello world</h2>'
).window
const heading = document.querySelector('.title')
heading.textContent = 'Hello there!'
heading.classList.add('welcome')

heading.innerHTML
// <h2 class="title welcome">Hello there!</h2>

代码中用 JSDOM 创建一个 DOM,然后你可以用和操纵浏览器 DOM 相同的方法和属性来操纵该 DOM。

为了演示如何用 JSDOM 与网站进行交互,我们将获得 Reddit r/programming 论坛的第一篇帖子并对其进行投票,然后验证该帖子是否已被投票。

首先运行以下命令来安装 jsdom 和 axios:npm install jsdom axios

然后创建名为 crawler.js的文件,并复制粘贴以下代码:

const { JSDOM } = require("jsdom")
const axios = require('axios')

const upvoteFirstPost = async () => {
  try {
    const { data } = await axios.get("https://old.reddit.com/r/programming/");
    const dom = new JSDOM(data, {
      runScripts: "dangerously",
      resources: "usable"
    });
    const { document } = dom.window;
    const firstPost = document.querySelector("div > div.midcol > div.arrow");
    firstPost.click();
    const isUpvoted = firstPost.classList.contains("upmod");
    const msg = isUpvoted
      ? "Post has been upvoted successfully!"
      : "The post has not been upvoted!";

    return msg;
  } catch (error) {
    throw error;
  }
};

upvoteFirstPost().then(msg => console.log(msg));

upvoteFirstPost() 是一个异步函数,它将在 r/programming 中获取第一个帖子,然后对其进行投票。axios 发送 HTTP GET 请求获取指定 URL 的HTML。然后通过先前获取的 HTML 来创建新的 DOM。JSDOM 构造函数把HTML 作为第一个参数,把 option 作为第二个参数,已添加的 2 个 option 项执行以下功能:

  • runScripts:设置为 dangerously 时允许执行事件 handler 和任何 Javascript 代码。如果你不清楚将要运行的脚本的安全性,则最好将 runScripts 设置为“outside-only”,这会把所有提供的 Javascript 规范附加到 “window” 对象,从而阻止在 inside 上执行的任何脚本。
  • resources:设置为“usable”时,允许加载用 <script> 标记声明的任何外部脚本(例如:从 CDN 提取的 JQuery 库)

创建 DOM 后,用相同的 DOM 方法得到第一篇文章的 upvote 按钮,然后单击。要验证是否确实单击了它,可以检查 classList 中是否有一个名为 upmod 的类。如果存在于 classList 中,则返回一条消息。

打开终端并运行 node crawler.js,然后会看到一个整洁的字符串,该字符串将表明帖子是否被赞过。尽管这个例子很简单,但你可以在这个基础上构建功能强大的东西,例如,一个围绕特定用户的帖子进行投票的机器人。

如果你不喜欢缺乏表达能力的 JSDOM ,并且实践中要依赖于许多此类操作,或者需要重新创建许多不同的 DOM,那么下面将是更好的选择。

Puppeteer:无头浏览器

顾名思义,Puppeteer 允许你以编程方式操纵浏览器,就像操纵木偶一样。它通过为开发人员提供高级 API 来默认控制无头版本的 Chrome。

Puppeteer 比上述工具更有用,因为它可以使你像真正的人在与浏览器进行交互一样对网络进行爬取。这就具备了一些以前没有的可能性:

  • 你可以获取屏幕截图或生成页面 PDF。
  • 可以抓取单页应用并生成预渲染的内容。
  • 自动执行许多不同的用户交互,例如键盘输入、表单提交、导航等。

它还可以在 Web 爬取之外的其他任务中发挥重要作用,例如 UI 测试、辅助性能优化等。

通常你会想要截取网站的屏幕截图,也许是为了了解竞争对手的产品目录,可以用 puppeteer 来做到。首先运行以下命令安装 puppeteer,:npm install puppeteer

这将下载 Chromium 的 bundle 版本,根据操作系统的不同,该版本大约 180 MB 至 300 MB。如果你要禁用此功能。

让我们尝试在 Reddit 中获取 r/programming 论坛的屏幕截图和 PDF,创建一个名为 crawler.js的新文件,然后复制粘贴以下代码:

const puppeteer = require('puppeteer')

async function getVisual() {
 try {
  const URL = 'https://www.reddit.com/r/programming/'
  const browser = await puppeteer.launch()
  const page = await browser.newPage()

  await page.goto(URL)
  await page.screenshot({ path: 'screenshot.png' })
  await page.pdf({ path: 'page.pdf' })

  await browser.close()
 } catch (error) {
  console.error(error)
 }
}

getVisual()

getVisual() 是一个异步函数,它将获 URL 变量中 url 对应的屏幕截图和 pdf。首先,通过 puppeteer.launch() 创建浏览器实例,然后创建一个新页面。可以将该页面视为常规浏览器中的选项卡。然后通过以 URL 为参数调用 page.goto() ,将先前创建的页面定向到指定的 URL。最终,浏览器实例与页面一起被销毁。

完成操作并完成页面加载后,将分别使用 page.screenshot()page.pdf() 获取屏幕截图和 pdf。你也可以侦听 javascript load 事件,然后执行这些操作,在生产环境级别下强烈建议这样做。

在终端上运行 node crawler.js ,几秒钟后,你会注意到已经创建了两个文件,分别名为 screenshot.jpgpage.pdf

Nightmare:Puppeteer 的替代者

Nightmare 是类似 Puppeteer 的高级浏览器自动化库,该库使用 Electron,但据说速度是其前身 PhantomJS 的两倍。

如果你在某种程度上不喜欢 Puppeteer 或对 Chromium 捆绑包的大小感到沮丧,那么 nightmare 是一个理想的选择。首先,运行以下命令安装 nightmare 库:npm install nightmare

然后,一旦下载了 nightmare,我们将用它通过 Google 搜索引擎找到 ScrapingBee 的网站。创建一个名为crawler.js的文件,然后将以下代码复制粘贴到其中:

const Nightmare = require('nightmare')
const nightmare = Nightmare()

nightmare
 .goto('https://www.google.com/')
 .type("input[title='Search']", 'ScrapingBee')
 .click("input[value='Google Search']")
 .wait('#rso > div:nth-child(1) > div > div > div.r > a')
 .evaluate(
  () =>
   document.querySelector(
    '#rso > div:nth-child(1) > div > div > div.r > a'
   ).href
 )
 .end()
 .then((link) => {
  console.log('Scraping Bee Web Link': link)
 })
 .catch((error) => {
  console.error('Search failed:', error)
 })

首先创建一个 Nighmare 实例,然后通过调用 goto() 将该实例定向到 Google 搜索引擎,加载后,使用其选择器获取搜索框,然后使用搜索框的值(输入标签)更改为“ScrapingBee”。完成后,通过单击 “Google搜索” 按钮提交搜索表单。然后告诉 Nightmare 等到第一个链接加载完毕,一旦完成,它将使用 DOM 方法来获取包含该链接的定位标记的 href 属性的值。

最后,完成所有操作后,链接将打印到控制台。

总结

  • Node.js 是 Javascript 在服务器端的运行时环境。由于事件循环机制,它具有“非阻塞”性质。
  • HTTP客户端(例如 Axios、Superagent 和 Request)用于将 HTTP 请求发送到服务器并接收响应。
  • CheerioJQuery 的优点抽出来,在服务器端 进行 Web 爬取是唯一的目的,但不执行 Javascript 代码。
  • JSDOM 根据标准 Javascript规范 从 HTML 字符串中创建一个 DOM,并允许你对其执行DOM操作。
  • Puppeteer and Nightmare高级(high-level )浏览器自动化库,可让你以编程方式去操作 Web 应用,就像真实的人正在与之交互一样。