尤物视频网站_国产精品成人在亚洲_国产成人亚洲综合无码不卡精品_丁香五月香婷婷五月_亚洲成AV人片高潮喷水

您的位置:首頁 >聚焦 >

副業(yè)1000元,文末有代碼,制作一個谷歌瀏覽器插件,實現(xiàn)網(wǎng)頁數(shù)據(jù)爬蟲

2022-11-29 15:46:12    來源:程序員客棧
一、什么是瀏覽器插件

瀏覽器插件,基于瀏覽器的原有功能,另外增加新功能的工具,是可定制瀏覽體驗的小型軟件程序,讓用戶可以根據(jù)個人需要或偏好來定制瀏覽器。

如攔截網(wǎng)頁中的廣告、劃詞翻譯、倍速視頻等等。

Chrome、edge等瀏覽器中都有專門的插件下載商店。受某些原因限制,Chrome服務(wù)并不能正常訪問


(相關(guān)資料圖)

這里提供幾個常用的瀏覽器插件下載地址:

Chrome插件,谷歌瀏覽器插件下載,chrome谷歌商店插件crx應(yīng)用推薦與下載-擴展迷

Chrome插件,Chrome商店,谷歌瀏覽器插件下載,谷歌商店 - Chrome插件網(wǎng)

極簡插件_Chrome擴展插件商店_優(yōu)質(zhì)crx應(yīng)用下載

有興趣的小伙伴可以進入網(wǎng)站看看有沒有感興趣的、滿足自己定制化需求的插件。

如何開發(fā)瀏覽器插件

假如小伙伴找了半天,發(fā)現(xiàn)網(wǎng)上并沒有滿足自身需求的插件,那么你是否會考慮自己做一個呢?

其實這并不是什么高大上的事,網(wǎng)上那么多好用的瀏覽器插件都是開發(fā)者基于Chrome開放出的瀏覽器插件api完成的。

Chrome已經(jīng)把可能用到的各種“磚”封裝好開放出來了,那我們只需要按照一定的規(guī)則“搬”就行了啊。

開發(fā)插件之前,我們首先要了解的是插件都有哪些能力,什么能實現(xiàn),什么不能實現(xiàn)。

如果你想點一點就把15寸的筆記本屏放大到30寸,用完讓屏幕再縮小回去,那這插件肯定是實現(xiàn)不了的。

相關(guān)文檔:

chrome谷歌瀏覽器開發(fā)文檔

360瀏覽器綜述--擴展開發(fā)文檔

瀏覽器擴展 - Mozilla | MDN

以上都是通篇文檔,可能對新手并不友好,讀起來枯燥無味。

下面推薦一個能快速上手的博客,同時感謝該作者的技術(shù)輸出。Chrome插件(擴展)開發(fā)全攻略(干貨)

常見爬蟲方法的對比

后面我們會實現(xiàn)一個爬蟲功能的插件。在開始實戰(zhàn)之前,我們可以先聊一聊常見爬蟲能力的優(yōu)缺點。

api接口該方法速度快,容易上手,會任意編程語言都可以實現(xiàn),且操作用戶對此無感知。

但同時也有很大的缺點,這種方法很難同時發(fā)起用戶行為收集請求,有些產(chǎn)品會通過這些行為收集接口分析用戶的操作,如果邏輯變化,需要手動更新代碼到客戶處。

如果只有數(shù)據(jù)接口請求,沒有統(tǒng)計接口請求,很容易被判定為爬蟲,從而產(chǎn)生一系列負(fù)面影響。

有些產(chǎn)品還會有加密代碼,需要一些逆向工作,這就更進一步提高這種方法爬取數(shù)據(jù)的難度了。

Selenium該方法是通過運行測試的開源工具實現(xiàn)的,常見編程語言都有對應(yīng)的工具,相較于第一種方法有著更廣范圍的應(yīng)用場景。

該方法通過啟動相關(guān)驅(qū)動支持的真實的瀏覽器,盡可能的模擬用戶操作,相關(guān)行為分析會自動請求,幾乎不需要逆向,一定程度上填補了第一種方法的弊端。

但同時該方法也有弊端,需要給客戶機安裝運行環(huán)境和客戶的Chrome瀏覽器升級等問題。

瀏覽器升級可能導(dǎo)致Selenium驅(qū)動版本和瀏覽器版本不匹配,程序就會運行失敗。邏輯變化需要手動更新到客戶處。

該方式也會被產(chǎn)品方識別出是程序啟動而不是真實用戶啟動的瀏覽器,從而產(chǎn)生負(fù)面影響。

瀏覽器插件該方法是通過瀏覽器的開放能力實現(xiàn)的,是用戶啟動的真實瀏覽器,進一步填充了前兩種方法的弊端,通過各種形式的腳本實現(xiàn)復(fù)雜的操作。

可以發(fā)布到像app發(fā)布到應(yīng)用商店一樣發(fā)布到瀏覽器應(yīng)用商店,且提供線上更新功能。

該方法必須得會JavaScript腳本語言,同時熟知瀏覽器的開放能力,增加了學(xué)習(xí)難度。

該方法仍可以被產(chǎn)品識別出,如使用 MutationObserver 方法檢測出dom變化等。

沒有完美的方法,只有更適合的方法,不同場景使用不同的技術(shù)應(yīng)對不同的困難即可。

實現(xiàn)一個瀏覽器爬蟲插件

介紹完常見爬蟲的區(qū)別后,接下來,我們就開始實現(xiàn)一個瀏覽器爬蟲插件。

此處假設(shè)小伙伴已經(jīng)閱讀上述推薦的博客并基本熟悉瀏覽器插件的能力。

需求:爬取10頁boss直聘網(wǎng)站上全國范圍內(nèi)Python崗位的招聘信息。

拆解需求:目標(biāo)網(wǎng)站:

boss直聘網(wǎng)站

篩選條件:

城市:全國關(guān)鍵詞:Python

數(shù)量:

1-10頁內(nèi)的全部數(shù)據(jù)

url地址:https://www.zhipin.com/web/geek/job?query=Python&city=100010000&page=1

難點分析使用什么腳本類型

插件有injected、content、popup、background、devtools 5種類型的腳本,不同類型擁有不同的能力,相互之間的通信方式也不盡相同。

所以首先需要根據(jù)需求結(jié)合具體類型腳本的能力來確定使用什么腳本。

popup肯定是需要的,給文件指定名稱和下達(dá)開始爬取的命令時要用到該類型腳本。

此處已確定popup腳本,其他類型待定。

攔截網(wǎng)絡(luò)請求

經(jīng)分析,從dom結(jié)構(gòu)中獲取數(shù)據(jù)不靠譜。如,跳轉(zhuǎn)鏈接,某些產(chǎn)品的鏈接并不放在dom中,而是通過點擊事件句柄判斷按鈕的index、id等唯一標(biāo)識,從js作用域中找到對應(yīng)的鏈接進行跳轉(zhuǎn)。

那么就需要考慮怎么能攔截到網(wǎng)絡(luò)請求了。插件的核心是不同類型js腳本,不同類型的腳本能力不同,需結(jié)合實際考慮。

在5種腳本類型對比可知,只有injected、devtools、background可以攔截到網(wǎng)絡(luò)請求。但background拿不到響應(yīng)體,故拋棄。

devtools功能很強大,它可以模擬出一個和開發(fā)者工具(F12)-網(wǎng)絡(luò)(network)功能幾乎一樣的面板,但實現(xiàn)起來會相對復(fù)雜。

前端同學(xué)常用的React Developer Tools、vue-tools調(diào)試面板就是使用該技術(shù)開發(fā)的。

經(jīng)過權(quán)衡對比,使用更加簡單的injected來做網(wǎng)絡(luò)攔截。

此處已確定popup和injected兩種腳本。

通信

爬取過程很簡單,通信是一件復(fù)雜的事,詳情通信可參考上述文檔。

現(xiàn)在的流程是 injected攔截網(wǎng)絡(luò)請求 -> popup下達(dá)開始爬取的指令 -> injected開始執(zhí)行腳本收集數(shù)據(jù) -> injected清洗并導(dǎo)出數(shù)據(jù)。

現(xiàn)在確定的popup和injected兩種類型能滿足嗎?很遺憾,不能,通過上述博客中總結(jié)的通信方式可知,這兩種類型的腳本不能直接通信,也就是popup不能告訴injected可以開始收集數(shù)據(jù)了。

怎么實現(xiàn)呢?需要引入一個“中介”——content,作為popup和injected中間通信的橋梁。

現(xiàn)在的過程就變成了,injected攔截網(wǎng)絡(luò)請求 -> popup下達(dá)開始爬取的指令 -> content轉(zhuǎn)發(fā)指令-> injected開始執(zhí)行腳本收集數(shù)據(jù) -> injected清洗并導(dǎo)出數(shù)據(jù)

這里可以留一個小問題,最后一步可以使用content實現(xiàn)嗎,為什么不使用這種方式?

此處已確定popup和injected、content三種腳本。

代碼部分

確定腳本選型后就可以創(chuàng)建工程了,新建一個文件夾,創(chuàng)建如下的目錄結(jié)構(gòu):

boss-plugin├─html│└─popup│├─popup.html//點擊瀏覽器右上角插件,彈出popup,傳遞用戶指令│└─popup.js├─js│├─content//content腳本通過manifest.json配置文件可以直接添加到頁面中││├─install.js//injected腳本并不能直接通過配置添加到頁面中,需要通過content執(zhí)行js代碼動態(tài)插入到dom中││└─page.js//“中介角色”,轉(zhuǎn)發(fā)指令│└─inject│├─network.js//攔截網(wǎng)絡(luò)請求│├─page.js//具體執(zhí)行收集、清洗、導(dǎo)出數(shù)據(jù)的邏輯│└─pikazExcel.js//導(dǎo)出數(shù)據(jù)為Excel的js類庫└─manifest.json//瀏覽器識別插件配置的文件,必須

manifest.json

{"name":"爬取boss數(shù)據(jù)","version":"1.0","manifest_version":2,"browser_action":{"default_popup":"/html/popup/popup.html"},"content_scripts":[{"matches":["*://www.zhipin.com/*"],"js":["/js/content/page.js","/js/content/install.js"],"run_at":"document_start"}],"web_accessible_resources":["/js/inject/pikazExcel.js","/js/inject/page.js","/js/inject/network.js"]}

html/popup/popup.html

文件名:
導(dǎo)出數(shù)據(jù)
<scriptsrc="/html/popup/popup.js"></script>

html/popup/popup.js

functiononClickExport(){document.getElementById("export-btn").disabled=trueconstfilename=document.getElementById("filename").valueconstcb=(tab)=>{chrome.tabs.sendMessage(tab.id,{action:"CHANGE_POPUP_ALLOW_DOWNLOAD",filename});}chrome.tabs.getSelected(null,cb);}document.getElementById("export-btn").onclick=onClickExport

js/content/install.js

setTimeout(()=>{constpageScript=document.createElement("script");pageScript.setAttribute("type","text/javascript");pageScript.setAttribute("src",chrome.extension.getURL("/js/inject/page.js"));document.head.appendChild(pageScript);constnetworkScript=document.createElement("script");networkScript.setAttribute("type","text/javascript");networkScript.setAttribute("src",chrome.extension.getURL("/js/inject/network.js"));document.head.appendChild(networkScript);constexcelScript=document.createElement("script");excelScript.setAttribute("type","text/javascript");excelScript.setAttribute("src",chrome.extension.getURL("/js/inject/pikazExcel.js"));document.head.appendChild(excelScript);});

js/content/page.js

//轉(zhuǎn)發(fā)popup指令popup=>contentscript=>injectscriptchrome.extension.onMessage.addListener(function(request){if(request.action=="CHANGE_POPUP_ALLOW_DOWNLOAD"){//popup告訴頁面可以開始收集并下載數(shù)據(jù)了window.postMessage({action:"CHANGE_POPUP_ALLOW_DOWNLOAD",popupAllowDownload:true,filename:request.filename},"*");}});

js/inject/network.js

此處需要注意瀏覽器發(fā)起請求的兩種方式:xhr和fetch,前者使用較多,后者也在開發(fā)過程中見到過。

const_requestTools={formatQueryString(queryString=""){constresult={};if(queryString.length>0){queryString=queryString.split("?")[1].split("&");for(letkvofqueryString){kv=kv.split("=");if(kv[0])result[kv[0]]=decodeURIComponent(kv[1]);}}returnresult}}function_initXMLHttpRequest(){//攔截網(wǎng)絡(luò)請求方法1constopen=XMLHttpRequest.prototype.open;const_targetApiList=["wapi/zpgeek/search/joblist.json"]XMLHttpRequest.prototype.open=function(...args){this.addEventListener("load",function(){//如果當(dāng)前url并不包含_targetApiList中任意一個地址,則阻止后續(xù)操作if(!_targetApiList.find(item=>this.responseURL.includes(item)))returnconstresult={responseHeaders:{},responseData:{},request:this,status:this.status,params:_requestTools.formatQueryString(this.responseURL)}//格式化響應(yīng)頭this.getAllResponseHeaders().split("\r\n").forEach((item)=>{const[key,value]=item.split(":");if(key)result.responseHeaders[key]=value;});if(result.responseHeaders["content-type"].includes("application/json")){//如果響應(yīng)頭是content-type是json,則格式化響應(yīng)體if(this.response?.length)result.responseData=JSON.parse(this.response);}_crawler.collectData(result)})returnopen.apply(this,args);};//攔截網(wǎng)絡(luò)請求方法2//此處的方法攔截在目標(biāo)網(wǎng)站中并沒有遇到,在其他項目中遇到過,故添加在此做補充知識點。const{fetch:originalFetch}=window;window.fetch=async(...args)=>{let[resource,config]=args;letresponse=awaitoriginalFetch(resource,config);if(response.status===200){response.clone().json().then((data)=>{console.log("響應(yīng)數(shù)據(jù):",data)});}returnresponse;};}_initXMLHttpRequest();

js/inject/page.js

//因為injectjs和頁面共享js作用域,為防止污染全局變量,故插件中變量名都以_開頭class_Crawler{constructor(){this.downloadPageNum=10//允許下載多少頁this.filename=""http://從popup傳進來的輸入的文件名this.allowDownload=false//popup給出指令允許下載this.collectionList=[]//收集每頁請求得到的數(shù)據(jù)}/***獲取當(dāng)前年-月-日時:分:秒*@returnsstring*/getTime(){consttime=newDate();consttimeInfo=(time.getFullYear()+"-"+(time.getMonth()+1)+"-"+time.getDate()+""+time.getHours()+":"+time.getMinutes()+":"+time.getSeconds())returntimeInfo}//生成隨機延遲秒數(shù),默認(rèn)3-4秒getRandomTimeOut(x=3000,y=4000){returnMath.round(Math.random()*(y-x)+x)}collectData(result){//首次進來或搜索條件變化,清空收集結(jié)果constcurrentPage=result.params.page*1;if(currentPage*1===1)this.collectionList=[]if(!this.collectionList.find(el=>result.request.responseURL.includes(el.responseURL))){constitem={responseURL:result.request.responseURL,responseData:result.responseData}this.collectionList.push(item)}//如果沒有點擊導(dǎo)出按鈕,則阻止后續(xù)操作if(!this.allowDownload)return//結(jié)束收集行為的條件,然后進行數(shù)據(jù)清洗和導(dǎo)出excelif(currentPage>=this.downloadPageNum){constsheet=this.clearData()this.download(sheet)}else{//隨機3-4秒后進行點擊下一頁//這是寫爬蟲最基本的道德了,盡量在學(xué)習(xí)技術(shù)的同時,不要對目標(biāo)服務(wù)器產(chǎn)生壓力和影響其正常運行constrandomTimeout=this.getRandomTimeOut()setTimeout(()=>{this.handleClickNext()},randomTimeout);}}clearData(){constheaderAndKeyList=[{header:"崗位名稱",key:"jobName"},{header:"地址",key:"jobAddress"},{header:"薪資",key:"salaryDesc"},{header:"經(jīng)驗",key:"jobExperience"},{header:"學(xué)歷",key:"jobDegree"},{header:"技術(shù)棧",key:"skills"},{header:"公司名稱",key:"brandName"},{header:"公司行業(yè)",key:"brandIndustry"},{header:"公司融資階段",key:"brandStageName"},{header:"公司規(guī)模",key:"brandScaleName"},{header:"福利待遇",key:"welfareList"},]constitemTableConfig={tHeader:headerAndKeyList.map(el=>el.header),keys:headerAndKeyList.map(el=>el.key),table:[]}this.collectionList.forEach(el1=>{el1.responseData.zpData.jobList.forEach(el2=>{const{jobName,cityName,areaDistrict,businessDistrict,salaryDesc,jobExperience,jobDegree,skills,brandName,brandIndustry,brandStageName,brandScaleName,welfareList}=el2constitem={jobName,jobAddress:`${cityName}·${areaDistrict}·${businessDistrict}`,salaryDesc,jobExperience,jobDegree,skills,brandName,brandIndustry,brandStageName,brandScaleName,welfareList}itemTableConfig.table.push(item)})})return[itemTableConfig]}download(sheet){constfilename=this.filename||this.getTime()window.pikazExcelJs.default.excelExport({sheet,filename,beforeStart:(bookType,filename,sheet)=>{console.log("開始導(dǎo)出",bookType,sheet,filename);},}).then(()=>{this.filename=""this.allowDownload=falsethis.collectionList=[]});}handleClickNext(){constnextSelector=".pagination-area.options-pagesa:last-child"constnextDom=document.querySelector(nextSelector)nextDom.click()//如果目標(biāo)網(wǎng)站有收集用戶行為的接口,此處可添加模擬用戶操作,如滾動頁面、點擊某些元素}}const_crawler=new_Crawler();//監(jiān)聽從popup發(fā)送的指令popup=>contentscript=>injectscriptwindow.addEventListener("message",function(e){if(e.data.action==="CHANGE_POPUP_ALLOW_DOWNLOAD"){_crawler.filename=e.data.filename_crawler.allowDownload=true_crawler.handleClickNext()}},false);

js/inject/pikazExcel.js

文檔和下載地址:https://www.npmjs.com/package/pikaz-excel-js

最后在Chrome瀏覽器中打開這個地址 chrome://extensions/

開啟開發(fā)者模式 -> 加載已解壓的擴展程序 -> 選擇剛才新建的文件夾 -> 確認(rèn)導(dǎo)入

這時候就已經(jīng)把剛才編寫的導(dǎo)入到瀏覽器中了,打開目標(biāo)頁面

然后點擊紅框區(qū)域,輸入文件名(可選),點擊導(dǎo)出數(shù)據(jù),就可以開始爬取內(nèi)容了

最終效果:

參考鏈接,感謝以下鏈接提供相關(guān)技術(shù)的解決思路:

XMLHttpRequest 攔截處理

Chrome插件(擴展)開發(fā)全攻略(干貨)

pikaz-excel-js

代碼下載地址

鏈接:https://pan.baidu.com/s/1RHYE-CuZqmBJm7Wj9G4fYQ

提取碼:u5cp

如果想接副業(yè)單子

如果想跟著螞蟻老師做副業(yè)兼職,可以每晚22點來螞蟻老師抖音直播間:抖音賬號:Python導(dǎo)師-螞蟻

關(guān)鍵詞: 谷歌瀏覽器 下載地址 通信方式

相關(guān)閱讀