摘要:爬蟲介紹二爬蟲的分類通用網絡爬蟲全網爬蟲爬行對象從一些種子擴充到整個,主要為門戶站點搜索引擎和大型服務提供商采集數據。
分分鐘教你用node.js寫個爬蟲 寫在前面
十分感謝大家的點贊和關注。其實,這是我第一次在segmentfault上寫文章。因為我也是前段時間偶然之間才開始了解和學習爬蟲,而且學習node的時間也不是很長。雖然用node做過一些后端的項目,但其實在node和爬蟲方面我還是一個新人,這篇文章主要是想和大家分享一下node和爬蟲方面的基本知識,希望對大家有幫助,也想和大家一起交流,一起學習,再次謝謝大家的支持!
對了,我開通了個人的 GitHub主頁 ,里面有自己的技術文章,還會有個人的隨想、思考和日志。以后所有的文章都會第一時間更新到這里,然后同步到其他平臺。有喜歡的朋友可以沒事去逛逛,再次感謝大家的支持!
一、什么是爬蟲網絡爬蟲(又被稱為網頁蜘蛛,網絡機器人,在FOAF社區中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。另外一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲。二、爬蟲的分類
WIKIPEDIA 爬蟲介紹
通用網絡爬蟲(全網爬蟲)
爬行對象從一些 種子URL 擴充到整個 Web,主要為門戶站點搜索引擎和大型 Web 服務提供商采集數據。
聚焦網絡爬蟲(主題網絡爬蟲)
是 指選擇性 地爬行那些與預先定義好的主題相關頁面的網絡爬蟲。
增量式網絡爬蟲
指對已下載網頁采取增量式更新和 只爬行新產生的或者已經發生變化網頁 的爬蟲,它能夠在一定程度上保證所爬行的頁面是盡可能新的頁面。
Deep Web 爬蟲
爬行對象是一些在用戶填入關鍵字搜索或登錄后才能訪問到的深層網頁信息的爬蟲。三、爬蟲的爬行策略
通用網絡爬蟲(全網爬蟲)
深度優先策略、廣度優先策略
聚焦網絡爬蟲(主題網絡爬蟲)
基于內容評價的爬行策略(內容相關性),基于鏈接結構評價的爬行策略、基于增強學習的爬行策略(鏈接重要性),基于語境圖的爬行策略(距離,圖論中兩節點間邊的權重)
增量式網絡爬蟲
統一更新法、個體更新法、基于分類的更新法、自適應調頻更新法
Deep Web 爬蟲
Deep Web 爬蟲爬行過程中最重要部分就是表單填寫,包含兩種類型:基于領域知識的表單填寫、基于網頁結構分析的表單填寫
現代的網頁爬蟲的行為通常是四種策略組合的結果:
選擇策略:決定所要下載的頁面;四、寫一個簡單網頁爬蟲的流程
重新訪問策略:決定什么時候檢查頁面的更新變化;
平衡禮貌策略:指出怎樣避免站點超載;
并行策略:指出怎么協同達到分布式抓取的效果;
確定爬取對象(網站/頁面)
分析頁面內容(目標數據/DOM結構)
確定開發語言、框架、工具等
編碼 測試,爬取數據
優化
一個簡單的百度新聞爬蟲 確定爬取對象(網站/頁面)百度新聞 (http://news.baidu.com/)分析頁面內容(目標數據/DOM結構)
······確定開發語言、框架、工具等
node.js (express) + SublimeText 3編碼,測試,爬取數據
coding ···Let"s start 新建項目目錄
1.在合適的磁盤目錄下創建項目目錄baiduNews(我的項目目錄是:F:webaiduNews)
注:因為在寫這篇文章的時候用的電腦真心比較渣。安裝WebStorm或者VsCode跑項目有些吃力。所以后面的命令行操作我都是在Window自帶的DOS命令行窗口中執行的。
初始化package.json1.在DOS命令行中進入項目根目錄 baiduNews安裝依賴
2.執行npm init,初始化package.json文件
express (使用express來搭建一個簡單的Http服務器。當然,你也可以使用node中自帶的http模塊)
superagent (superagent是node里一個非常方便的、輕量的、漸進式的第三方客戶端請求代理模塊,用他來請求目標頁面)
cheerio (cheerio相當于node版的jQuery,用過jQuery的同學會非常容易上手。它主要是用來獲取抓取到的頁面元素和其中的數據信息)
// 個人比較喜歡使用yarn來安裝依賴包,當然你也可以使用 npm install 來安裝依賴,看個人習慣。 yarn add express yarn add superagent yarn add cheerio
依賴安裝完成后你可以在package.json中查看剛才安裝的依賴是否成功。
安裝正確后如下圖:
一、使用express啟動一個簡單的本地Http服務器
1、在項目根目錄下創建index.js文件(后面都會在這個index文件中進行coding)
2、創建好index.js后,我們首先實例化一個express對象,用它來啟動一個本地監聽3000端口的Http服務。
const express = require("express"); const app = express(); // ... let server = app.listen(3000, function () { let host = server.address().address; let port = server.address().port; console.log("Your App is running at http://%s:%s", host, port); });
對,就是這么簡單,不到10行代碼,搭建啟動一個簡單的本地Http服務。
3、按照國際慣例,我們希望在訪問本機地址http://localhost:3000的時候,這個服務能給我們犯規一個Hello World!在index.js中加入如下代碼:
app.get("/", function (req, res) { res.send("Hello World!"); });
此時,在DOS中項目根目錄baiduNews下執行node index.js,讓項目跑起來。之后,打開瀏覽器,訪問http://localhost:3000,你就會發現頁面上顯示"Hellow World!"字樣。
這樣,在后面我們獲取到百度新聞首頁的信息后,就可以在訪問http://localhost:3000時看到這些信息。
二、抓取百度新聞首頁的新聞信息
1、 首先,我們先來分析一下百度新聞首頁的頁面信息。
百度新聞首頁大體上分為“熱點新聞”、“本地新聞”、“國內新聞”、“國際新聞”......等。這次我們先來嘗試抓取左側“熱點新聞”和下方的“本地新聞”兩處的新聞數據。
F12打開Chrome的控制臺,審查頁面元素,經過查看左側“熱點新聞”信息所在DOM的結構,我們發現所有的“熱點新聞”信息(包括新聞標題和新聞頁面鏈接)都在id為#pane-news的>下面下下的標簽中。用jQuery的選擇器表示為:#pane-news ul li a。
2、為了爬取新聞數據,首先我們要用superagent請求目標頁面,獲取整個新聞首頁信息
// 引入所需要的第三方包 const superagent= require("superagent"); let hotNews = []; // 熱點新聞 let localNews = []; // 本地新聞 /** * index.js * [description] - 使用superagent.get()方法來訪問百度新聞首頁 */ superagent.get("http://news.baidu.com/").end((err, res) => { if (err) { // 如果訪問失敗或者出錯,會這行這里 console.log(`熱點新聞抓取失敗 - ${err}`) } else { // 訪問成功,請求http://news.baidu.com/頁面所返回的數據會包含在res // 抓取熱點新聞數據 hotNews = getHotNews(res) } });3、獲取頁面信息后,我們來定義一個函數getHotNews()來抓取頁面內的“熱點新聞”數據。
/** * index.js * [description] - 抓取熱點新聞頁面 */ // 引入所需要的第三方包 const cheerio = require("cheerio"); let getHotNews = (res) => { let hotNews = []; // 訪問成功,請求http://news.baidu.com/頁面所返回的數據會包含在res.text中。 /* 使用cheerio模塊的cherrio.load()方法,將HTMLdocument作為參數傳入函數 以后就可以使用類似jQuery的$(selectior)的方式來獲取頁面元素 */ let $ = cheerio.load(res.text); // 找到目標數據所在的頁面元素,獲取數據 $("div#pane-news ul li a").each((idx, ele) => { // cherrio中$("selector").each()用來遍歷所有匹配到的DOM元素 // 參數idx是當前遍歷的元素的索引,ele就是當前便利的DOM元素 let news = { title: $(ele).text(), // 獲取新聞標題 href: $(ele).attr("href") // 獲取新聞網頁鏈接 }; hotNews.push(news) // 存入最終結果數組 }); return hotNews };這里要多說幾點:
async/await據說是異步編程的終級解決方案,它可以讓我們以同步的思維方式來進行異步編程。Promise解決了異步編程的“回調地獄”,async/await同時使異步流程控制變得友好而有清晰,有興趣的同學可以去了解學習一下,真的很好用。
superagent模塊提供了很多比如get、post、delte等方法,可以很方便地進行Ajax請求操作。在請求結束后執行.end()回調函數。.end()接受一個函數作為參數,該函數又有兩個參數error和res。當請求失敗,error會包含返回的錯誤信息,請求成功,error值為null,返回的數據會包含在res參數中。
cheerio模塊的.load()方法,將HTML document作為參數傳入函數,以后就可以使用類似jQuery的$(selectior)的方式來獲取頁面元素。同時可以使用類似于jQuery中的.each()來遍歷元素。此外,還有很多方法,大家可以自行Google/Baidu。
4、將抓取的數據返回給前端瀏覽器
前面,const app = express();實例化了一個express對象app。
app.get("", async() => {})接受兩個參數,第一個參數接受一個String類型的路由路徑,表示Ajax的請求路徑。第二個參數接受一個函數Function,當請求此路徑時就會執行這個函數中的代碼。/** * [description] - 跟路由 */ // 當一個get請求 http://localhost:3000時,就會后面的async函數 app.get("/", async (req, res, next) => { res.send(hotNews); });在DOS中項目根目錄baiduNews下執行node index.js,讓項目跑起來。之后,打開瀏覽器,訪問http://localhost:3000,你就會發現抓取到的數據返回到了前端頁面。我運行代碼后瀏覽器展示的返回信息如下:
注:因為我的Chrome安裝了JSONView擴展程序,所以返回的數據在頁面展示的時候會被自動格式化為結構性的JSON格式,方便查看。OK!!這樣,一個簡單的百度“熱點新聞”的爬蟲就大功告成啦!!
簡單總結一下,其實步驟很簡單:
express啟動一個簡單的Http服務
分析目標頁面DOM結構,找到所要抓取的信息的相關DOM元素
使用superagent請求目標頁面
使用cheerio獲取頁面元素,獲取目標數據
返回數據到前端瀏覽器
現在,繼續我們的目標,抓取“本地新聞”數據(編碼過程中,我們會遇到一些有意思的問題)
有了前面的基礎,我們自然而然的會想到利用和上面相同的方法“本地新聞”數據。
1、 分析頁面中“本地新聞”部分的DOM結構,如下圖:F12打開控制臺,審查“本地新聞”DOM元素,我們發現,“本地新聞”分為兩個主要部分,“左側新聞”和右側的“新聞資訊”。這所有目標數據都在id為#local_news的div中。“左側新聞”數據又在id為#localnews-focus的ul標簽下的li標簽下的a標簽中,包括新聞標題和頁面鏈接。“本地資訊”數據又在id為#localnews-zixun的div下的ul標簽下的li標簽下的a標簽中,包括新聞標題和頁面鏈接。2、OK!分析了DOM結構,確定了數據的位置,接下來和爬取“熱點新聞”一樣,按部就班,定義一個getLocalNews()函數,爬取這些數據。
/** * [description] - 抓取本地新聞頁面 */ let getLocalNews = (res) => { let localNews = []; let $ = cheerio.load(res); // 本地新聞 $("ul#localnews-focus li a").each((idx, ele) => { let news = { title: $(ele).text(), href: $(ele).attr("href"), }; localNews.push(news) }); // 本地資訊 $("div#localnews-zixun ul li a").each((index, item) => { let news = { title: $(item).text(), href: $(item).attr("href") }; localNews.push(news); }); return localNews };對應的,在superagent.get()中請求頁面后,我們需要調用getLocalNews()函數,來爬去本地新聞數據。
superagent.get()函數修改為:superagent.get("http://news.baidu.com/").end((err, res) => { if (err) { // 如果訪問失敗或者出錯,會這行這里 console.log(`熱點新聞抓取失敗 - ${err}`) } else { // 訪問成功,請求http://news.baidu.com/頁面所返回的數據會包含在res // 抓取熱點新聞數據 hotNews = getHotNews(res) localNews = getLocalNews(res) } });同時,我們要在app.get()路由中也要將數據返回給前端瀏覽器。app.get()路由代碼修改為:
/** * [description] - 跟路由 */ // 當一個get請求 http://localhost:3000時,就會后面的async函數 app.get("/", async (req, res, next) => { res.send({ hotNews: hotNews, localNews: localNews }); });編碼完成,激動不已!!DOS中讓項目跑起來,用瀏覽器訪問http://localhost:3000尷尬的事情發生了!!返回的數據只有熱點新聞,而本地新聞返回一個空數組[ ]。檢查代碼,發現也沒有問題,但為什么一直返回的空數組呢?
一個有意思的問題
經過一番原因查找,才返現問題出在哪里!!為了找到原因,首先,我們看看用superagent.get("http://news.baidu.com/").end((err, res) => {})請求百度新聞首頁在回調函數.end()中的第二個參數res中到底拿到了什么內容?// 新定義一個全局變量 pageRes let pageRes = {}; // supergaent頁面返回值 // superagent.get()中將res存入pageRes superagent.get("http://news.baidu.com/").end((err, res) => { if (err) { // 如果訪問失敗或者出錯,會這行這里 console.log(`熱點新聞抓取失敗 - ${err}`) } else { // 訪問成功,請求http://news.baidu.com/頁面所返回的數據會包含在res // 抓取熱點新聞數據 // hotNews = getHotNews(res) // localNews = getLocalNews(res) pageRes = res } }); // 將pageRes返回給前端瀏覽器,便于查看 app.get("/", async (req, res, next) => { res.send({ // {}hotNews: hotNews, // localNews: localNews, pageRes: pageRes }); });訪問瀏覽器http://localhost:3000,頁面展示如下內容:可以看到,返回值中的text字段應該就是整個頁面的HTML代碼的字符串格式。為了方便我們觀察,可以直接把這個text字段值返回給前端瀏覽器,這樣我們就能夠清晰地看到經過瀏覽器渲染后的頁面。修改給前端瀏覽器的返回值
app.get("/", async (req, res, next) => { res.send(pageRes.text) }訪問瀏覽器http://localhost:3000,頁面展示如下內容:
審查元素才發現,原來我們抓取的目標數據所在的DOM元素中是空的,里面沒有數據!
到這里,一切水落石出!在我們使用superagent.get()訪問百度新聞首頁時,res中包含的獲取的頁面內容中,我們想要的“本地新聞”數據還沒有生成,DOM節點元素是空的,所以出現前面的情況!抓取后返回的數據一直是空數組[ ]。在控制臺的Network中我們發現頁面請求了一次這樣的接口:
http://localhost:3000/widget?id=LocalNews&ajax=json&t=1526295667917,接口狀態 404。
這應該就是百度新聞獲取“本地新聞”的接口,到這里一切都明白了!“本地新聞”是在頁面加載后動態請求上面這個接口獲取的,所以我們用superagent.get()請求的頁面再去請求這個接口時,接口URL中hostname部分變成了本地IP地址,而本機上沒有這個接口,所以404,請求不到數據。找到原因,我們來想辦法解決這個問題!!
直接使用superagent訪問正確合法的百度“本地新聞”的接口,獲取數據后返回給前端瀏覽器。
使用第三方npm包,模擬瀏覽器訪問百度新聞首頁,在這個模擬瀏覽器中當“本地新聞”加載成功后,抓取數據,返回給前端瀏覽器。
以上方法均可,我們來試試比較有意思的第二種方法
使用Nightmare自動化測試工具Electron可以讓你使用純JavaScript調用Chrome豐富的原生的接口來創造桌面應用。你可以把它看作一個專注于桌面應用的Node.js的變體,而不是Web服務器。其基于瀏覽器的應用方式可以極方便的做各種響應式的交互安裝依賴Nightmare是一個基于Electron的框架,針對Web自動化測試和爬蟲,因為其具有跟PlantomJS一樣的自動化測試的功能可以在頁面上模擬用戶的行為觸發一些異步數據加載,也可以跟Request庫一樣直接訪問URL來抓取數據,并且可以設置頁面的延遲時間,所以無論是手動觸發腳本還是行為觸發腳本都是輕而易舉的。
// 安裝nightmare yarn add nightmare為獲取“本地新聞”,繼續coding...給index.js中新增如下代碼:
const Nightmare = require("nightmare"); // 自動化測試包,處理動態頁面 const nightmare = Nightmare({ show: true }); // show:true 顯示內置模擬瀏覽器 /** * [description] - 抓取本地新聞頁面 * [nremark] - 百度本地新聞在訪問頁面后加載js定位IP位置后獲取對應新聞, * 所以抓取本地新聞需要使用 nightmare 一類的自動化測試工具, * 模擬瀏覽器環境訪問頁面,使js運行,生成動態頁面再抓取 */ // 抓取本地新聞頁面 nightmare .goto("http://news.baidu.com/") .wait("div#local_news") .evaluate(() => document.querySelector("div#local_news").innerHTML) .then(htmlStr => { // 獲取本地新聞數據 localNews = getLocalNews(htmlStr) }) .catch(error => { console.log(`本地新聞抓取失敗 - ${error}`); })修改getLocalNews()函數為:
/** * [description]- 獲取本地新聞數據 */ let getLocalNews = (htmlStr) => { let localNews = []; let $ = cheerio.load(htmlStr); // 本地新聞 $("ul#localnews-focus li a").each((idx, ele) => { let news = { title: $(ele).text(), href: $(ele).attr("href"), }; localNews.push(news) }); // 本地資訊 $("div#localnews-zixun ul li a").each((index, item) => { let news = { title: $(item).text(), href: $(item).attr("href") }; localNews.push(news); }); return localNews }修改app.get("/")路由為:
/** * [description] - 跟路由 */ // 當一個get請求 http://localhost:3000時,就會后面的async函數 app.get("/", async (req, res, next) => { res.send({ hotNews: hotNews, localNews: localNews }) });此時,DOS命令行中重新讓項目跑起來,瀏覽器訪問https://localhost:3000,看看頁面展示的信息,看是否抓取到了“本地新聞”數據!至此,一個簡單而又完整的抓取百度新聞頁面“熱點新聞”和“本地新聞”的爬蟲就大功告成啦!!
最后總結一下,整體思路如下:
完整代碼express啟動一個簡單的Http服務
分析目標頁面DOM結構,找到所要抓取的信息的相關DOM元素
使用superagent請求目標頁面
動態頁面(需要加載頁面后運行JS或請求接口的頁面)可以使用Nightmare模擬瀏覽器訪問
使用cheerio獲取頁面元素,獲取目標數據
爬蟲完整代碼GitHub地址:完整代碼后面,應該還會做一些進階,來爬取某些網站上比較好看的圖片(手動滑稽),會牽扯到并發控制和反-反爬蟲的一些策略。再用爬蟲取爬去一些需要登錄和輸入驗證碼的網站,歡迎到時大家關注和指正交流。
我想說再次感謝大家的點贊和關注和評論,謝謝大家的支持,謝謝!我自己覺得我算是一個愛文字,愛音樂,同時也喜歡coding的半文藝程序員。之前也一直想著寫一寫技術性和其他偏文學性的文章。雖然自己的底子沒有多么優秀,但總是覺得在寫文章的過程中,不論是技術性的還是偏文學性的,這個過程中可以督促自己去思考,督促自己去學習和交流。畢竟每天忙忙碌碌之余,還是要活出自己不一樣的生活。所以,以后如果有一些好的文章我會積極和大家分享!再次感謝大家的支持!文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94878.html
摘要:可以通過傳入待刪除數組元素組成的數組進行一次性刪除。如果后臺返回的為表示登錄的已失效,需要重新執行。等所有的異步執行完畢后,再執行回調函數。回調函數的參數是每個函數返回數據組成的數組。 其實在早之前,就做過立馬理財的銷售額統計,只不過是用前端js寫的,需要在首頁的console調試面板里粘貼一段代碼執行,點擊這里。主要是通過定時爬取https://www.lmlc.com/s/web/...
摘要:前端日報精選專題之跟著學節流冴羽的博客全家桶仿微信項目,支持多人在線聊天和機器人聊天騰訊前端團隊社區編碼的奧秘模塊實現入門淺析知乎專欄前端每周清單發布新版本提升應用性能的方法中文寇可往吾亦可往用實現對決支付寶的微信企業付款到零 2017-06-20 前端日報 精選 JavaScript專題之跟著 underscore 學節流 - 冴羽的JavaScript博客 - SegmentFau...
摘要:我們的目標是用爬蟲來干一件略污事情最近聽說煎蛋上有好多可愛的妹子,而且爬蟲從妹子圖抓起練手最好,畢竟動力大嘛。服務器超載尤其是對給定服務器的訪問過高時。個人爬蟲,如果過多的人使用,可能導致網絡或者服務器阻塞。 我們的目標是用爬蟲來干一件略污事情 最近聽說煎蛋上有好多可愛的妹子,而且爬蟲從妹子圖抓起練手最好,畢竟動力大嘛。而且現在網絡上的妹子很黃很暴力,一下接受太多容易營養不量,但是本著...
摘要:實際前端開發中我們常常需要模擬數據市場上有許多工具提供使用但是基本都是提供數據展示如果我們想要一個具備增刪改查功能的接口怎么辦呢當然是強大自己啦首先我們需要新建一個目錄安裝依賴及以上版本,首先確認版本在以上,版本低的請自行搞定寫個試 實際前端開發中我們常常需要模擬數據,市場上有許多工具提供使用但是基本都是提供數據展示,如果我們想要一個具備增刪改查功能的接口怎么辦呢?當然是強大自己啦!!...
摘要:實際前端開發中我們常常需要模擬數據市場上有許多工具提供使用但是基本都是提供數據展示如果我們想要一個具備增刪改查功能的接口怎么辦呢當然是強大自己啦首先我們需要新建一個目錄安裝依賴及以上版本,首先確認版本在以上,版本低的請自行搞定寫個試 實際前端開發中我們常常需要模擬數據,市場上有許多工具提供使用但是基本都是提供數據展示,如果我們想要一個具備增刪改查功能的接口怎么辦呢?當然是強大自己啦!!...
閱讀 2664·2021-11-18 10:02
閱讀 3426·2021-09-22 15:50
閱讀 2358·2021-09-06 15:02
閱讀 3577·2019-08-29 16:34
閱讀 1745·2019-08-29 13:49
閱讀 1275·2019-08-29 13:29
閱讀 3629·2019-08-28 18:08
閱讀 2937·2019-08-26 11:52