TML 使用超級鏈接與網絡上的另一個文檔相連。
幾乎可以在所有的網頁中找到鏈接。點擊鏈接可以從一張頁面跳轉到另一張頁面。
實例
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>
<a href="/index.html">本文本</a> 是一個指向本網站中的一個頁面的鏈接。</p>
<p><a >本文本</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 >Visit W3School</a>
上面這行代碼顯示為:Visit W3School
點擊這個超鏈接會把用戶帶到 W3School 的首頁。
提示:"鏈接文本" 不必一定是文本。圖片或其他 HTML 元素都可以成為鏈接。
HTML 鏈接 - target 屬性
使用 Target 屬性,你可以定義被鏈接的文檔在何處顯示。
下面的這行會在新窗口打開文檔:
<a target="_blank">Visit W3School!</a>
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<a 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 >有用的提示</a>
在上面的代碼中,我們將 # 符號和錨名稱添加到 URL 的末端,就可以直接鏈接到 tips 這個命名錨了。
基本的注意事項 - 有用的提示:
注釋:請始終將正斜杠添加到子文件夾。假如這樣書寫鏈接:,就會向服務器產生兩次 HTTP 請求。這是因為服務器會添加正斜杠到這個地址,然后創建一個新的請求,就像這樣:。
提示:命名錨經常用于在大型文檔開始位置上創建目錄。可以為每個章節賦予一個命名錨,然后把鏈接到這些錨的鏈接放到文檔的上部。如果您經常訪問百度百科,您會發現其中幾乎每個詞條都采用這樣的導航方式。
提示:假如瀏覽器找不到已定義的命名錨,那么就會定位到文檔的頂端。不會有錯誤發生。
更多實例
在新的瀏覽器窗口打開鏈接
[demo]
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<a 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 并不熟悉,那么本文仍然能夠對你有所幫助。
你將學到
通過本文你將學到:
了解 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> 標記的 textContent 或 innerHTML。但是結果中包含一些不需要的文本( “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 用起來非常相似。
但是,盡管它的工作方式不同于網絡瀏覽器,也就這意味著它不能:
因此,如果你嘗試爬取的網站或 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 項執行以下功能:
創建 DOM 后,用相同的 DOM 方法得到第一篇文章的 upvote 按鈕,然后單擊。要驗證是否確實單擊了它,可以檢查 classList 中是否有一個名為 upmod 的類。如果存在于 classList 中,則返回一條消息。
打開終端并運行 node crawler.js,然后會看到一個整潔的字符串,該字符串將表明帖子是否被贊過。盡管這個例子很簡單,但你可以在這個基礎上構建功能強大的東西,例如,一個圍繞特定用戶的帖子進行投票的機器人。
如果你不喜歡缺乏表達能力的 JSDOM ,并且實踐中要依賴于許多此類操作,或者需要重新創建許多不同的 DOM,那么下面將是更好的選擇。
Puppeteer:無頭瀏覽器
顧名思義,Puppeteer 允許你以編程方式操縱瀏覽器,就像操縱木偶一樣。它通過為開發人員提供高級 API 來默認控制無頭版本的 Chrome。
Puppeteer 比上述工具更有用,因為它可以使你像真正的人在與瀏覽器進行交互一樣對網絡進行爬取。這就具備了一些以前沒有的可能性:
它還可以在 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.jpg 和 page.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 屬性的值。
最后,完成所有操作后,鏈接將打印到控制臺。
總結
*請認真填寫需求信息,我們會在24小時內與您取得聯系。