摘要:編寫異步小爬蟲在通過的課程初步了解的各大模塊之后,不禁感慨于的強大,讓我們這些前端小白也可以進行進階的功能實現,同時發現自己也已經可以通過實現一些比較日常的小功能。
nodejs編寫異步小爬蟲
在通過learnyounode的課程初步了解nodejs的各大模塊之后,不禁感慨于nodejs的強大,讓我們這些前端小白也可以進行進階的功能實現,同時發現自己也已經可以通過nodejs實現一些比較日常的小功能。比如在看完fs模塊之后,我就用nodejs寫了一個批量修改文件名的小demo,還是相當好用的。技術服務于生活,這才是硬道理~
上周用nodejs寫了一個小爬蟲,但是由于當時的認知有限,爬蟲雖然工作了,但是在爬圖片的時候總是丟圖漏圖,也經常出現http請求并發太多造成鏈接超時導致爬蟲掛掉的情況。在研究別人的爬蟲代碼之后,我決定用async重寫一個爬安居客租房信息的異步爬蟲,寫下這篇筆記記錄自己的心得~
爬蟲完整代碼:https://github.com/zzuzsj/myN...
需求:利用爬蟲將安居客杭州市全部區域規定頁數內的租房信息以文件夾形式保存到本地,并將租房的圖片保存到相應文件夾里。思路整理
在寫爬蟲之前,我們需要整理我們的思路,首先我們需要分析安居客租房的網頁跳轉路徑:
租房信息分頁路徑:https://hz.zu.anjuke.com/fang...
租房信息帖子路徑:https://hz.zu.anjuke.com/fang...
租房信息帖子內房源圖片路徑:https://pic1.ajkimg.com/displ...
emmmm,事實證明,只有分頁路徑有跡可循,這也是我們爬蟲的突破點所在。
在需求中,我們需要訪問指定頁面的租房信息,規定的頁數我們可以通過node傳參傳入指定,拼接成對應分頁的url并儲存。用request模塊請求所有的分頁url,利用cheerio將頁面結構解碼之后獲取所有租房信息帖子的路徑并儲存。將所有帖子url路徑保存之后,繼續請求這些路徑,獲取到所有租房圖片的src路徑并儲存。等將所有圖片路徑儲存之后,利用request和fs在本地建立相應文件夾并將圖片下到本地。然后利用async將上訴的這些操作進行異步化,并在并發請求的時候對并發數進行限制。就醬,完美~
cheerio和async是nodejs的第三方模塊,需要先下載,在命令行中運行:
npm init
npm install cheerio async --save-dev
安裝完畢之后,我們在當前目錄下建立一個ajkSpider.js,同時建立一個rent_image文件夾,拿來存放爬蟲所爬到的信息。我們在ajkSpider.js先引用模塊:
const fs = require("fs"); const cheerio = require("cheerio"); const request = require("request"); const async = require("async");2.分頁路徑獲取
我們需要獲取所有分頁路徑,并將其存到數組里面,開始頁和結束頁通過在執行文件的時候傳參指定。例如
node ajkSpider.js 1 5
let pageArray = []; let startIndex = process.argv[2]; let endIndex = process.argv[2]; for (let i = startIndex; i < endIndex; i++) { let pageUrl = "https://hz.zu.anjuke.com/fangyuan/quanbu/p" + i; pageArray.push(pageUrl); }3.帖子路徑獲取
利用async對pageArray進行遍歷操作,獲取到url之后發起request請求,解析頁面結構,獲取所有帖子的dom節點,解析出帖子標題、價格、地段、url存到對象中,將對象存入數組以利于下一步的分析。
let topicArray = []; function saveAllPage(callback) { let pageindex = startIndex; async.map(pageArray, function (url, cb) { request({ "url": url, "method": "GET", "accept-charset": "utf-8", "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36" } }, (err, res, body) => { if (err) cb(err, null); let $ = cheerio.load(res.body.toString()); $(".zu-itemmod").each((i, e) => { let topicObj = {}; let title = $(e).find("h3").find("a").attr("title").replace(/[(s+):、,*:]/g, ""); let topicUrl = $(e).find("h3").find("a").attr("href"); let address = $(e).find("address").text().replace(//g, "").replace(/s+/g, ""); let price = $(e).find(".zu-side").find("strong").text(); let fileName = price + "_" + address + "_" + title; topicObj.fileName = fileName; topicObj.topicUrl = topicUrl; topicArray.push(topicObj); if (!fs.existsSync("./rent_image/" + fileName)) { fs.mkdirSync("./rent_image/" + fileName); } // console.log(topicObj.topicUrl + " " + topicObj.fileName + " "); }) console.log("=============== page " + pageindex + " end ============="); cb(null, "page " + pageindex); pageindex++; }); }, (err, result) => { if (err) throw err; console.log(topicArray.length); console.log(result + " finished"); console.log(" > 1 saveAllPage end"); if (callback) { callback(null, "task 1 saveAllPage"); } }) }
為了方便查看,我將帖子的標題價格地段都存了下來,并將價格+地段+貼子標題結合在一起做成了文件名。為了保證文件路徑的有效性,我對標題進行了特殊符號的去除,所以多了一串冗余的代碼,不需要的可以自行去掉。在信息獲取完畢之后,同步創建相應文件,以便于后續存放帖子內的房源圖片。代碼中的cb函數是async進行map操作所必要的內部回調,如果異步操作出錯調用 cb(err,null) 中斷操作,異步操作成功則調用 cb(null,value) ,后續代碼中的cb大致都是這個意思。在async將所有的異步操作遍歷完畢之后,會調用map后的那個回調函數,我們可以利用這個回調進行下一個異步操作。
4.房源圖片路徑獲取我們已經將所有的帖子路徑保存下來了,那么接下來我們就要獲取帖子內所有房源圖片的路徑。同樣的,我們可以參照上一步的步驟,將所有圖片路徑保存下來。但需要注意的一點是,如果帖子數量很多,用async的map函數來做request請求容易造成導致爬蟲掛掉。所以為了爬蟲的穩定,我決定用async的mapLimit函數來遍歷帖子路徑,好處是可以控制同時并發的http請求數目,讓爬蟲更加穩定,寫法也和map函數差不多,增加了第二個參數--并發數目限制。
let houseImgUrlArray = []; function saveAllImagePage(topicArray, callback) { async.mapLimit(topicArray, 10, function (obj, cb) { request({ "url": obj.topicUrl, "method": "GET", "accept-charset": "utf-8", "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36" } }, (err, res, body) => { if (err) cb(err, null); let $ = cheerio.load(res.body.toString()); let index = 0; $(".bigps").find(".picMove").find("li").find("img").each((i, e) => { index++; let imgUrlArray = {}; imgUrlArray.fileName = obj.fileName; var imgsrc = ($(e).attr("src").indexOf("default") != -1 || $(e).attr("src").length <= 0) ? $(e).attr("data-src") : $(e).attr("src"); imgUrlArray.imgsrc = imgsrc; console.log(imgUrlArray.imgsrc + " "); imgUrlArray.index = index; houseImgUrlArray.push(imgUrlArray); }); cb(null, obj.topicUrl + " "); }); }, (err, result) => { if (err) throw err; console.log(houseImgUrlArray.length); console.log(" > 2 saveAllImagePage end"); if (callback) { callback(null, "task 2 saveAllImagePage"); } }) }
由于頁面中的大圖采用了懶加載的模式,所以大部分圖片我們無法直接從dom節點的src屬性上獲取圖片路徑,變通一下,獲取dom節點的data-src屬性即可獲取到。獲取到圖片路徑之后我們就可以將其儲存,進行最后一步--下載圖片啦~
5.房源圖片下載保存圖片保存的文件夾信息已經記錄在houseImageUrlArray里了,發送請求之后我們只需要將文件寫入到對應文件夾里就行。不過我在爬蟲啟動的時候經常出現文件夾不存在導致爬蟲中斷,所以在寫入文件之前,我都檢查相應路徑是否存在,如果不存在就直接創建文件,以免爬蟲經常中斷
。下載圖片是一個較為繁重的操作,所以我們不妨將并發請求數控制的低一些,保證爬蟲穩定性。
function saveAllImage(houseImgUrlArray, callback) { async.mapLimit(houseImgUrlArray, 4, function (obj, cb) { console.log(obj); if (!fs.existsSync("./rent_image/" + obj.fileName)) { fs.mkdirSync("./rent_image/" + obj.fileName); } request({ "url": obj.imgsrc, "method": "GET", "accept-charset": "utf-8", "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36", } }).pipe(fs.createWriteStream("./rent_image/" + obj.fileName + "/" + obj.index + ".jpg").on("close", function () { cb(null, obj.title + " img respose"); })); }, (err, result) => { if (err) throw err; console.log(" > 3 saveAllImage end"); if (callback) { callback(null, "task 3 saveAllImage"); } }) }
通過這一步你就可以把帖子內房源的圖片下載到本地文件夾啦~看到這么多圖片被保存到本地,開不開心!刺不刺激!學會了你可以肆意去爬圖啦!好吧,開玩笑的,規模稍微大些的網站都會做一些反爬蟲策略。就拿安居客來說,懶加載勉強也算是一種反爬蟲的方法,更可怕的是,如果同一ip高頻率請求安居客網頁,它會要求圖片驗證碼驗證,所以有時候運行爬蟲什么東西都爬不到。至于這種高等爬蟲技巧,等以后進階再說吧,現在也是小白練手而已~
6.組織異步流程其實靠上面那些步驟通過異步回調組織一下就已經可以形成一個完整的爬蟲了。不過既然用了async,那就干脆用到底,將這些操作組織一下,讓代碼更好看、更有邏輯性,async的series方法就可以很輕易地幫我們組織好。
function startDownload() { async.series([ function (callback) { // do some stuff ... saveAllPage(process.argv[2], process.argv[3], callback); }, function (callback) { // do some more stuff ... saveAllImagePage(topicArray, callback); }, // function (callback) { // // do some more stuff ... // saveAllImageUrl(imgPageArray, callback); // }, function (callback) { // do some more stuff ... saveAllImage(houseImgUrlArray, callback); } ], // optional callback function (err, results) { // results is now equal to ["one", "two"] if (err) throw err; console.log(results + " success"); }); } startDownload();本文小結
雖然這只是一個最初級的爬蟲,沒有穩定性的保證,也沒有反爬蟲措施的破解。但是值得開心的是,它已經是可以正常運行的啦~記得寫出的第一版本的時候,雖然可以記錄帖子標題,但是圖片無論如何也是存不全的,最多存一兩百張圖爬蟲就結束了。多方參考之后,引入了async模塊,重構代碼邏輯,終于能夠存一千多張圖了,已經挺滿意了~可以說,async模塊是寫這個爬蟲收獲最多的地方了,你們也可以用一下。
學習nodejs之后,發現能做的事多了很多,很開心,同時也發現自己能做的還很少,很憂心。作為一個前端小白,不知道什么好的學習方法,但是我知道,能做一些對自己有用的東西總歸是好的。利用所學的知識服務于生活則是更好的。每個走在成長道路上的人,都該為自己打打氣,堅持走下一步。
常規性的為自己立一個下一階段的小目標:將nodejs與electron結合,寫一個具有爬蟲功能的桌面軟件~也不知道能不能完成,做了再說~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89227.html
摘要:也就是說,我的篇文章的請求對應個實例,這些實例都請求完畢后,執行以下邏輯他的目的在于對每一個返回值這個返回值為單篇文章的內容,進行方法處理。 英國人Robert Pitt曾在Github上公布了他的爬蟲腳本,導致任何人都可以容易地取得Google Plus的大量公開用戶的ID信息。至今大概有2億2千5百萬用戶ID遭曝光。 亮點在于,這是個nodejs腳本,非常短,包括注釋只有71行。 ...
摘要:本文將介紹如何使用和抓取主流的技術博客文章,然后用搭建一個小型的技術文章聚合平臺。是谷歌開源的基于和的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。相對于,是新的開源項目,而且是谷歌開發,可以使用很多新的特性。 背景 說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但...
摘要:本文將介紹如何使用和抓取主流的技術博客文章,然后用搭建一個小型的技術文章聚合平臺。是谷歌開源的基于和的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。相對于,是新的開源項目,而且是谷歌開發,可以使用很多新的特性。 背景 說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但...
摘要:很基礎,不喜勿噴轉載注明出處爬蟲實戰項目之鏈家效果圖思路爬蟲究竟是怎么實現的通過訪問要爬取的網站地址,獲得該頁面的文檔內容,找到我們需要保存的數據,進一步查看數據所在的元素節點,他們在某方面一定是有規律的,遵循規律,操作,保存數據。 說明 作為一個前端界的小學生,一直想著自己做一些項目向全棧努力。愁人的是沒有后臺,搜羅之后且學會了nodejs和express寫成本地的接口給前端頁面調用...
閱讀 1002·2023-04-25 19:35
閱讀 2660·2021-11-22 09:34
閱讀 3690·2021-10-09 09:44
閱讀 1723·2021-09-22 15:25
閱讀 2939·2019-08-29 14:00
閱讀 3374·2019-08-29 11:01
閱讀 2598·2019-08-26 13:26
閱讀 1740·2019-08-23 18:08