摘要:前言之前初學的時候,有用爬蟲爬過一些磁力鏈接詳情見羞羞的爬蟲但是沒有并發,沒有代理,那時也對異步不是很了解所以這次又寫了個爬蟲,爬取壁紙站的所有壁紙并且爬取開心代理的條,并將有用的存進文件中用到的模塊控制并發解析庫使用代理讀寫文件其中的具
前言
之前初學node的時候,有用爬蟲爬過一些磁力鏈接
詳情見羞羞的node爬蟲
但是沒有并發,沒有代理,那時也對異步不是很了解
所以這次又寫了個爬蟲,爬取bilibili壁紙站的所有壁紙
并且爬取開心代理的100條ip,并將有用的ip存進json文件中
async (控制并發)
cheerio (解析DOM)
superagent (http庫)
superagent-proxy (使用代理)
fs (讀寫文件)
其中cheerio, superagent的具體用法見我之前的 羞羞的node爬蟲
不過之前初學,代碼寫得很難看就對了
代理ip是干嘛的
我們訪問互聯網資源時,都是用我們自己的ip(身份證)去訪問的
而爬蟲得頻繁地去獲取互聯網資源
因此如果你在某個時間點頻繁地訪問某網站的某資源
造成該網站的服務器壓力
就有可能被網站管理者禁ip, 從而訪問不了該網站
代理ip就是偽造身份去訪問
怎么檢驗ip的可用性
這里面就使用到了 superagent 的一個拓展 superagent-proxy
然后用其去訪問http://ip.chinaz.com/getip.aspx
若 3s 內能返回值,則證明該 ip 可用
const superagent = require("superagent") require("superagent-proxy")(superagent); // 寫上你先要測試的 ip,下面僅為測試ip let testIp = "http://61.178.238.122:63000"; (async function() { superagent.get("http://ip.chinaz.com/getip.aspx").proxy(testIp).timeout(3000) .end((err, res) => { if(res === undefined) { console.log("掛了"); return } if(err) { console.log("報錯啦") } console.log("成功: " + res.text) }) }())
爬取ip并存儲
首先我們先看下我們要爬取的開心代理的DOM
我們要爬取得ip地址放在tr 標簽的第一個td上
并且點擊第二頁時,鏈接變為http://www.kxdaili.com/dailiip/1/2.html#ip
鏈接上的數組表示得是頁數,也就是說我們只要改變鏈接上數字的值
就可以獲取到其他頁的html
代碼如下:
const superagent = require("superagent") const cheerio = require("cheerio") const fs = require("fs") const apiFunc = require("../common/apiFunc") // 封裝的一些讀寫api // 爬取開心代理的 ip const website = "http://www.kxdaili.com" let url = website + "/dailiip/1/" // 總執行函數 let getIp = async function() { // promise 存放的數組 let tasks = [] // 讀取 ip.js 本身存儲的ip let ips = await apiFunc.readFile("./ip.js") ips = JSON.parse(ips) for(let page = 1; page <= 10; page++) { let res = await superagent.get(url + page +".html") let $ = cheerio.load(res.text) let tr = $("tbody>tr") for(let i = 0; i < tr.length; i++) { let td = $(tr[i]).children("td") let proxy = "http://" + $(td[0]).text() + ":" + $(td[1]).text() let pro = apiFunc.filterIp(proxy) // 將所有的IP過濾Promise存入一個tasks數組中 tasks.push(pro) } } // 使用 all 等待所有ip過濾完畢后執行 寫入 ip.js過程 Promise.all(tasks).then((arr) => { // 過濾掉返回值為 undefined 的數據 let usefulIp = arr.filter((item) => { return (item !== undefined) }) ips = JSON.stringify(ips.concat(usefulIp)) console.log(ips) apiFunc.writeFile("./ip.js", ips) }) } getIp() module.exports = getIp爬取bilibili壁紙站
我們先進入bilibili壁紙站
發現有一個點擊加載更多的按鈕
如果有對前端有了解的話,我們應該知道這是通過 ajax 請求來異步獲取數據
因此我們打開開發者的NetWork
果然在 XHR 這一欄發現了一個api
里面返回的是存儲了當前頁面所有壁紙縮略圖信息的json文件
僅依靠這個json文件,我們便可以爬取所有壁紙的縮略圖
可我們要的可是高清大圖啊
于是我們隨意點擊一張縮略圖
發現它的url的參數(il_id, width, height)都來自我們之前獲取的json內的數據
也就是說我們可以拼接該鏈接來獲取到該高清圖片的鏈接,再利用cheerio來解析DOM獲取圖片地址就ok了
!!!
!!!
!!!
然而,哈哈哈哈哈哈哈哈哈哈哈哈
當我們獲取到該網頁的html后,發現該標簽內的src是空的
也就是說該也是js賦值,所以下意識又去看了NetWork的XHR
果然發現了另一個api
而高清圖片的url就是該api返回的json數據中的il_file
因此我們只需要拼接該api鏈接,再用superagent請求就可以獲取到高清圖片的url
理下思路
獲取縮略圖api返回的包含高清圖片數據的json
將1的json數據拼接到高清圖片api鏈接上,并將所有api鏈接存入數組
并發獲取2數組中的api, 獲取所有的圖片url,并將url存入數組
并發下載數組中的圖片url, 存進本地文件夾
結果在爬取bilibili壁紙站時,是不需要解析DOM的,也就是不需要使用cheerio模塊啦
代碼如下:
const superagent = require("superagent") require("superagent-proxy")(superagent); const fs = require("fs") const cheerio = require("cheerio") const async = require("async") // 獲取bilibili API的json數據 let jsonUrl = "http://h.bilibili.com/wallpaperApi?action=getOptions&page=1" let proxy = "http://218.201.98.196:3128" let getPicJson = function () { return new Promise((resolve, reject) => { superagent .get(jsonUrl) .proxy(proxy) .end((err, res) => { if (err) console.log("代理出錯啦") if (res === undefined) return if (res.statusCode == 200) { let json = JSON.parse(res.text) resolve(json) } }) }) } // 獲取高清圖片api的json數據 let dealHd = async function () { let picHd = [] let picJson = await getPicJson() let picLength = picJson.length for (let i = 1; i < picLength; i++) { let item = {} // let width = picJson[i].detail[0].width // let height = picJson[i].detail[0].height let il_id = picJson[i].detail[0].il_id item.title = picJson[i].detail[0].title item.url = `http://h.bilibili.com/wallpaperApi?action=getDetail&il_id=${il_id}` picHd.push(item) // item.url = `http://h.bilibili.com/wallpaper?action=detail&il_id=${il_id}&type=Bilibili&width=${width}&height=${height}` // picHtmlJson.push(item) } return picHd } // 獲取高清圖片的url ===== queue let dealPicJson = async function () { console.log("獲取高清圖片url,開始執行....") var concurrencyCount = 0; let result = [] let hdJson = await dealHd() return new Promise((resolve, reject) => { let q = async.queue((hDJson, callback) => { var delay = parseInt((Math.random() * 30000000) % 1000, 10); //設置延時并發爬取 concurrencyCount++; console.log("現在的并發數是", concurrencyCount, ",正在獲取的是", hDJson.title, "延遲", delay, "毫秒"); superagent.get(hDJson.url).proxy(proxy).end((err, res) => { if (err) { console.log(err); callback(null); } else { // let $ = cheerio.load(res.text) // let hdUrl = $("#wallpaper").attr("id") // console.log("鏈接是" + hdUrl) let pic = {} pic.title = hDJson.title pic.url = res.body[0].detail[0].il_file pic.format = pic.url.match(/.{3}$/)[0] // console.log(result) result.push(pic) concurrencyCount -- callback(null) } }) }, 5) q.drain = function () { resolve(result) } q.push(hdJson) }) } // 下載HD圖片 let downloadImg = async function () { console.log("開始下載圖片..."); // let folder = `Data/img-${Config.currentImgType}-${Config.startPage}-${Config.endPage}`; // fs.mkdirSync(folder); let downloadCount = 0; var concurrencyCount = 0; let q = async.queue(function (image, callback) { // console.log("正在下載 : " + image.title); var delay = parseInt((Math.random() * 30000000) % 1000, 10); //設置延時并發爬取 concurrencyCount++; console.log("現在的并發數是", concurrencyCount, ",正在抓取的是", image.title, "延遲", delay, "毫秒"); superagent.get(image.url).proxy(proxy).end(function (err, res) { if (err) { console.log(err); callback(null); } else { downloadCount++; fs.writeFile(`./picture/${downloadCount}-${image.title}.${image.format}`, res.body, function (err) { if (err) { console.log(err); } else { console.log("圖片下載成功"); } setTimeout(() => { concurrencyCount--; callback(null); }, delay) }); } }); }, 5); // 當所有任務都執行完以后,將調用該函數 q.drain = function () { console.log("All img download"); } let imgList = await dealPicJson(); q.push(imgList);//將所有任務加入隊列 } downloadImg()async控制并發
控制并發我通常是用async.maplimit,因為最早接觸
不過看到一篇文章介紹了async.queue,我就試了下
區別在于, mapLimit會返回所有并發任務結束后的結果數組
而queue是沒有的,因此要自己定個變量來存放每一個并發任務返回的結果
具體api用法見: async常用api
github代碼: bilibili壁紙站爬蟲
里面有一些必要注釋
有4個可以跑的js
./aboutIp/getIp.js (用來抓并存有用的代理ip)
./aboutIp/ipTest.js (測試ip可不可用)
app-thumbnails.js (用來爬壁紙的縮略圖)
app-hd.js (用來爬壁紙的高清圖)
雖然懂得很淺,但能漸漸感受到爬蟲的魅力了?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84795.html
摘要:站的彈幕服務器也有類似的機制,隨便打開一個未開播的直播間,抓包將看到每隔左右會給服務端發送一個心跳包,協議頭第四部分的值從修改為即可。 原文:B 站直播間數據爬蟲, 歡迎轉載項目地址:bilibili-live-crawler 前言 起因 去年在 B 站發現一個后期超強的 UP 主:修仙不倒大小眼,專出 PDD 這樣知名主播的吃雞精彩集錦,漲粉超快。于是想怎么做這樣的 UP,遇到的第一...
摘要:我又回頭看那個爬京東的程序哦我好像被反爬蟲發現了解決反爬蟲問題這下可以了吧直接點開鏈接看一下沒錯,火狐才是我的默認瀏覽器終于不用再說交封不殺了。 昨晚終于提交了該死的31條CPU,今天十節課翹了八節,躺在宿舍睡覺,不幸遇到幾百年難得一見的點名……然而當時我在吃炸雞,沒法(懶)趕過去,達成第一次翹課就點名。 心情郁結的我打算看一看漂亮小姐姐開心一下,于是我發現了這個視頻:showImg(...
摘要:沒有結果返回百度搜索的可以指定頁碼,最多一頁個,使用后有效減少了連接次數。但親測下來設置過以后的結果與實際用戶在百度搜索的結果排序和個數都有出入。 showImg(https://segmentfault.com/img/bVbnA0I?w=1280&h=787); 一直有一個需求,希望看到自己網站在百度的實時的排名用過一些工具,要么反應遲鈍,要么結果不準確或不實時于是打算用jsoup...
摘要:今天為大家整理了個爬蟲項目。地址新浪微博爬蟲主要爬取新浪微博用戶的個人信息微博信息粉絲和關注。代碼獲取新浪微博進行登錄,可通過多賬號登錄來防止新浪的反扒。涵蓋鏈家爬蟲一文的全部代碼,包括鏈家模擬登錄代碼。支持微博知乎豆瓣。 showImg(https://segmentfault.com/img/remote/1460000018452185?w=1000&h=667); 今天為大家整...
閱讀 2785·2021-11-04 16:15
閱讀 3458·2021-09-29 09:35
閱讀 4032·2021-09-22 15:45
閱讀 1417·2019-08-30 15:55
閱讀 1689·2019-08-30 15:44
閱讀 2711·2019-08-29 12:56
閱讀 2697·2019-08-26 13:30
閱讀 2169·2019-08-23 17:00