亚洲国产精品无码久久大片,亚洲AV无码乱码麻豆精品国产,亚洲品质自拍网站,少妇伦子伦精品无码STYLES,国产精久久久久久久

文章采集api

文章采集api

數據治理之數字畫(huà)像

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 179 次瀏覽 ? 2022-07-10 02:00 ? 來(lái)自相關(guān)話(huà)題

  數據治理之數字畫(huà)像
  00引言
  隨著(zhù)全網(wǎng)步入大數據時(shí)代,企業(yè)的目光日益聚焦在利用大數據服務(wù)精細化營(yíng)銷(xiāo)、精細化運營(yíng)上,各類(lèi)客戶(hù)畫(huà)像、員工畫(huà)像理論如雨后春筍般興起,而數據應用的底層——數據治理,卻鮮有整體的理論體系。如何避免治理工作自身“無(wú)的放矢”,如何量化數據基礎建設的貢獻,我們需要為數據治理工作描繪一張“數字畫(huà)像”。這個(gè)命題的內涵外延非常豐富,在此我們選取用戶(hù)體驗、架構質(zhì)量?jì)蓚€(gè)角度進(jìn)行討論。
  01用戶(hù)體驗的數字畫(huà)像
  基于不同的感知角度,將用戶(hù)分為外部客戶(hù)、內部用戶(hù)、管理層、技術(shù)人員四類(lèi),針對特定的業(yè)務(wù)場(chǎng)景刻畫(huà)四類(lèi)使用者所體會(huì )到的“科技賦能”。
  1、外部客戶(hù)
  功能體驗指標:功能體驗指標用于衡量操作平臺的易用性及直觀(guān)程度??梢酝ㄟ^(guò)各類(lèi)埋點(diǎn),對用戶(hù)的點(diǎn)擊行為、頁(yè)面停留時(shí)間、頁(yè)面瀏覽深度進(jìn)行跟蹤。從而挖掘用戶(hù)常使用的功能,探究的實(shí)際需要,對于常用功能可以開(kāi)展功能的改版優(yōu)化,進(jìn)行同業(yè)產(chǎn)品比較、用戶(hù)反饋調查等,重點(diǎn)關(guān)注主要功能的流暢度、實(shí)用性。
  平臺服務(wù)指標:
 ?。?)服務(wù)平臺一般利用API接口向外提供數據,因此,通過(guò)計算API調用率可以計算出其向外輸出服務(wù)的活躍程度。
 ?。?)由數據服務(wù)帶來(lái)的產(chǎn)品升值也是需要衡量的一大重要指標。營(yíng)銷(xiāo)、運營(yíng)等商業(yè)活動(dòng)價(jià)值提升以一定的比例分配給其相應的數據治理工作,從業(yè)務(wù)部門(mén)有感的角度評估數據治理工作對營(yíng)銷(xiāo)等活動(dòng)的賦能。
  2、內部用戶(hù)
  
  便利性:過(guò)去業(yè)務(wù)部門(mén)向數據管理部門(mén)申請使用數據,通常使用郵件或行政流程的方式,這種方法無(wú)法實(shí)時(shí)跟蹤申請進(jìn)度,也無(wú)法在統一的頁(yè)面集中管理,導致相關(guān)工作人員在查詢(xún)與溝通上花費了大量精力。建立自動(dòng)化、規范化流程以及線(xiàn)上運營(yíng)工具,將極大地便利流程,因此,手工提取工單壓降比率可以作為度量?jì)炔坑脩?hù)程度的指標。
  時(shí)效性:線(xiàn)上化數據治理意味著(zhù)資產(chǎn)地圖、標準架構即存放在用戶(hù)指尖,關(guān)鍵作業(yè)的全鏈路交付時(shí)間是觸達內部用戶(hù)的另一直觀(guān)感受。該指標可以通過(guò)統計各節點(diǎn)的流轉時(shí)間,計算相應平均值獲得。
  貢獻度:不僅是底層的數據管理,數據的應用輸出同樣能夠為用戶(hù)帶來(lái)實(shí)際業(yè)務(wù)價(jià)值。BI工具的使用、模型提供數量等指標標志著(zhù)用戶(hù)對于應用類(lèi)數據成果的滿(mǎn)意度。
  3、管理層
  質(zhì)量提升:對于管理層而言,保障數據倉庫、數據湖的“清澈”是他們關(guān)心的問(wèn)題。由于監管報送結果是銀保監對銀行的重點(diǎn)考核指標,報送規定的達標率成為對于管理層數據治理成效最直觀(guān)的反映?;贒QC的一系列指標同樣可作為面向管理層的數據清潔度體現。
  效率提升:除監管要求之外,數據運營(yíng)成本對于全行管理也是至關(guān)重要的。只有建立規范和高效的數據架構,壓降數據報表,降低儲存、運維成本,才能實(shí)現精細化營(yíng)運,維持高效率盈利。
  4、技術(shù)人員
  數據字典評分:當企業(yè)實(shí)施開(kāi)發(fā)過(guò)程強管控時(shí),數據字典的角色可看作是法律之于社會(huì ),其整體邏輯必須經(jīng)得起反復推敲。在數據字典的查詢(xún)頁(yè)面設立評分反饋是一種簡(jiǎn)單但行之有效的方法。頁(yè)面上有計劃的引導,反映設計者關(guān)注開(kāi)發(fā)人員的使用體驗,從而讓“吐槽”變成建議,優(yōu)化和解決使用數據字典時(shí)遇到的問(wèn)題。
  02架構質(zhì)量的數字畫(huà)像
  全行統一的數據架構應在追求高效率的同時(shí)降低成本,根據《華為數據之道》中信息架構的經(jīng)典四范式,我們將從模型、分布、標準、資產(chǎn)四個(gè)角度對架構賦能能力進(jìn)行度量。
  1、模型
  公共層加工頻率:公共層中存放有事實(shí)數據、維表數據等等,它們支撐著(zhù)指標體系中的一級指標層。在建立指標時(shí),將規范化、集約化,提高公共指標的復用性,減少重復加工的操作,故公共層數據模型的復用率可作為公共層架構評估的指數之一。
  
  應用層引用頻率:類(lèi)似于人際關(guān)系網(wǎng)絡(luò )拓撲結構中的核心人物算法,該指數直接衡量應用層中數據的系統性重要程度,引導資產(chǎn)盤(pán)點(diǎn)的目標。數據血緣關(guān)系是一種有向的、無(wú)權值、無(wú)自環(huán)的網(wǎng)絡(luò )圖。被引用頻率高的資產(chǎn)一般來(lái)源于關(guān)鍵業(yè)務(wù)實(shí)體中最準確和最及時(shí)的業(yè)務(wù)記錄。這一些資產(chǎn)被跨部門(mén)、跨業(yè)務(wù)領(lǐng)域調用的概率最大,需要實(shí)現所有部門(mén)可訪(fǎng)問(wèn)并且訪(fǎng)問(wèn)到相同的數據。該指數還能夠有效地篩選出“孤兒表”、臨時(shí)表,減少資源投入和儲存成本。
  2、分布
  數據覆蓋:對于大型銀行而言,數以百計的系統,數以萬(wàn)計的庫表在全國范圍內分布式儲存。采集是資產(chǎn)盤(pán)點(diǎn)的第一步,測量采集數在全量系統的覆蓋率幫助我們明確當前采集的進(jìn)度,定位未采集的數據來(lái)源。
  數據冗余:數據冗余指同層數據的冗余,具體可分為兩個(gè)來(lái)源。第一,多個(gè)物理位置中存儲了相同意義的數據;第二,架構模型本身在設計上有較多的重復交叉項。
  數據容量:數據容量是對數據中臺的整體描述,它包括當前中臺所囊括的整體數據體量的絕對值,也包含該體量隨時(shí)間的增長(cháng)比例。數據容量并非越高或者越低更理想,它需要結合銀行的現狀辯證性地看待。
  3、標準
  標準穩定性:數據標準規范化了數據含義、結構等等,應當滿(mǎn)足內容統一、不交叉定義等條件,避免數據標準內部發(fā)生“數據打架”。
  標準落標率:在標準的技術(shù)規范完備,主題齊全,標準已權威發(fā)布的前提下,標準落標率反映了數據標準“最后一公里”的執行情況。借助自動(dòng)化工具,能夠計算出各類(lèi)分層、切片后的數據落標率,智能化地發(fā)現落標潛在問(wèn)題。
  4、資產(chǎn)
  技術(shù)元數據統計:技術(shù)元數據打通了源數據和,記錄了數據從產(chǎn)生到消亡的過(guò)程。我們從中挑選出系統覆蓋率、系統內表級覆蓋率、表名以及字段名的有效率、枚舉值的有效率等統計指標表示數據架構中技術(shù)類(lèi)資產(chǎn)的產(chǎn)出效益。
  企業(yè)活動(dòng)命中率:數據資產(chǎn)是從業(yè)務(wù)流程、業(yè)務(wù)模型中抽取出來(lái)的數字化描述。標簽資產(chǎn)對業(yè)務(wù)行為的命中率、指標資產(chǎn)對報表統計的命中率、報表資產(chǎn)的用戶(hù)訪(fǎng)問(wèn)量等數值越高,代表著(zhù)資產(chǎn)內容映射企業(yè)活動(dòng)的準確度越高。
  03結語(yǔ)
  伴隨著(zhù)企業(yè)數字化轉型不斷深入,“數據治理的數字畫(huà)像”從方法論到實(shí)踐都將趨于完善,內容價(jià)值、安全性能、用戶(hù)體驗也會(huì )隨之提高。如何動(dòng)態(tài)地衡量數據治理工作成效,建立適合自身企業(yè)的“北極星指標”,是每一家處于智慧轉型階段的公司所必須研究的,它的成功將創(chuàng )造出不可估量的商業(yè)價(jià)值。 查看全部

  數據治理之數字畫(huà)像
  00引言
  隨著(zhù)全網(wǎng)步入大數據時(shí)代,企業(yè)的目光日益聚焦在利用大數據服務(wù)精細化營(yíng)銷(xiāo)、精細化運營(yíng)上,各類(lèi)客戶(hù)畫(huà)像、員工畫(huà)像理論如雨后春筍般興起,而數據應用的底層——數據治理,卻鮮有整體的理論體系。如何避免治理工作自身“無(wú)的放矢”,如何量化數據基礎建設的貢獻,我們需要為數據治理工作描繪一張“數字畫(huà)像”。這個(gè)命題的內涵外延非常豐富,在此我們選取用戶(hù)體驗、架構質(zhì)量?jì)蓚€(gè)角度進(jìn)行討論。
  01用戶(hù)體驗的數字畫(huà)像
  基于不同的感知角度,將用戶(hù)分為外部客戶(hù)、內部用戶(hù)、管理層、技術(shù)人員四類(lèi),針對特定的業(yè)務(wù)場(chǎng)景刻畫(huà)四類(lèi)使用者所體會(huì )到的“科技賦能”。
  1、外部客戶(hù)
  功能體驗指標:功能體驗指標用于衡量操作平臺的易用性及直觀(guān)程度??梢酝ㄟ^(guò)各類(lèi)埋點(diǎn),對用戶(hù)的點(diǎn)擊行為、頁(yè)面停留時(shí)間、頁(yè)面瀏覽深度進(jìn)行跟蹤。從而挖掘用戶(hù)常使用的功能,探究的實(shí)際需要,對于常用功能可以開(kāi)展功能的改版優(yōu)化,進(jìn)行同業(yè)產(chǎn)品比較、用戶(hù)反饋調查等,重點(diǎn)關(guān)注主要功能的流暢度、實(shí)用性。
  平臺服務(wù)指標:
 ?。?)服務(wù)平臺一般利用API接口向外提供數據,因此,通過(guò)計算API調用率可以計算出其向外輸出服務(wù)的活躍程度。
 ?。?)由數據服務(wù)帶來(lái)的產(chǎn)品升值也是需要衡量的一大重要指標。營(yíng)銷(xiāo)、運營(yíng)等商業(yè)活動(dòng)價(jià)值提升以一定的比例分配給其相應的數據治理工作,從業(yè)務(wù)部門(mén)有感的角度評估數據治理工作對營(yíng)銷(xiāo)等活動(dòng)的賦能。
  2、內部用戶(hù)
  
  便利性:過(guò)去業(yè)務(wù)部門(mén)向數據管理部門(mén)申請使用數據,通常使用郵件或行政流程的方式,這種方法無(wú)法實(shí)時(shí)跟蹤申請進(jìn)度,也無(wú)法在統一的頁(yè)面集中管理,導致相關(guān)工作人員在查詢(xún)與溝通上花費了大量精力。建立自動(dòng)化、規范化流程以及線(xiàn)上運營(yíng)工具,將極大地便利流程,因此,手工提取工單壓降比率可以作為度量?jì)炔坑脩?hù)程度的指標。
  時(shí)效性:線(xiàn)上化數據治理意味著(zhù)資產(chǎn)地圖、標準架構即存放在用戶(hù)指尖,關(guān)鍵作業(yè)的全鏈路交付時(shí)間是觸達內部用戶(hù)的另一直觀(guān)感受。該指標可以通過(guò)統計各節點(diǎn)的流轉時(shí)間,計算相應平均值獲得。
  貢獻度:不僅是底層的數據管理,數據的應用輸出同樣能夠為用戶(hù)帶來(lái)實(shí)際業(yè)務(wù)價(jià)值。BI工具的使用、模型提供數量等指標標志著(zhù)用戶(hù)對于應用類(lèi)數據成果的滿(mǎn)意度。
  3、管理層
  質(zhì)量提升:對于管理層而言,保障數據倉庫、數據湖的“清澈”是他們關(guān)心的問(wèn)題。由于監管報送結果是銀保監對銀行的重點(diǎn)考核指標,報送規定的達標率成為對于管理層數據治理成效最直觀(guān)的反映?;贒QC的一系列指標同樣可作為面向管理層的數據清潔度體現。
  效率提升:除監管要求之外,數據運營(yíng)成本對于全行管理也是至關(guān)重要的。只有建立規范和高效的數據架構,壓降數據報表,降低儲存、運維成本,才能實(shí)現精細化營(yíng)運,維持高效率盈利。
  4、技術(shù)人員
  數據字典評分:當企業(yè)實(shí)施開(kāi)發(fā)過(guò)程強管控時(shí),數據字典的角色可看作是法律之于社會(huì ),其整體邏輯必須經(jīng)得起反復推敲。在數據字典的查詢(xún)頁(yè)面設立評分反饋是一種簡(jiǎn)單但行之有效的方法。頁(yè)面上有計劃的引導,反映設計者關(guān)注開(kāi)發(fā)人員的使用體驗,從而讓“吐槽”變成建議,優(yōu)化和解決使用數據字典時(shí)遇到的問(wèn)題。
  02架構質(zhì)量的數字畫(huà)像
  全行統一的數據架構應在追求高效率的同時(shí)降低成本,根據《華為數據之道》中信息架構的經(jīng)典四范式,我們將從模型、分布、標準、資產(chǎn)四個(gè)角度對架構賦能能力進(jìn)行度量。
  1、模型
  公共層加工頻率:公共層中存放有事實(shí)數據、維表數據等等,它們支撐著(zhù)指標體系中的一級指標層。在建立指標時(shí),將規范化、集約化,提高公共指標的復用性,減少重復加工的操作,故公共層數據模型的復用率可作為公共層架構評估的指數之一。
  
  應用層引用頻率:類(lèi)似于人際關(guān)系網(wǎng)絡(luò )拓撲結構中的核心人物算法,該指數直接衡量應用層中數據的系統性重要程度,引導資產(chǎn)盤(pán)點(diǎn)的目標。數據血緣關(guān)系是一種有向的、無(wú)權值、無(wú)自環(huán)的網(wǎng)絡(luò )圖。被引用頻率高的資產(chǎn)一般來(lái)源于關(guān)鍵業(yè)務(wù)實(shí)體中最準確和最及時(shí)的業(yè)務(wù)記錄。這一些資產(chǎn)被跨部門(mén)、跨業(yè)務(wù)領(lǐng)域調用的概率最大,需要實(shí)現所有部門(mén)可訪(fǎng)問(wèn)并且訪(fǎng)問(wèn)到相同的數據。該指數還能夠有效地篩選出“孤兒表”、臨時(shí)表,減少資源投入和儲存成本。
  2、分布
  數據覆蓋:對于大型銀行而言,數以百計的系統,數以萬(wàn)計的庫表在全國范圍內分布式儲存。采集是資產(chǎn)盤(pán)點(diǎn)的第一步,測量采集數在全量系統的覆蓋率幫助我們明確當前采集的進(jìn)度,定位未采集的數據來(lái)源。
  數據冗余:數據冗余指同層數據的冗余,具體可分為兩個(gè)來(lái)源。第一,多個(gè)物理位置中存儲了相同意義的數據;第二,架構模型本身在設計上有較多的重復交叉項。
  數據容量:數據容量是對數據中臺的整體描述,它包括當前中臺所囊括的整體數據體量的絕對值,也包含該體量隨時(shí)間的增長(cháng)比例。數據容量并非越高或者越低更理想,它需要結合銀行的現狀辯證性地看待。
  3、標準
  標準穩定性:數據標準規范化了數據含義、結構等等,應當滿(mǎn)足內容統一、不交叉定義等條件,避免數據標準內部發(fā)生“數據打架”。
  標準落標率:在標準的技術(shù)規范完備,主題齊全,標準已權威發(fā)布的前提下,標準落標率反映了數據標準“最后一公里”的執行情況。借助自動(dòng)化工具,能夠計算出各類(lèi)分層、切片后的數據落標率,智能化地發(fā)現落標潛在問(wèn)題。
  4、資產(chǎn)
  技術(shù)元數據統計:技術(shù)元數據打通了源數據和,記錄了數據從產(chǎn)生到消亡的過(guò)程。我們從中挑選出系統覆蓋率、系統內表級覆蓋率、表名以及字段名的有效率、枚舉值的有效率等統計指標表示數據架構中技術(shù)類(lèi)資產(chǎn)的產(chǎn)出效益。
  企業(yè)活動(dòng)命中率:數據資產(chǎn)是從業(yè)務(wù)流程、業(yè)務(wù)模型中抽取出來(lái)的數字化描述。標簽資產(chǎn)對業(yè)務(wù)行為的命中率、指標資產(chǎn)對報表統計的命中率、報表資產(chǎn)的用戶(hù)訪(fǎng)問(wèn)量等數值越高,代表著(zhù)資產(chǎn)內容映射企業(yè)活動(dòng)的準確度越高。
  03結語(yǔ)
  伴隨著(zhù)企業(yè)數字化轉型不斷深入,“數據治理的數字畫(huà)像”從方法論到實(shí)踐都將趨于完善,內容價(jià)值、安全性能、用戶(hù)體驗也會(huì )隨之提高。如何動(dòng)態(tài)地衡量數據治理工作成效,建立適合自身企業(yè)的“北極星指標”,是每一家處于智慧轉型階段的公司所必須研究的,它的成功將創(chuàng )造出不可估量的商業(yè)價(jià)值。

文章采集api Zabbix 任性,文末送書(shū) X 5

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 125 次瀏覽 ? 2022-07-02 05:59 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Zabbix 任性,文末送書(shū) X 5
  Zabbix版本不斷升級,以滿(mǎn)足日益增長(cháng)的用戶(hù)需求,支持高可用HA,k8s、指標topN、機器學(xué)習、定制前端品牌logo等!
  舊版本需要腳本才實(shí)現的功能,升級至最新版本可輕松解決!Zabbix6.0為業(yè)務(wù)服務(wù)提供商、DevOps和ITOps團隊提供了附加值,優(yōu)化了整體監控工作流程,并在許多不同層面提供了新見(jiàn)解。
  目錄
  業(yè)務(wù)服務(wù)監控達到全新高度
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  開(kāi)箱即用的Zabbix server高可用群集
  機器學(xué)習
  Kubernetes監控
  詳細高效的Zabbix審計日志模式
  可視化數據的新方法
  Zabbix性能優(yōu)化
  提升Zabbix Agent2模塊化,新的Zabbix Agent 監控項和功能
  原生TLS/SSL網(wǎng)站證書(shū)監控
  通用性改進(jìn)
  通過(guò)自定義密碼復雜程度要求來(lái)保護您的Zabbix登錄
  支持定制前端展示品牌logo
  新增模板和集成
  其它新功能和優(yōu)化
  01
  BMS業(yè)務(wù)服務(wù)監控達到全新高度
  優(yōu)化Services部分,顯示業(yè)務(wù)服務(wù)的狀態(tài)和當前SLA級別
  通過(guò)對現有Services頁(yè)面和功能的重大改進(jìn)和優(yōu)化,業(yè)務(wù)服務(wù)監控提升到了一個(gè)新高度。業(yè)務(wù)服務(wù)監控功能(BSM)非常適合多組件服務(wù)場(chǎng)景,例如服務(wù)器群集、負載平衡器和其它具有冗余組件的服務(wù)。
  Zabbix 6.0提供多種功能自定義業(yè)務(wù)服務(wù)樹(shù)實(shí)現BMS業(yè)務(wù)服務(wù)監控:
  ? 重新設計 Zabbix 6.0 Services頁(yè)面和功能
  ? 支持單個(gè)Zabbix實(shí)例監控超過(guò)10萬(wàn)個(gè)業(yè)務(wù)服務(wù)
  ? 支持新的靈活服務(wù)狀態(tài)計算邏輯
  ? 能夠自定義業(yè)務(wù)服務(wù)的訪(fǎng)問(wèn)權限
  ? 能夠為特定業(yè)務(wù)服務(wù)自定義只讀和讀寫(xiě)權限
  ? 業(yè)務(wù)服務(wù)權限既可以基于顯式服務(wù)列表,也可以基于服務(wù)標簽的訪(fǎng)問(wèn)限制
  ? 導出和導入業(yè)務(wù)服務(wù)樹(shù)
  ? 新的Service動(dòng)作類(lèi)型能讓用戶(hù)接收告警并對業(yè)務(wù)服務(wù)狀態(tài)更改作出反應
  02
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  提供大量可供選擇的服務(wù)狀態(tài)計算規則,能支持靈活的服務(wù)定義
  業(yè)務(wù)服務(wù)狀態(tài)計算邏輯在Zabbix 6.0中得到了極大擴展,增加了許多新功能,例如:
  ? 能夠為每項業(yè)務(wù)服務(wù)分配權重
  ? 僅當N個(gè)子服務(wù)都處于X嚴重級別的問(wèn)題狀態(tài)時(shí)才更改狀態(tài)
  ? 對處于問(wèn)題狀態(tài)下的子服務(wù)的權重進(jìn)行分析并作出反應
  ? 僅當特定百分比的子服務(wù)處于問(wèn)題狀態(tài)時(shí)才作出反應
  ? 其它計算規則
  用戶(hù)還可以自定義和訪(fǎng)問(wèn)指定服務(wù)的SLA報告。
  03
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  根因問(wèn)題會(huì )立即顯示在service下
  對業(yè)務(wù)服務(wù)執行根因分析。利用根因分析功能找出可能導致業(yè)務(wù)服務(wù)SLA下降的潛在問(wèn)題列表:
  ? 在Zabbix前端Services頁(yè)面查看根因問(wèn)題列表
  ? 接收告警中的根因問(wèn)題列表
  ? 通過(guò)Zabbix API收集根因問(wèn)題信息
  04
  開(kāi)箱即用的Zabbix server高可用群集
  在系統信息組件中跟蹤集群集節點(diǎn)狀態(tài)
  Zabbix server高可用防止硬件故障或計劃維護期的停機:
  ? 原生選擇加入HA群集配置
  ? 定義一個(gè)或多個(gè)備用節點(diǎn)
  
  ?實(shí)時(shí)監控Zabbix server群集節點(diǎn)的狀態(tài)
  ? 不需要外部工具即可將Zabbix server配置為HA群集模式
  05
  機器學(xué)習
  使用新函數對意外異常率或與指標基準的偏差做出反應
  新的基線(xiàn)監控和異常檢測趨勢功能以動(dòng)態(tài)方式檢測問(wèn)題,而不是靜態(tài)閾值方式:
  ?新的趨勢函數-baselinewma and baselinedev ,能計算指標基線(xiàn)和偏離值
  ?新的趨勢函數-trendstl,能檢測異常指標行為
  ?能夠指定異常檢測偏差算法及季節性
  06
  Kubernetes監控
  Zabbix 6.0 LTS添加了多個(gè)新模板,用于監控不同的Kubernetes組件
  Zabbix 6.0 LTS新增Kubernetes監控功能,可以在Kubernetes系統從多個(gè)維度采集指標:
  ?Kubernetes節點(diǎn)和pods的自動(dòng)發(fā)現和監控
  ?無(wú)代理方式采集Kubernetes pods和節點(diǎn)的信息
  ?獲取Kubernetes節點(diǎn)主機高水平信息
  Kubernetes監控還能夠監控Kubernetes組件,例如
  ?kube-controller-manager
  ?kube-proxy
  ?kube-apiserver
  ?kube-scheduler
  ?kubelet
  07
  詳細高效的Zabbix審計日志模式
  重新設計的審計日志能提供全新的詳細信息,并優(yōu)化篩選功能。
  新的審計日志模式允許用戶(hù)對Zabbix前端、Zabbix API和Zabbix server記錄執行詳細審計。通過(guò)修改審計日志,對Zabbix實(shí)例執行的所有更改都將記錄在審計日志中:
  ?創(chuàng )建、修改或刪除新對象
  ?通過(guò)LLD發(fā)現新實(shí)體
  ?API命令
  ?定期登錄/退出
  ?Zabbix實(shí)例中發(fā)生的所有其它事情
  新的審計日志模式在設計時(shí)考慮了最佳性能,因此擴展的功能不會(huì )影響Zabbix實(shí)例的性能。審計日志模式的工作是一項持續的工作,會(huì )在后續Zabbix發(fā)布周期中持續進(jìn)行。
  08
  可視化數據的新方法
  主機排序組件可顯示按監控項值排序的前N個(gè)或后N個(gè)主機的列表
  Zabbix 6.0新增的構件提供了展示信息的許多新方法。
  ?地理地圖構件能在地圖上顯示主機和問(wèn)題
  ?數據表構件能創(chuàng )建有關(guān)主機指標狀態(tài)的摘要視圖
  ?數據表構件的前N和后N函數能展示最高或最低的監控項值
  ?單一監控項構件能展示單個(gè)指標的值
  ?對現有矢量圖的許多改進(jìn),例如新的矢量圖類(lèi)型、引用單一監控項等
  ?SLA構件能顯示特定業(yè)務(wù)服務(wù)的當前SLA
  09
  Zabbix性能優(yōu)化
  針對不同的Zabbix組件進(jìn)行多項性能優(yōu)化:
  ?提升鏈接模板時(shí)的性能
  ?提升Zabbix proxy性能和內存使用率
  歷史數據表使用主鍵,這有多種好處,例如:
  ?提高Zabbix server和Zabbix前端的性能
  ?減少歷史數據表的大小
  10
  提升Zabbix Agent2模塊化,
  新的Zabbix Agent 監控項和功能
  優(yōu)化的Zabbix agent現在能夠開(kāi)箱即用監控一組指標
  Zabbix 6.0為Zabbix Agent和Agent2提供了一套新的監控項。支持以下功能:
  ?獲取額外文件信息,如文件所有者和文件權限
  ?采集agent主機元數據作為指標
  
  ?計數匹配的TCP/UDP sockets
  某些已有的監控項支持新的功能:
  ?vfs.fs.discovery-在Windows上添加了對{#FSLABEL}宏的支持
  ?vfs.fs.get-在Windows上添加了對{#FSLABEL}宏的支持
  ? vfs.file.size-添加了一個(gè)新的模式參數。設置以字節數或行數為單位
  Zabbix Agent2現在支持加載獨立插件,而無(wú)需重新編譯Agent2。
  11
  原生TLS/SSL網(wǎng)站證書(shū)監控
  使用新的Zabbix agent2 監控項監控SSL/TLS證書(shū)
  支持使用新的Zabbix agent 2監控項來(lái)監控SSL/TLS證書(shū)。監控項可用于驗證TLS/SSL證書(shū),并提供其它證書(shū)詳細信息。
  12
  通用性改進(jìn)
  通過(guò)優(yōu)化的創(chuàng )建主機UI,使創(chuàng )建新主機從未如此簡(jiǎn)單
  Zabbix 6.0使Zabbix配置工作流程更精簡(jiǎn)!Zabbix用戶(hù)現在可直接在Monitoring頁(yè)面創(chuàng )建主機和監控項:
  ?直接從Monitoring -Hosts頁(yè)面創(chuàng )建主機
  ?直接從Monitoring -Latest data頁(yè)面創(chuàng )建監控項
  ?刪除了Monitoring -Overview頁(yè)面。為了改善用戶(hù)體驗,現在只能通過(guò)儀表盤(pán)構件訪(fǎng)問(wèn)觸發(fā)器和數據概覽功能。
  現在將根據監控項的鍵值自動(dòng)選擇監控項的默認信息類(lèi)型。
  拓撲圖標簽和圖形名稱(chēng)中的簡(jiǎn)單宏已替換為表達式宏,以確保與新的觸發(fā)器表達式語(yǔ)法一致。
  13
  通過(guò)自定義密碼復雜程度要求
  來(lái)保護您的Zabbix登錄
  設置密碼復雜程度確保前端登錄安全
  Zabbix超級管理員現在能夠定義密碼復雜程度要求?,F在可以:
  ?設置最小密碼長(cháng)度
  ?定義密碼字符要求
  ?通過(guò)禁止使用最常見(jiàn)的密碼字符串來(lái)降低字典攻擊的風(fēng)險。
  14
  支持定制前端展示品牌logo
  定制Zabbix實(shí)例代表您的公司。將現有的Zabbix品牌和幫助頁(yè)面URL替換為您自己的公司品牌和自定義網(wǎng)站URL。
  改名功能不會(huì )違反Zabbix許可協(xié)議-可以自由更換Zabbix品牌!
  15
  新增模板和集成
  Zabbix 6.0為最受歡迎的供應商提供了許多新模板:
  ?f5 BIG-IP
  ?Cisco ASAv
  ?HPE ProLiant servers
  ?Cloudflare
  ?InfluxDB
  ?Travis CI
  ?Dell PowerEdge
  Zabbix 6.0還帶來(lái)了一個(gè)新的Github webhook集成,能基于Zabbix問(wèn)題或恢復事件生成Github問(wèn)題!
  所有官方的Zabbix模板現在都是獨立的,不需要依賴(lài)導入其他模板。
  請查看當前可用集成的完整列表。
  16
  其它新功能和優(yōu)化
  更多改進(jìn)功能(部分):
  ?使用新聚合函數計數返回值或匹配監控項的數量-count和item_count函數
  ?在未配置交換空間的情況下提升system.swap監控項行為
  ?使用新的單調歷史函數檢測連續增加或減少的值
  ?支持兩個(gè)新的Prometheus預處理標簽匹配運算符!= 及 !~
  ?當從構件鏈接導航到列表樣式頁(yè)面時(shí),構件顯示能更可靠地轉換為不同的篩選器選項
  ?使用新配置參數ListenBacklog為Zabbix server、Zabbix proxy、Zabbix agent配置TCP隊列中掛起連接的最大數量
  ?文檔頁(yè)面字體和可讀性的改進(jìn)
  ?調整許多現有模板和修復小bug
  ?新增utf8mb4作為受支持的MySQL字符集和校對集
  ?新增對Webhook的額外HTTP方法的支持
  ?對Zabbix命令行工具的超時(shí)設置
  Zabbix官方首本工具書(shū)《Zabbix監控系統之深度解析和實(shí)踐》現已出版,歡迎閱讀。 查看全部

  文章采集api Zabbix 任性,文末送書(shū) X 5
  Zabbix版本不斷升級,以滿(mǎn)足日益增長(cháng)的用戶(hù)需求,支持高可用HA,k8s、指標topN、機器學(xué)習、定制前端品牌logo等!
  舊版本需要腳本才實(shí)現的功能,升級至最新版本可輕松解決!Zabbix6.0為業(yè)務(wù)服務(wù)提供商、DevOps和ITOps團隊提供了附加值,優(yōu)化了整體監控工作流程,并在許多不同層面提供了新見(jiàn)解。
  目錄
  業(yè)務(wù)服務(wù)監控達到全新高度
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  開(kāi)箱即用的Zabbix server高可用群集
  機器學(xué)習
  Kubernetes監控
  詳細高效的Zabbix審計日志模式
  可視化數據的新方法
  Zabbix性能優(yōu)化
  提升Zabbix Agent2模塊化,新的Zabbix Agent 監控項和功能
  原生TLS/SSL網(wǎng)站證書(shū)監控
  通用性改進(jìn)
  通過(guò)自定義密碼復雜程度要求來(lái)保護您的Zabbix登錄
  支持定制前端展示品牌logo
  新增模板和集成
  其它新功能和優(yōu)化
  01
  BMS業(yè)務(wù)服務(wù)監控達到全新高度
  優(yōu)化Services部分,顯示業(yè)務(wù)服務(wù)的狀態(tài)和當前SLA級別
  通過(guò)對現有Services頁(yè)面和功能的重大改進(jìn)和優(yōu)化,業(yè)務(wù)服務(wù)監控提升到了一個(gè)新高度。業(yè)務(wù)服務(wù)監控功能(BSM)非常適合多組件服務(wù)場(chǎng)景,例如服務(wù)器群集、負載平衡器和其它具有冗余組件的服務(wù)。
  Zabbix 6.0提供多種功能自定義業(yè)務(wù)服務(wù)樹(shù)實(shí)現BMS業(yè)務(wù)服務(wù)監控:
  ? 重新設計 Zabbix 6.0 Services頁(yè)面和功能
  ? 支持單個(gè)Zabbix實(shí)例監控超過(guò)10萬(wàn)個(gè)業(yè)務(wù)服務(wù)
  ? 支持新的靈活服務(wù)狀態(tài)計算邏輯
  ? 能夠自定義業(yè)務(wù)服務(wù)的訪(fǎng)問(wèn)權限
  ? 能夠為特定業(yè)務(wù)服務(wù)自定義只讀和讀寫(xiě)權限
  ? 業(yè)務(wù)服務(wù)權限既可以基于顯式服務(wù)列表,也可以基于服務(wù)標簽的訪(fǎng)問(wèn)限制
  ? 導出和導入業(yè)務(wù)服務(wù)樹(shù)
  ? 新的Service動(dòng)作類(lèi)型能讓用戶(hù)接收告警并對業(yè)務(wù)服務(wù)狀態(tài)更改作出反應
  02
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  提供大量可供選擇的服務(wù)狀態(tài)計算規則,能支持靈活的服務(wù)定義
  業(yè)務(wù)服務(wù)狀態(tài)計算邏輯在Zabbix 6.0中得到了極大擴展,增加了許多新功能,例如:
  ? 能夠為每項業(yè)務(wù)服務(wù)分配權重
  ? 僅當N個(gè)子服務(wù)都處于X嚴重級別的問(wèn)題狀態(tài)時(shí)才更改狀態(tài)
  ? 對處于問(wèn)題狀態(tài)下的子服務(wù)的權重進(jìn)行分析并作出反應
  ? 僅當特定百分比的子服務(wù)處于問(wèn)題狀態(tài)時(shí)才作出反應
  ? 其它計算規則
  用戶(hù)還可以自定義和訪(fǎng)問(wèn)指定服務(wù)的SLA報告。
  03
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  根因問(wèn)題會(huì )立即顯示在service下
  對業(yè)務(wù)服務(wù)執行根因分析。利用根因分析功能找出可能導致業(yè)務(wù)服務(wù)SLA下降的潛在問(wèn)題列表:
  ? 在Zabbix前端Services頁(yè)面查看根因問(wèn)題列表
  ? 接收告警中的根因問(wèn)題列表
  ? 通過(guò)Zabbix API收集根因問(wèn)題信息
  04
  開(kāi)箱即用的Zabbix server高可用群集
  在系統信息組件中跟蹤集群集節點(diǎn)狀態(tài)
  Zabbix server高可用防止硬件故障或計劃維護期的停機:
  ? 原生選擇加入HA群集配置
  ? 定義一個(gè)或多個(gè)備用節點(diǎn)
  
  ?實(shí)時(shí)監控Zabbix server群集節點(diǎn)的狀態(tài)
  ? 不需要外部工具即可將Zabbix server配置為HA群集模式
  05
  機器學(xué)習
  使用新函數對意外異常率或與指標基準的偏差做出反應
  新的基線(xiàn)監控和異常檢測趨勢功能以動(dòng)態(tài)方式檢測問(wèn)題,而不是靜態(tài)閾值方式:
  ?新的趨勢函數-baselinewma and baselinedev ,能計算指標基線(xiàn)和偏離值
  ?新的趨勢函數-trendstl,能檢測異常指標行為
  ?能夠指定異常檢測偏差算法及季節性
  06
  Kubernetes監控
  Zabbix 6.0 LTS添加了多個(gè)新模板,用于監控不同的Kubernetes組件
  Zabbix 6.0 LTS新增Kubernetes監控功能,可以在Kubernetes系統從多個(gè)維度采集指標:
  ?Kubernetes節點(diǎn)和pods的自動(dòng)發(fā)現和監控
  ?無(wú)代理方式采集Kubernetes pods和節點(diǎn)的信息
  ?獲取Kubernetes節點(diǎn)主機高水平信息
  Kubernetes監控還能夠監控Kubernetes組件,例如
  ?kube-controller-manager
  ?kube-proxy
  ?kube-apiserver
  ?kube-scheduler
  ?kubelet
  07
  詳細高效的Zabbix審計日志模式
  重新設計的審計日志能提供全新的詳細信息,并優(yōu)化篩選功能。
  新的審計日志模式允許用戶(hù)對Zabbix前端、Zabbix API和Zabbix server記錄執行詳細審計。通過(guò)修改審計日志,對Zabbix實(shí)例執行的所有更改都將記錄在審計日志中:
  ?創(chuàng )建、修改或刪除新對象
  ?通過(guò)LLD發(fā)現新實(shí)體
  ?API命令
  ?定期登錄/退出
  ?Zabbix實(shí)例中發(fā)生的所有其它事情
  新的審計日志模式在設計時(shí)考慮了最佳性能,因此擴展的功能不會(huì )影響Zabbix實(shí)例的性能。審計日志模式的工作是一項持續的工作,會(huì )在后續Zabbix發(fā)布周期中持續進(jìn)行。
  08
  可視化數據的新方法
  主機排序組件可顯示按監控項值排序的前N個(gè)或后N個(gè)主機的列表
  Zabbix 6.0新增的構件提供了展示信息的許多新方法。
  ?地理地圖構件能在地圖上顯示主機和問(wèn)題
  ?數據表構件能創(chuàng )建有關(guān)主機指標狀態(tài)的摘要視圖
  ?數據表構件的前N和后N函數能展示最高或最低的監控項值
  ?單一監控項構件能展示單個(gè)指標的值
  ?對現有矢量圖的許多改進(jìn),例如新的矢量圖類(lèi)型、引用單一監控項等
  ?SLA構件能顯示特定業(yè)務(wù)服務(wù)的當前SLA
  09
  Zabbix性能優(yōu)化
  針對不同的Zabbix組件進(jìn)行多項性能優(yōu)化:
  ?提升鏈接模板時(shí)的性能
  ?提升Zabbix proxy性能和內存使用率
  歷史數據表使用主鍵,這有多種好處,例如:
  ?提高Zabbix server和Zabbix前端的性能
  ?減少歷史數據表的大小
  10
  提升Zabbix Agent2模塊化,
  新的Zabbix Agent 監控項和功能
  優(yōu)化的Zabbix agent現在能夠開(kāi)箱即用監控一組指標
  Zabbix 6.0為Zabbix Agent和Agent2提供了一套新的監控項。支持以下功能:
  ?獲取額外文件信息,如文件所有者和文件權限
  ?采集agent主機元數據作為指標
  
  ?計數匹配的TCP/UDP sockets
  某些已有的監控項支持新的功能:
  ?vfs.fs.discovery-在Windows上添加了對{#FSLABEL}宏的支持
  ?vfs.fs.get-在Windows上添加了對{#FSLABEL}宏的支持
  ? vfs.file.size-添加了一個(gè)新的模式參數。設置以字節數或行數為單位
  Zabbix Agent2現在支持加載獨立插件,而無(wú)需重新編譯Agent2。
  11
  原生TLS/SSL網(wǎng)站證書(shū)監控
  使用新的Zabbix agent2 監控項監控SSL/TLS證書(shū)
  支持使用新的Zabbix agent 2監控項來(lái)監控SSL/TLS證書(shū)。監控項可用于驗證TLS/SSL證書(shū),并提供其它證書(shū)詳細信息。
  12
  通用性改進(jìn)
  通過(guò)優(yōu)化的創(chuàng )建主機UI,使創(chuàng )建新主機從未如此簡(jiǎn)單
  Zabbix 6.0使Zabbix配置工作流程更精簡(jiǎn)!Zabbix用戶(hù)現在可直接在Monitoring頁(yè)面創(chuàng )建主機和監控項:
  ?直接從Monitoring -Hosts頁(yè)面創(chuàng )建主機
  ?直接從Monitoring -Latest data頁(yè)面創(chuàng )建監控項
  ?刪除了Monitoring -Overview頁(yè)面。為了改善用戶(hù)體驗,現在只能通過(guò)儀表盤(pán)構件訪(fǎng)問(wèn)觸發(fā)器和數據概覽功能。
  現在將根據監控項的鍵值自動(dòng)選擇監控項的默認信息類(lèi)型。
  拓撲圖標簽和圖形名稱(chēng)中的簡(jiǎn)單宏已替換為表達式宏,以確保與新的觸發(fā)器表達式語(yǔ)法一致。
  13
  通過(guò)自定義密碼復雜程度要求
  來(lái)保護您的Zabbix登錄
  設置密碼復雜程度確保前端登錄安全
  Zabbix超級管理員現在能夠定義密碼復雜程度要求?,F在可以:
  ?設置最小密碼長(cháng)度
  ?定義密碼字符要求
  ?通過(guò)禁止使用最常見(jiàn)的密碼字符串來(lái)降低字典攻擊的風(fēng)險。
  14
  支持定制前端展示品牌logo
  定制Zabbix實(shí)例代表您的公司。將現有的Zabbix品牌和幫助頁(yè)面URL替換為您自己的公司品牌和自定義網(wǎng)站URL。
  改名功能不會(huì )違反Zabbix許可協(xié)議-可以自由更換Zabbix品牌!
  15
  新增模板和集成
  Zabbix 6.0為最受歡迎的供應商提供了許多新模板:
  ?f5 BIG-IP
  ?Cisco ASAv
  ?HPE ProLiant servers
  ?Cloudflare
  ?InfluxDB
  ?Travis CI
  ?Dell PowerEdge
  Zabbix 6.0還帶來(lái)了一個(gè)新的Github webhook集成,能基于Zabbix問(wèn)題或恢復事件生成Github問(wèn)題!
  所有官方的Zabbix模板現在都是獨立的,不需要依賴(lài)導入其他模板。
  請查看當前可用集成的完整列表。
  16
  其它新功能和優(yōu)化
  更多改進(jìn)功能(部分):
  ?使用新聚合函數計數返回值或匹配監控項的數量-count和item_count函數
  ?在未配置交換空間的情況下提升system.swap監控項行為
  ?使用新的單調歷史函數檢測連續增加或減少的值
  ?支持兩個(gè)新的Prometheus預處理標簽匹配運算符!= 及 !~
  ?當從構件鏈接導航到列表樣式頁(yè)面時(shí),構件顯示能更可靠地轉換為不同的篩選器選項
  ?使用新配置參數ListenBacklog為Zabbix server、Zabbix proxy、Zabbix agent配置TCP隊列中掛起連接的最大數量
  ?文檔頁(yè)面字體和可讀性的改進(jìn)
  ?調整許多現有模板和修復小bug
  ?新增utf8mb4作為受支持的MySQL字符集和校對集
  ?新增對Webhook的額外HTTP方法的支持
  ?對Zabbix命令行工具的超時(shí)設置
  Zabbix官方首本工具書(shū)《Zabbix監控系統之深度解析和實(shí)踐》現已出版,歡迎閱讀。

騰訊3面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 67 次瀏覽 ? 2022-06-28 05:55 ? 來(lái)自相關(guān)話(huà)題

  騰訊3面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在 騰訊三輪面試 的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計 ;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的 整體架構 和 可以做的亮點(diǎn)優(yōu)化 ;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀; 傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò) 削峰限流 和 數據加工 后,將原始日志存儲于 ES 中,再經(jīng)過(guò) 數據清洗 、數據聚合 后,將 issue(聚合的數據) 持久化存儲 于 MySQL ,最后提供 RESTful API 提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是 內核+插件 的插件式設計;每個(gè) SDK 首先繼承于平臺無(wú)關(guān)的 Core 層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè) web環(huán)境 下的數據,包括 Nodejs、微信小程序、Electron 等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè) SDK 可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護 ;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改 build 的腳本,對 packages 文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到 npm 平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK 也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件 的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加 Monitor 插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br />class?WebSdk?extends?Core?{<br />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br />??public?performanceInstance:?WebVitals;<br /><br />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br />??public?userInstance:?UserVitals;<br /><br />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br />??public?errorInstance:?ErrorVitals;<br /><br />??//?上報實(shí)例,這里面封裝上報方法<br />??public?transportInstance:?TransportInstance;<br /><br />??//?數據格式化實(shí)例<br />??public?builderInstance:?BuilderInstance;<br /><br />??//?維度實(shí)例,用以初始化?uid、sid等信息<br />??public?dimensionInstance:?DimensionInstance;<br /><br />??//?參數初始化實(shí)例<br />??public?configInstance:?ConfigInstance;<br /><br />??private?options:?initOptions;<br /><br />??constructor(options:?initOptions)?{<br />????super();<br />????this.configInstance?=?new?ConfigInstance(this,?options);<br />????//?各種初始化......<br />??}<br />}<br /><br />export?default?WebSdk;<br />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將 this 傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的 內核+插件 設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有 單元測試 的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對 每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè) SDK 都用 try catch 包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的 catch 里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò) JS 調用 new Date() 獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō) API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭 上有一個(gè)字段 Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的 Date 值后計算 Diff差值 存在本地;
  這樣子就可以提供一個(gè) 公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間 比較趨近于 服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br />export?const?diffTime?=?(date:?string)?=>?{<br />??const?serverDate?=?new?Date(date);<br />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br />??if?(diff?===?0?||?diff?>?inDiff)?{<br />????diff?=?inDiff;<br />??}<br />};<br /><br />export?const?getTime?=?()?=>?{<br />??return?new?Date(Date.now()?-?diff);<br />};<br />
  當然,這里還可以做的更精確一點(diǎn),我們可以讓后端服務(wù)在返回的時(shí)候,帶上 API 請求在后端服務(wù)執行完畢所消耗的時(shí)間 server-timing,放在響應頭里;我們取到數據后,將 ttfb 耗時(shí) 減去返回的 server-timing 再除以 2;就是單程傳輸的耗時(shí);那這樣我們上文的計算中差的 單程傳輸耗時(shí)的誤差 就可以補上了;
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成 錯誤mid 的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br />export?const?getErrorUid?=?(input:?string)?=>?{<br />??return?window.btoa(unescape(encodeURIComponent(input)));<br />};<br />
  
  所以說(shuō)我們傳入的參數,是 錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的 錯誤mid 都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br />??const?submitParams?=?{<br />????...data,<br />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br />??}?as?ExceptionMetrics;<br />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br />??if?(hasSubmitStatus)?return;<br />??this.submitErrorUids.push(submitParams.errorUid);<br />??//?記錄后清除?breadcrumbs<br />??this.engineInstance.userInstance.breadcrumbs.clear();<br />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br />??this.engineInstance.transportInstance.kernelTransportHandler(<br />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br />??);<br />};<br />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先 Beacon API[6] 是一個(gè)較新的 API
  然后 Ajax 請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是 Ajax;
  最后來(lái)說(shuō)一下 Image 上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用 sendBeacon + xmlHttpRequest 降級上報的方式,當瀏覽器不支持 sendBeacon 或者 傳輸的數據量超過(guò)了 sendBeacon 的限制,我們就降級采用 xmlHttpRequest 進(jìn)行上報數據;
  優(yōu)先選用 Beacon API 的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載 的傳輸方式。
  而降級使用 XMLHttpRequest 的原因是, Beacon API 現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且 sendbeacon 不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到 Ajax 來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用 Image 呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br />??//?PV訪(fǎng)問(wèn)數據<br />??PV?=?'pv',<br />??//?性能數據<br />??PERF?=?'perf',<br />??//?api?請求數據<br />??API?=?'api',<br />??//?報錯數據<br />??ERROR?=?'error',<br />??//?自定義行為<br />??CUS?=?'custom',<br />}<br /><br />export?interface?DimensionStructure?{<br />??//?用戶(hù)id,存儲于cookie<br />??uid:?string;<br />??//?會(huì )話(huà)id,存儲于cookiestorage<br />??sid:?string;<br />??//?應用id,使用方傳入<br />??pid:?string;<br />??//?應用版本號<br />??release:?string;<br />??//?應用環(huán)境<br />??environment:?string;<br />}<br /><br />export?interface?TransportStructure?{<br />??//?上報類(lèi)別<br />??category:?transportCategory;<br />??//?上報的維度信息<br />??dimension:?DimensionStructure;<br />??//?上報對象(正文)<br />??context?:?Object;<br />??//?上報對象數組<br />??contexts?:?Array;<br />??//?捕獲的sdk版本信息,版本號等...<br />??sdk:?Object;<br />}<br /><br />export?default?class?TransportInstance?{<br />??private?engineInstance:?EngineInstance;<br /><br />??public?kernelTransportHandler:?Function;<br /><br />??private?options:?TransportParams;<br /><br />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br />????this.engineInstance?=?engineInstance;<br />????this.options?=?options;<br />????this.kernelTransportHandler?=?this.initTransportHandler();<br />??}<br /><br />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br />????const?transportStructure?=?{<br />??????category,<br />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br />??????sdk:?getSdkVersion(),<br />????}?as?TransportStructure;<br />????if?(data?instanceof?Array)?{<br />??????transportStructure.contexts?=?data;<br />????}?else?{<br />??????transportStructure.context?=?data;<br />????}<br />????return?transportStructure;<br />??};<br /><br />??//?初始化上報方法<br />??initTransportHandler?=?()?=>?{<br />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br />??};<br /><br />??//?beacon?形式上報<br />??beaconTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br />??????if?(!status)?this.xmlTransport().apply(this,?data);<br />????};<br />????return?handler;<br />??};<br /><br />??//?XMLHttpRequest?形式上報<br />??xmlTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br />??????xhr.open('POST',?this.options.transportUrl,?true);<br />??????xhr.send(JSON.stringify(data));<br />????};<br />????return?handler;<br />??};<br />}<br />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是 Math.random() ,我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random() 查看全部

  騰訊3面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在 騰訊三輪面試 的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計 ;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的 整體架構 和 可以做的亮點(diǎn)優(yōu)化 ;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀; 傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò) 削峰限流 和 數據加工 后,將原始日志存儲于 ES 中,再經(jīng)過(guò) 數據清洗 、數據聚合 后,將 issue(聚合的數據) 持久化存儲 于 MySQL ,最后提供 RESTful API 提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是 內核+插件 的插件式設計;每個(gè) SDK 首先繼承于平臺無(wú)關(guān)的 Core 層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè) web環(huán)境 下的數據,包括 Nodejs、微信小程序、Electron 等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè) SDK 可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護 ;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改 build 的腳本,對 packages 文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到 npm 平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK 也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件 的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加 Monitor 插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br />class?WebSdk?extends?Core?{<br />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br />??public?performanceInstance:?WebVitals;<br /><br />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br />??public?userInstance:?UserVitals;<br /><br />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br />??public?errorInstance:?ErrorVitals;<br /><br />??//?上報實(shí)例,這里面封裝上報方法<br />??public?transportInstance:?TransportInstance;<br /><br />??//?數據格式化實(shí)例<br />??public?builderInstance:?BuilderInstance;<br /><br />??//?維度實(shí)例,用以初始化?uid、sid等信息<br />??public?dimensionInstance:?DimensionInstance;<br /><br />??//?參數初始化實(shí)例<br />??public?configInstance:?ConfigInstance;<br /><br />??private?options:?initOptions;<br /><br />??constructor(options:?initOptions)?{<br />????super();<br />????this.configInstance?=?new?ConfigInstance(this,?options);<br />????//?各種初始化......<br />??}<br />}<br /><br />export?default?WebSdk;<br />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將 this 傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的 內核+插件 設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有 單元測試 的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對 每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè) SDK 都用 try catch 包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的 catch 里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò) JS 調用 new Date() 獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō) API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭 上有一個(gè)字段 Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的 Date 值后計算 Diff差值 存在本地;
  這樣子就可以提供一個(gè) 公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間 比較趨近于 服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br />export?const?diffTime?=?(date:?string)?=>?{<br />??const?serverDate?=?new?Date(date);<br />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br />??if?(diff?===?0?||?diff?>?inDiff)?{<br />????diff?=?inDiff;<br />??}<br />};<br /><br />export?const?getTime?=?()?=>?{<br />??return?new?Date(Date.now()?-?diff);<br />};<br />
  當然,這里還可以做的更精確一點(diǎn),我們可以讓后端服務(wù)在返回的時(shí)候,帶上 API 請求在后端服務(wù)執行完畢所消耗的時(shí)間 server-timing,放在響應頭里;我們取到數據后,將 ttfb 耗時(shí) 減去返回的 server-timing 再除以 2;就是單程傳輸的耗時(shí);那這樣我們上文的計算中差的 單程傳輸耗時(shí)的誤差 就可以補上了;
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成 錯誤mid 的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br />export?const?getErrorUid?=?(input:?string)?=>?{<br />??return?window.btoa(unescape(encodeURIComponent(input)));<br />};<br />
  
  所以說(shuō)我們傳入的參數,是 錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的 錯誤mid 都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br />??const?submitParams?=?{<br />????...data,<br />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br />??}?as?ExceptionMetrics;<br />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br />??if?(hasSubmitStatus)?return;<br />??this.submitErrorUids.push(submitParams.errorUid);<br />??//?記錄后清除?breadcrumbs<br />??this.engineInstance.userInstance.breadcrumbs.clear();<br />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br />??this.engineInstance.transportInstance.kernelTransportHandler(<br />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br />??);<br />};<br />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先 Beacon API[6] 是一個(gè)較新的 API
  然后 Ajax 請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是 Ajax;
  最后來(lái)說(shuō)一下 Image 上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用 sendBeacon + xmlHttpRequest 降級上報的方式,當瀏覽器不支持 sendBeacon 或者 傳輸的數據量超過(guò)了 sendBeacon 的限制,我們就降級采用 xmlHttpRequest 進(jìn)行上報數據;
  優(yōu)先選用 Beacon API 的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載 的傳輸方式。
  而降級使用 XMLHttpRequest 的原因是, Beacon API 現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且 sendbeacon 不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到 Ajax 來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用 Image 呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br />??//?PV訪(fǎng)問(wèn)數據<br />??PV?=?'pv',<br />??//?性能數據<br />??PERF?=?'perf',<br />??//?api?請求數據<br />??API?=?'api',<br />??//?報錯數據<br />??ERROR?=?'error',<br />??//?自定義行為<br />??CUS?=?'custom',<br />}<br /><br />export?interface?DimensionStructure?{<br />??//?用戶(hù)id,存儲于cookie<br />??uid:?string;<br />??//?會(huì )話(huà)id,存儲于cookiestorage<br />??sid:?string;<br />??//?應用id,使用方傳入<br />??pid:?string;<br />??//?應用版本號<br />??release:?string;<br />??//?應用環(huán)境<br />??environment:?string;<br />}<br /><br />export?interface?TransportStructure?{<br />??//?上報類(lèi)別<br />??category:?transportCategory;<br />??//?上報的維度信息<br />??dimension:?DimensionStructure;<br />??//?上報對象(正文)<br />??context?:?Object;<br />??//?上報對象數組<br />??contexts?:?Array;<br />??//?捕獲的sdk版本信息,版本號等...<br />??sdk:?Object;<br />}<br /><br />export?default?class?TransportInstance?{<br />??private?engineInstance:?EngineInstance;<br /><br />??public?kernelTransportHandler:?Function;<br /><br />??private?options:?TransportParams;<br /><br />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br />????this.engineInstance?=?engineInstance;<br />????this.options?=?options;<br />????this.kernelTransportHandler?=?this.initTransportHandler();<br />??}<br /><br />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br />????const?transportStructure?=?{<br />??????category,<br />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br />??????sdk:?getSdkVersion(),<br />????}?as?TransportStructure;<br />????if?(data?instanceof?Array)?{<br />??????transportStructure.contexts?=?data;<br />????}?else?{<br />??????transportStructure.context?=?data;<br />????}<br />????return?transportStructure;<br />??};<br /><br />??//?初始化上報方法<br />??initTransportHandler?=?()?=>?{<br />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br />??};<br /><br />??//?beacon?形式上報<br />??beaconTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br />??????if?(!status)?this.xmlTransport().apply(this,?data);<br />????};<br />????return?handler;<br />??};<br /><br />??//?XMLHttpRequest?形式上報<br />??xmlTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br />??????xhr.open('POST',?this.options.transportUrl,?true);<br />??????xhr.send(JSON.stringify(data));<br />????};<br />????return?handler;<br />??};<br />}<br />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是 Math.random() ,我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random()

騰訊三面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 104 次瀏覽 ? 2022-06-27 21:05 ? 來(lái)自相關(guān)話(huà)題

  騰訊三面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在騰訊三輪面試的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的整體架構和可以做的亮點(diǎn)優(yōu)化;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀;傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò)削峰限流和數據加工后,將原始日志存儲于ES中,再經(jīng)過(guò)數據清洗、數據聚合后,將issue(聚合的數據)持久化存儲于MySQL,最后提供RESTful API提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是內核+插件的插件式設計;每個(gè)SDK首先繼承于平臺無(wú)關(guān)的Core層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè)web環(huán)境下的數據,包括Nodejs、微信小程序、Electron等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè)SDK可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改build的腳本,對packages文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到npm平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加Monitor插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />class?WebSdk?extends?Core?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?performanceInstance:?WebVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?userInstance:?UserVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?errorInstance:?ErrorVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報實(shí)例,這里面封裝上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?transportInstance:?TransportInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?數據格式化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?builderInstance:?BuilderInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?維度實(shí)例,用以初始化?uid、sid等信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?dimensionInstance:?DimensionInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?參數初始化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?configInstance:?ConfigInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?initOptions;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(options:?initOptions)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????super();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.configInstance?=?new?ConfigInstance(this,?options);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//?各種初始化......<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?WebSdk;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將this傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的內核+插件設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有單元測試的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè)SDK都用try catch包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的catch里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò)JS調用new Date()獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō)API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭上有一個(gè)字段Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的Date值后計算Diff差值存在本地;
  這樣子就可以提供一個(gè)公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間比較趨近于服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?diffTime?=?(date:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?serverDate?=?new?Date(date);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(diff?===?0?||?diff?>?inDiff)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????diff?=?inDiff;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getTime?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?new?Date(Date.now()?-?diff);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成錯誤mid的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getErrorUid?=?(input:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?window.btoa(unescape(encodeURIComponent(input)));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  所以說(shuō)我們傳入的參數,是錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的錯誤mid都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?submitParams?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????...data,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}?as?ExceptionMetrics;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(hasSubmitStatus)?return;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.submitErrorUids.push(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?記錄后清除?breadcrumbs<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.userInstance.breadcrumbs.clear();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.transportInstance.kernelTransportHandler(<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先Beacon API[6]是一個(gè)較新的 API
  然后Ajax請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是Ajax;
  最后來(lái)說(shuō)一下Image上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用sendBeacon+xmlHttpRequest降級上報的方式,當瀏覽器不支持sendBeacon或者傳輸的數據量超過(guò)了sendBeacon的限制,我們就降級采用xmlHttpRequest進(jìn)行上報數據;
  優(yōu)先選用Beacon API的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載的傳輸方式。
  而降級使用XMLHttpRequest的原因是,Beacon API現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且sendbeacon不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到Ajax來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用Image呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?PV訪(fǎng)問(wèn)數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PV?=?'pv',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PERF?=?'perf',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?api?請求數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??API?=?'api',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?報錯數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??ERROR?=?'error',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?自定義行為<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??CUS?=?'custom',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?DimensionStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?用戶(hù)id,存儲于cookie<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??uid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?會(huì )話(huà)id,存儲于cookiestorage<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用id,使用方傳入<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??pid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用版本號<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??release:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用環(huán)境<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??environment:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?TransportStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報類(lèi)別<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??category:?transportCategory;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報的維度信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??dimension:?DimensionStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象(正文)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??context?:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象數組<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??contexts?:?Array;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?捕獲的sdk版本信息,版本號等...<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sdk:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?class?TransportInstance?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?engineInstance:?EngineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?kernelTransportHandler:?Function;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?TransportParams;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance?=?engineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.options?=?options;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.kernelTransportHandler?=?this.initTransportHandler();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?transportStructure?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????category,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????sdk:?getSdkVersion(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?as?TransportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????if?(data?instanceof?Array)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.contexts?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?else?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.context?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?transportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?初始化上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??initTransportHandler?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?beacon?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??beaconTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????if?(!status)?this.xmlTransport().apply(this,?data);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?XMLHttpRequest?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??xmlTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.open('POST',?this.options.transportUrl,?true);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.send(JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是Math.random(),我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random() 查看全部

  騰訊三面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在騰訊三輪面試的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的整體架構和可以做的亮點(diǎn)優(yōu)化;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀;傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò)削峰限流和數據加工后,將原始日志存儲于ES中,再經(jīng)過(guò)數據清洗、數據聚合后,將issue(聚合的數據)持久化存儲于MySQL,最后提供RESTful API提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是內核+插件的插件式設計;每個(gè)SDK首先繼承于平臺無(wú)關(guān)的Core層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè)web環(huán)境下的數據,包括Nodejs、微信小程序、Electron等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè)SDK可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改build的腳本,對packages文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到npm平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加Monitor插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />class?WebSdk?extends?Core?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?performanceInstance:?WebVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?userInstance:?UserVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?errorInstance:?ErrorVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報實(shí)例,這里面封裝上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?transportInstance:?TransportInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?數據格式化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?builderInstance:?BuilderInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?維度實(shí)例,用以初始化?uid、sid等信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?dimensionInstance:?DimensionInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?參數初始化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?configInstance:?ConfigInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?initOptions;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(options:?initOptions)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????super();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.configInstance?=?new?ConfigInstance(this,?options);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//?各種初始化......<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?WebSdk;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將this傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的內核+插件設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有單元測試的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè)SDK都用try catch包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的catch里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò)JS調用new Date()獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō)API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭上有一個(gè)字段Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的Date值后計算Diff差值存在本地;
  這樣子就可以提供一個(gè)公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間比較趨近于服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?diffTime?=?(date:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?serverDate?=?new?Date(date);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(diff?===?0?||?diff?>?inDiff)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????diff?=?inDiff;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getTime?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?new?Date(Date.now()?-?diff);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成錯誤mid的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getErrorUid?=?(input:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?window.btoa(unescape(encodeURIComponent(input)));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  所以說(shuō)我們傳入的參數,是錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的錯誤mid都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?submitParams?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????...data,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}?as?ExceptionMetrics;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(hasSubmitStatus)?return;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.submitErrorUids.push(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?記錄后清除?breadcrumbs<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.userInstance.breadcrumbs.clear();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.transportInstance.kernelTransportHandler(<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先Beacon API[6]是一個(gè)較新的 API
  然后Ajax請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是Ajax;
  最后來(lái)說(shuō)一下Image上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用sendBeacon+xmlHttpRequest降級上報的方式,當瀏覽器不支持sendBeacon或者傳輸的數據量超過(guò)了sendBeacon的限制,我們就降級采用xmlHttpRequest進(jìn)行上報數據;
  優(yōu)先選用Beacon API的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載的傳輸方式。
  而降級使用XMLHttpRequest的原因是,Beacon API現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且sendbeacon不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到Ajax來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用Image呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?PV訪(fǎng)問(wèn)數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PV?=?'pv',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PERF?=?'perf',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?api?請求數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??API?=?'api',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?報錯數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??ERROR?=?'error',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?自定義行為<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??CUS?=?'custom',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?DimensionStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?用戶(hù)id,存儲于cookie<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??uid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?會(huì )話(huà)id,存儲于cookiestorage<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用id,使用方傳入<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??pid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用版本號<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??release:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用環(huán)境<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??environment:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?TransportStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報類(lèi)別<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??category:?transportCategory;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報的維度信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??dimension:?DimensionStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象(正文)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??context?:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象數組<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??contexts?:?Array;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?捕獲的sdk版本信息,版本號等...<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sdk:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?class?TransportInstance?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?engineInstance:?EngineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?kernelTransportHandler:?Function;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?TransportParams;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance?=?engineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.options?=?options;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.kernelTransportHandler?=?this.initTransportHandler();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?transportStructure?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????category,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????sdk:?getSdkVersion(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?as?TransportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????if?(data?instanceof?Array)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.contexts?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?else?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.context?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?transportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?初始化上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??initTransportHandler?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?beacon?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??beaconTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????if?(!status)?this.xmlTransport().apply(this,?data);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?XMLHttpRequest?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??xmlTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.open('POST',?this.options.transportUrl,?true);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.send(JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是Math.random(),我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random()

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 154 次瀏覽 ? 2022-06-26 01:10 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。 查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。

信息收集組合拳之從廢棄接口中尋找漏洞

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 67 次瀏覽 ? 2022-06-21 06:53 ? 來(lái)自相關(guān)話(huà)題

  信息收集組合拳之從廢棄接口中尋找漏洞
  使用OneForAll工具通過(guò)域名收集網(wǎng)址和ip
  工具地址:GitHub - shmilylty/OneForAll: OneForAll是一款功能強大的子域收集工具
  常用命令:python oneforall.py --targets targets.txt run
  targets.txt中放入需要掃描的域名,運行后會(huì )在results文件夾下生成掃描結果
  在運行結果中我們可以看到有url、子域名、ip
  其中運行結果中ip是一行多個(gè)、還有重復的,我們需要提取ip轉換成每行一個(gè)ip且沒(méi)有重復的txt文本中,方便其他工具掃描
  腳本:刪除重復ip
  #!/usr/bin/env python# conding:utf-8<br />##把同一行的ip換行,然后寫(xiě)進(jìn)result.txt的文件里with open('ip.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): with open('result.txt','a',encoding='utf-8') as writelist: b = dirs.replace(",", '\n') writelist.write(b)<br />#去除重復ip,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://blog.csdn.net/qq_22764 ... s%3D1
  提取成這樣單行一個(gè)ip且不重復的文本,我們就可以放到goby、fscan、小米范等工具中掃描
  
  fscan工具掃描ip
  工具地址:GitHub - shadow1ng/fscan: 一款內網(wǎng)綜合掃描工具,方便一鍵自動(dòng)化、全方位漏掃掃描。
  這款工具主要是用于內網(wǎng)掃描,發(fā)現資產(chǎn)以及進(jìn)行漏洞掃描與弱口令爆破,運行速度很快,用于外網(wǎng)探測發(fā)現一些web資產(chǎn)也是不錯的選擇
  常用命令:全端口掃描 fscan64.exe -hf ip.txt -p 1-65535 -o result.txt
  ip.txt中放入需要掃描的ip地址,result.txt為運行結果
  
  小米范
  工具地址:我的小工具 - 標簽 - 范世強 - 博客園
 ?。ㄕ也坏竭@個(gè)版本的地址了,就貼個(gè)作者的博客地址吧)
  JSFinder掃描js及url
  工具地址:GitHub - Threezh1/JSFinder: JSFinder is a tool for quickly extracting URLs and subdomains from JS files on a website.
  常用命令:python JSFinder.py -f targets.txt -d -ou JSurl.txt -os JSdomain.txt
  targets.txt中放入需要掃描的url,運行結束后生會(huì )成兩個(gè)txt文本, JSurl.txt為URL,JSdomain.txt為子域名
  上面這些工具的掃描結果中含有很多的url,我們需要效率高一些的話(huà)我們可以?xún)?yōu)先從參數下手,于是需要篩選含有參數的url
  腳本:提取含有參數的url
  #!/usr/bin/env python# conding:utf-8<br />#字符串中有“?”且不在字符串的結尾的就寫(xiě)入result.txt中with open('JSurl.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): # re_result=re.search(r"'?'",dirs) # re_result=str(re_result) if "?" in dirs :#判斷字符中是否有“?”,如果有則返回該字符串的位置,是從坐標0開(kāi)始算的 re = dirs.find("?") # a=len(dirs)-2是為了判斷“?”是不是在最后一個(gè)字符,len()與find()不同是從一開(kāi)始算字符串的長(cháng)度的,在加上每行字符中\n換行符也占了一個(gè)字符,所以要減2 a=len(dirs)-2#判斷字符串中“?”是不是在字符的最后 if re < a : with open('result.txt','a',encoding='utf-8') as writelist: writelist.write(dirs)<br />#去除result.txt中的重復字符串,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://www.cnblogs.com/luguankun/p/11846401.html(判斷一個(gè)字符是否在一個(gè)字符串中)
  <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  運行完腳本生成的含有參數的url如下所示
  從廢棄接口中尋找漏洞
  有些網(wǎng)站經(jīng)過(guò)了多輪滲透,正常業(yè)務(wù)都測爛的,連邏輯漏洞都找不到,經(jīng)歷了上面一番信息收集后,一般能收集到網(wǎng)站的歷史業(yè)務(wù)中的url。
  然后我們將url使用腳本處理篩選之后的結果可以批量的放到sqlmap中跑,還有一些敏感接口可以嘗試尋找越權、信息泄露等。
  sqlmap批量掃描
  常用命令:python sqlmap.py -m urls.txt --batch
  在urls.txt文件內放入我們使用“提取含有參數的url”這個(gè)腳本篩選后的url
  除了參數以外也可以用同樣的思路把腳本修改一下去找敏感接口與url跳轉參數等
  常見(jiàn)敏感接口
  常見(jiàn)跳轉參數
  toUrl=
  login_url=
  register_url
  redirect_url=
  load_url=
  proxy_url=
  file_url=
  jump_url=
  某次項目中客戶(hù)都疑惑我怎么找到的接口
  
  轉載于:
  推薦↓↓↓ 查看全部

  信息收集組合拳之從廢棄接口中尋找漏洞
  使用OneForAll工具通過(guò)域名收集網(wǎng)址和ip
  工具地址:GitHub - shmilylty/OneForAll: OneForAll是一款功能強大的子域收集工具
  常用命令:python oneforall.py --targets targets.txt run
  targets.txt中放入需要掃描的域名,運行后會(huì )在results文件夾下生成掃描結果
  在運行結果中我們可以看到有url、子域名、ip
  其中運行結果中ip是一行多個(gè)、還有重復的,我們需要提取ip轉換成每行一個(gè)ip且沒(méi)有重復的txt文本中,方便其他工具掃描
  腳本:刪除重復ip
  #!/usr/bin/env python# conding:utf-8<br />##把同一行的ip換行,然后寫(xiě)進(jìn)result.txt的文件里with open('ip.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): with open('result.txt','a',encoding='utf-8') as writelist: b = dirs.replace(",", '\n') writelist.write(b)<br />#去除重復ip,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://blog.csdn.net/qq_22764 ... s%3D1
  提取成這樣單行一個(gè)ip且不重復的文本,我們就可以放到goby、fscan、小米范等工具中掃描
  
  fscan工具掃描ip
  工具地址:GitHub - shadow1ng/fscan: 一款內網(wǎng)綜合掃描工具,方便一鍵自動(dòng)化、全方位漏掃掃描。
  這款工具主要是用于內網(wǎng)掃描,發(fā)現資產(chǎn)以及進(jìn)行漏洞掃描與弱口令爆破,運行速度很快,用于外網(wǎng)探測發(fā)現一些web資產(chǎn)也是不錯的選擇
  常用命令:全端口掃描 fscan64.exe -hf ip.txt -p 1-65535 -o result.txt
  ip.txt中放入需要掃描的ip地址,result.txt為運行結果
  
  小米范
  工具地址:我的小工具 - 標簽 - 范世強 - 博客園
 ?。ㄕ也坏竭@個(gè)版本的地址了,就貼個(gè)作者的博客地址吧)
  JSFinder掃描js及url
  工具地址:GitHub - Threezh1/JSFinder: JSFinder is a tool for quickly extracting URLs and subdomains from JS files on a website.
  常用命令:python JSFinder.py -f targets.txt -d -ou JSurl.txt -os JSdomain.txt
  targets.txt中放入需要掃描的url,運行結束后生會(huì )成兩個(gè)txt文本, JSurl.txt為URL,JSdomain.txt為子域名
  上面這些工具的掃描結果中含有很多的url,我們需要效率高一些的話(huà)我們可以?xún)?yōu)先從參數下手,于是需要篩選含有參數的url
  腳本:提取含有參數的url
  #!/usr/bin/env python# conding:utf-8<br />#字符串中有“?”且不在字符串的結尾的就寫(xiě)入result.txt中with open('JSurl.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): # re_result=re.search(r"'?'",dirs) # re_result=str(re_result) if "?" in dirs :#判斷字符中是否有“?”,如果有則返回該字符串的位置,是從坐標0開(kāi)始算的 re = dirs.find("?") # a=len(dirs)-2是為了判斷“?”是不是在最后一個(gè)字符,len()與find()不同是從一開(kāi)始算字符串的長(cháng)度的,在加上每行字符中\n換行符也占了一個(gè)字符,所以要減2 a=len(dirs)-2#判斷字符串中“?”是不是在字符的最后 if re < a : with open('result.txt','a',encoding='utf-8') as writelist: writelist.write(dirs)<br />#去除result.txt中的重復字符串,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://www.cnblogs.com/luguankun/p/11846401.html(判斷一個(gè)字符是否在一個(gè)字符串中)
  <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  運行完腳本生成的含有參數的url如下所示
  從廢棄接口中尋找漏洞
  有些網(wǎng)站經(jīng)過(guò)了多輪滲透,正常業(yè)務(wù)都測爛的,連邏輯漏洞都找不到,經(jīng)歷了上面一番信息收集后,一般能收集到網(wǎng)站的歷史業(yè)務(wù)中的url。
  然后我們將url使用腳本處理篩選之后的結果可以批量的放到sqlmap中跑,還有一些敏感接口可以嘗試尋找越權、信息泄露等。
  sqlmap批量掃描
  常用命令:python sqlmap.py -m urls.txt --batch
  在urls.txt文件內放入我們使用“提取含有參數的url”這個(gè)腳本篩選后的url
  除了參數以外也可以用同樣的思路把腳本修改一下去找敏感接口與url跳轉參數等
  常見(jiàn)敏感接口
  常見(jiàn)跳轉參數
  toUrl=
  login_url=
  register_url
  redirect_url=
  load_url=
  proxy_url=
  file_url=
  jump_url=
  某次項目中客戶(hù)都疑惑我怎么找到的接口
  
  轉載于:
  推薦↓↓↓

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 119 次瀏覽 ? 2022-06-19 21:31 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。 查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。

圖數據庫無(wú)縫集成Tushare接口

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 74 次瀏覽 ? 2022-06-11 03:29 ? 來(lái)自相關(guān)話(huà)題

  圖數據庫無(wú)縫集成Tushare接口
  
  @TOC[1]Here's the table of contents:
  圖數據庫無(wú)縫集成Tushare接口
  使用第三方API,有助于我們快速集成數據,構建業(yè)務(wù)分析需要的知識圖譜數據。這篇文章主要介紹如何將Tushare HTTP接口集成到圖數據庫,并使用Cypher構建知識圖譜。
  在開(kāi)始集成前,請確保你的圖數據庫安裝了APOC組件,并保證apoc.load.jsonParams過(guò)程可以正常使用。APOC同時(shí)還支持JSON Path,即以特定模式搜索JSON文檔中的數據項并返回其內容,其概念類(lèi)似應用于XML的XPath和應用于HTML的jQuery。更多使用方式可以查看Neo4j社區技術(shù)專(zhuān)家俞博士的文章Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據[2]。
  一、Tushare介紹
  Tushare[3]是一個(gè)免費、開(kāi)源的python財經(jīng)數據接口包。主要實(shí)現對股票等金融數據從數據采集、清洗加工到數據存儲的過(guò)程,能夠為金融分析人員提供快速、整潔、和多樣的便于分析的數據,為他們在數據獲取方面極大地減輕工作量,使他們更加專(zhuān)注于策略和模型的研究與實(shí)現上。
  
  Tushare官網(wǎng)二、集成Tushare接口
  在開(kāi)始集成之前請確保你有一個(gè)Tushare的訪(fǎng)問(wèn)賬號。
  在圖數據庫的安裝目錄下找到conf文件夾,并在neo4j.conf文件中為T(mén)ushare HTTP API的URL定義別名,新增一行配置即可。修改配置后,重啟數據庫服務(wù)即可。
  apoc.json.tushare.url=http://api.tushare.pro
  三、使用接口數據
  現在我們可以編寫(xiě)Cypher代碼很方便地從Tushare獲取數據了。下面我將演示一個(gè)申萬(wàn)成分股圖譜構建的案例。請注意在使用Cypher腳本時(shí)請設置私有token。
  下面的代碼通過(guò)分批循環(huán)調用首先從stock_basic接口獲取到股票代碼,然后再使用股票代碼獲取申萬(wàn)成分股時(shí)間序列數據。每個(gè)股票代碼調用index_member接口之前,設置了執行四百萬(wàn)次加法運算表示進(jìn)行延時(shí)1~2秒,這個(gè)操作的目的是為了保證HTTP接口調用時(shí)不要超過(guò)接口頻率限制。
  //循環(huán)獲取所有股票代碼<br />WITH?RANGE(1,10)?AS?list,1000?AS?limit<br />UNWIND?list?AS?num<br />WITH?num*limit?AS?offset,limit<br />//分批獲取股票代碼<br />WITH?<br />????'{"api_name":"stock_basic","token":"xxxxxxxxxxx","params":{"limit":"'+limit+'","offset":"'+offset+'"},"fields":"ts_code"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.has_more?AS?has_more,value.data.items?AS?stocks<br />WHERE?has_more<br />//獲取申萬(wàn)成分數據<br />WITH?has_more,stocks<br />UNWIND?stocks?AS?stock<br />WITH?stock<br />//延時(shí)執行【HTTP?API對調用頻率有限制,讓函數執行四百萬(wàn)次加法,耗時(shí)大概1~2秒】<br />WITH?RANGE(1,2000)?AS?l,stock?UNWIND?l?AS?a?UNWIND?l?AS?b?WITH?SUM(a+b)?AS?delay,stock<br />WITH?<br />????'{"api_name":"index_member","token":"xxxxxxxxxxx","params":{"ts_code":"'+stock[0]+'"},"fields":"index_code,index_name,con_code,con_name,in_date,out_date,is_new"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.items?AS?items<br />//構建申萬(wàn)行業(yè)成分股圖譜<br />UNWIND?items?AS?item<br />WITH?item<br />MERGE?(stk:股票?{code:item[2]})?SET?stk+={name:item[3]+'('+item[2]+')'}<br />MERGE?(hy:申萬(wàn)行業(yè)?{code:item[0]})?SET?hy+={name:item[1]+'('+item[0]+')'}<br />CREATE?(stk)-[r:屬于]->(hy)?SET?r+={in_date:item[4],out_date:item[5],is_new:item[6]}
  
  申萬(wàn)行業(yè)成分股時(shí)序圖譜引用鏈接
  [1]TOC:圖數據庫無(wú)縫集成Tushare接口
  [2]Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據:
  [3]Tushare:
   查看全部

  圖數據庫無(wú)縫集成Tushare接口
  
  @TOC[1]Here's the table of contents:
  圖數據庫無(wú)縫集成Tushare接口
  使用第三方API,有助于我們快速集成數據,構建業(yè)務(wù)分析需要的知識圖譜數據。這篇文章主要介紹如何將Tushare HTTP接口集成到圖數據庫,并使用Cypher構建知識圖譜。
  在開(kāi)始集成前,請確保你的圖數據庫安裝了APOC組件,并保證apoc.load.jsonParams過(guò)程可以正常使用。APOC同時(shí)還支持JSON Path,即以特定模式搜索JSON文檔中的數據項并返回其內容,其概念類(lèi)似應用于XML的XPath和應用于HTML的jQuery。更多使用方式可以查看Neo4j社區技術(shù)專(zhuān)家俞博士的文章Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據[2]。
  一、Tushare介紹
  Tushare[3]是一個(gè)免費、開(kāi)源的python財經(jīng)數據接口包。主要實(shí)現對股票等金融數據從數據采集、清洗加工到數據存儲的過(guò)程,能夠為金融分析人員提供快速、整潔、和多樣的便于分析的數據,為他們在數據獲取方面極大地減輕工作量,使他們更加專(zhuān)注于策略和模型的研究與實(shí)現上。
  
  Tushare官網(wǎng)二、集成Tushare接口
  在開(kāi)始集成之前請確保你有一個(gè)Tushare的訪(fǎng)問(wèn)賬號。
  在圖數據庫的安裝目錄下找到conf文件夾,并在neo4j.conf文件中為T(mén)ushare HTTP API的URL定義別名,新增一行配置即可。修改配置后,重啟數據庫服務(wù)即可。
  apoc.json.tushare.url=http://api.tushare.pro
  三、使用接口數據
  現在我們可以編寫(xiě)Cypher代碼很方便地從Tushare獲取數據了。下面我將演示一個(gè)申萬(wàn)成分股圖譜構建的案例。請注意在使用Cypher腳本時(shí)請設置私有token。
  下面的代碼通過(guò)分批循環(huán)調用首先從stock_basic接口獲取到股票代碼,然后再使用股票代碼獲取申萬(wàn)成分股時(shí)間序列數據。每個(gè)股票代碼調用index_member接口之前,設置了執行四百萬(wàn)次加法運算表示進(jìn)行延時(shí)1~2秒,這個(gè)操作的目的是為了保證HTTP接口調用時(shí)不要超過(guò)接口頻率限制。
  //循環(huán)獲取所有股票代碼<br />WITH?RANGE(1,10)?AS?list,1000?AS?limit<br />UNWIND?list?AS?num<br />WITH?num*limit?AS?offset,limit<br />//分批獲取股票代碼<br />WITH?<br />????'{"api_name":"stock_basic","token":"xxxxxxxxxxx","params":{"limit":"'+limit+'","offset":"'+offset+'"},"fields":"ts_code"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.has_more?AS?has_more,value.data.items?AS?stocks<br />WHERE?has_more<br />//獲取申萬(wàn)成分數據<br />WITH?has_more,stocks<br />UNWIND?stocks?AS?stock<br />WITH?stock<br />//延時(shí)執行【HTTP?API對調用頻率有限制,讓函數執行四百萬(wàn)次加法,耗時(shí)大概1~2秒】<br />WITH?RANGE(1,2000)?AS?l,stock?UNWIND?l?AS?a?UNWIND?l?AS?b?WITH?SUM(a+b)?AS?delay,stock<br />WITH?<br />????'{"api_name":"index_member","token":"xxxxxxxxxxx","params":{"ts_code":"'+stock[0]+'"},"fields":"index_code,index_name,con_code,con_name,in_date,out_date,is_new"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.items?AS?items<br />//構建申萬(wàn)行業(yè)成分股圖譜<br />UNWIND?items?AS?item<br />WITH?item<br />MERGE?(stk:股票?{code:item[2]})?SET?stk+={name:item[3]+'('+item[2]+')'}<br />MERGE?(hy:申萬(wàn)行業(yè)?{code:item[0]})?SET?hy+={name:item[1]+'('+item[0]+')'}<br />CREATE?(stk)-[r:屬于]->(hy)?SET?r+={in_date:item[4],out_date:item[5],is_new:item[6]}
  
  申萬(wàn)行業(yè)成分股時(shí)序圖譜引用鏈接
  [1]TOC:圖數據庫無(wú)縫集成Tushare接口
  [2]Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據:
  [3]Tushare:
  

【干貨】一篇文章講透數據挖掘

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 134 次瀏覽 ? 2022-06-10 03:56 ? 來(lái)自相關(guān)話(huà)題

  【干貨】一篇文章講透數據挖掘
  (1)數據挖掘流程
  數據挖掘一般是指從大量的數據中通過(guò)算法搜索隱藏于其中信息的過(guò)程。它通常與計算機科學(xué)有關(guān),并通過(guò)統計、在線(xiàn)分析處理、情報檢索、機器學(xué)習、專(zhuān)家系統(依靠過(guò)去的經(jīng)驗法則)和模式識別等諸多方法來(lái)實(shí)現上述目標。它的分析方法包括:分類(lèi)、估計、預測、相關(guān)性分組或關(guān)聯(lián)規則、聚類(lèi)和復雜數據類(lèi)型挖掘。
  
  1)數據的采集
  首先得有數據,數據的收集有兩個(gè)方式,第一個(gè)方式是拿,專(zhuān)業(yè)點(diǎn)的說(shuō)法叫抓取或者爬取,例如搜索引擎就是這么做的,它把網(wǎng)上的所有的信息都下載到它的數據中心,然后你一搜才能搜出來(lái)。
  2)數據的傳輸
  一般會(huì )通過(guò)隊列方式進(jìn)行,因為數據量實(shí)在是太大了,數據必須經(jīng)過(guò)處理才會(huì )有用,可是系統處理不過(guò)來(lái),只好排好隊,慢慢的處理。
  3)數據的存儲
  現在數據就是金錢(qián),掌握了數據就相當于掌握了錢(qián)。要不然網(wǎng)站怎么知道你想買(mǎi)什么呢?就是因為它有你歷史的交易的數據,這個(gè)信息可不能給別人,十分寶貴,所以需要存儲下來(lái)。
  4)數據的清洗和分析
  上面存儲的數據是原始數據,原始數據多是雜亂無(wú)章的,有很多垃圾數據在里面,因而需要清洗和過(guò)濾,得到一些高質(zhì)量的數據。對于高質(zhì)量的數據,就可以進(jìn)行分析,從而對數據進(jìn)行分類(lèi),或者發(fā)現數據之間的相互關(guān)系,得到知識。
  注:第三與第四個(gè)步驟,現存后清洗和先清洗再存,在真是的業(yè)務(wù)場(chǎng)景中可以適當互換。
  5)數據的檢索和挖掘
  檢索就是搜索,所謂外事問(wèn)google,內事問(wèn)百度。挖掘,僅僅搜索出來(lái)已經(jīng)不能滿(mǎn)足人們的要求了,還需要從信息中挖掘出相互的關(guān)系。
  6)數據的加載與應用
  怎么友好的展示與傳遞給用戶(hù)為數據挖掘工作做好閉環(huán)。
  (2)數據挖掘工具類(lèi)1)數據采集工具
  1、針對日志文件類(lèi)
  工具
  定義
  Logstash
  Logstash是一個(gè)開(kāi)源數據收集引擎,具有實(shí)時(shí)管道功能。Logstash可以動(dòng)態(tài)地將來(lái)自不同數據源的數據統一起來(lái),并將數據標準化到所選擇的目的地。
  Filebeat
  Filebeat 作為一個(gè)輕量級的日志傳輸工具可以將日志推送到中心 Logstash。
  Fluentd
  Fluentd 創(chuàng )建的初衷主要是盡可能的使用 JSON 作為日志輸出,所以傳輸工具及其下游的傳輸線(xiàn)不需要猜測子字符串里面各個(gè)字段的類(lèi)型。這樣,它為幾乎所有的語(yǔ)言都提供庫,即可以將它插入到自定義的程序中。
  Logagent
  Logagent 是 Sematext 提供的傳輸工具,它用來(lái)將日志傳輸到 Logsene(一個(gè)基于 SaaS 平臺的 Elasticsearch API)。
  Rsylog
  絕大多數 Linux 發(fā)布版本默認的守護進(jìn)程,rsyslog 讀取并寫(xiě)入 /var/log/messages 。它可以提取文件、解析、緩沖(磁盤(pán)和內存)以及將它們傳輸到多個(gè)目的地,包括 Elasticsearch ??梢詮拇颂幷业饺绾翁幚?Apache 以及系統日志。
  Logtail
  阿里云日志服務(wù)的生產(chǎn)者,目前在阿里集團內部機器上運行,經(jīng)過(guò)3年多時(shí)間的考驗,目前為阿里公有云用戶(hù)提供日志收集服務(wù)。
  2、針對爬蟲(chóng)類(lèi)
  頁(yè)面下載 --> 頁(yè)面解析 --> 數據存儲
 ?。?)頁(yè)面下載器
  對于下載器而言,python的庫requests能滿(mǎn)足大部分測試+抓取需求,進(jìn)階工程化scrapy,動(dòng)態(tài)網(wǎng)頁(yè)優(yōu)先找API接口,如果有簡(jiǎn)單加密就破解,實(shí)在困難就使用splash渲染。
 ?。?)頁(yè)面解析器
 ?、貰eautifulSoup(入門(mén)級):Python爬蟲(chóng)入門(mén)BeautifulSoup模塊
 ?、趐yquery(類(lèi)似jQuery):Python爬蟲(chóng):pyquery模塊解析網(wǎng)頁(yè)
 ?、踠xml:Python爬蟲(chóng):使用lxml解析網(wǎng)頁(yè)內容
 ?、躳arsel:Extract text using CSS or XPath selectors
 ?、輘crapy的Selector (強烈推薦, 比較高級的封裝,基于parsel)
 ?、捱x擇器(Selectors):python爬蟲(chóng):scrapy框架xpath和css選擇器語(yǔ)法
  ---------------------
  總結:
  解析器直接使用scrapy的Selector 就行,簡(jiǎn)單、直接、高效。
 ?。?)數據存儲
 ?、賢xt文本:Python全棧之路:文件file常用操作
 ?、赾sv文件:python讀取寫(xiě)入csv文件
 ?、踫qlite3 (python自帶):Python編程:使用數據庫sqlite3
 ?、躆ySQL:SQL:pymysql模塊讀寫(xiě)mysql數據
 ?、軲ongoDB:Python編程:mongodb的基本增刪改查操作
  ---------------------
  總結:
  數據存儲沒(méi)有什么可深究的,按照業(yè)務(wù)需求來(lái)就行,一般快速測試使用MongoDB,業(yè)務(wù)使用MySQL
 ?。?)其他工具
 ?、賓xecjs :執行js
  Python爬蟲(chóng):execjs在python中運行javascript代碼
 ?、趐yv8: 執行js
  mac安裝pyv8模塊-JavaScript翻譯成python
 ?、踙tml5lib
  Python爬蟲(chóng):scrapy利用html5lib解析不規范的html文本
  2)數據清洗工具
  1、DataWrangler
  基于網(wǎng)絡(luò )的服務(wù)是斯坦福大學(xué)的可視化組設計來(lái)清洗和重排數據的.文本編輯非常簡(jiǎn)單。例如,當我選擇大標題為“Reported crime in Alabama”的樣本數據的某行的“Alabama”,然后選擇另一組數據的“Alaska”,它會(huì )建議提取每州的名字。把鼠標停留在建議上,就可以看到用紅色突出顯示的行。
  2、Google Refine
  它可以導入導出多種格式的數據,如標簽或逗號分隔的文本文件、Excel、XML和JSON文件。Refine設有內置算法,可以發(fā)現一些拼寫(xiě)不一樣但實(shí)際上應分為一組的文本。導入你的數據后,選擇編輯單元格->聚類(lèi),編輯,然后選擇要用的算法。數據選項,提供快速簡(jiǎn)單的數據分布概貌。這個(gè)功能可以揭示那些可能由于輸入錯誤導致的異?!?,工資記錄不是80,000美元而竟然是800,000美元;或指出不一致的地方——例如薪酬數據記錄之間的差異,有的是計時(shí)工資,有的是每周支付,有的是年薪。除了數據管家功能,Google Refine還提供了一些有用的分析工具,例如排序和篩選。
  3、Logstash
  Logstash 是一款強大的數據處理工具,它可以實(shí)現數據傳輸,格式處理,格式化輸出,還有強大的插件功能,常用于日志處理。
  3)數據存儲工具
  數據存儲主要分為結構化數據的存儲和非結構化數據的存儲。
  1、結構化數據
 ?。?)定義
  一般指存儲在數據庫中,具有一定邏輯結構和物理結構的數據,最為常見(jiàn)的是存儲在關(guān)系數據庫中的數據;非結構化數據:一般指結構化數據以外的數據,這些數據不存儲在數據庫中,而是以各種類(lèi)型的文本形式存放,其中Web上的一些數據(內嵌于HTML或XML標記中)又具有一定的邏輯結構和物理結構,被稱(chēng)為半結構數據。
 ?。?)存儲系統
  目前比較成熟的結構化存儲系統有Oracle、MySQL、Hadoop等。
  2、非結構化數據
 ?。?)定義
  非結構化數據是數據結構不規則或不完整,沒(méi)有預定義的數據模型,不方便用數據庫二維邏輯表來(lái)表現的數據。包括所有格式的辦公文檔、文本、圖片、XML, HTML、各類(lèi)報表、圖像和音頻/視頻信息等等。
 ?。?)存儲方式
  1)使用文件系統存儲文件,而在數據庫中存儲訪(fǎng)問(wèn)路徑。這種方式的優(yōu)點(diǎn)是實(shí)現簡(jiǎn)單,不需要DBMS的高級功能,但是這種方式無(wú)法實(shí)現文件的事務(wù)性訪(fǎng)問(wèn),不便于數據備份和恢復,不便于數據遷移等;
  2)使用阿里云OSS的文件存儲功能。
  4)數據計算工具
  數據計算分為實(shí)時(shí)計算、在線(xiàn)計算、離線(xiàn)計算。
  1、數據實(shí)時(shí)計算
  ApacheStorm
  2、數據在線(xiàn)計算
  Elasticsearch
  MySQL
  3、數據離線(xiàn)計算
  HaDoop Hive
  5)數據分析工具
  1、對數據矩陣科學(xué)計算:Python的numpy庫
  2、對數據切片等常規處理:強大的pandas庫
  3、對數據建模處理:sklearn庫
  6)數據加載工具
  1、數據的可視化處理:Python中的matplotlib和seaborn庫
  2、常用的BI可視化工具:Tableu和帆軟
  3、ECharts
  ——————————————
  閱讀推薦 查看全部

  【干貨】一篇文章講透數據挖掘
  (1)數據挖掘流程
  數據挖掘一般是指從大量的數據中通過(guò)算法搜索隱藏于其中信息的過(guò)程。它通常與計算機科學(xué)有關(guān),并通過(guò)統計、在線(xiàn)分析處理、情報檢索、機器學(xué)習、專(zhuān)家系統(依靠過(guò)去的經(jīng)驗法則)和模式識別等諸多方法來(lái)實(shí)現上述目標。它的分析方法包括:分類(lèi)、估計、預測、相關(guān)性分組或關(guān)聯(lián)規則、聚類(lèi)和復雜數據類(lèi)型挖掘。
  
  1)數據的采集
  首先得有數據,數據的收集有兩個(gè)方式,第一個(gè)方式是拿,專(zhuān)業(yè)點(diǎn)的說(shuō)法叫抓取或者爬取,例如搜索引擎就是這么做的,它把網(wǎng)上的所有的信息都下載到它的數據中心,然后你一搜才能搜出來(lái)。
  2)數據的傳輸
  一般會(huì )通過(guò)隊列方式進(jìn)行,因為數據量實(shí)在是太大了,數據必須經(jīng)過(guò)處理才會(huì )有用,可是系統處理不過(guò)來(lái),只好排好隊,慢慢的處理。
  3)數據的存儲
  現在數據就是金錢(qián),掌握了數據就相當于掌握了錢(qián)。要不然網(wǎng)站怎么知道你想買(mǎi)什么呢?就是因為它有你歷史的交易的數據,這個(gè)信息可不能給別人,十分寶貴,所以需要存儲下來(lái)。
  4)數據的清洗和分析
  上面存儲的數據是原始數據,原始數據多是雜亂無(wú)章的,有很多垃圾數據在里面,因而需要清洗和過(guò)濾,得到一些高質(zhì)量的數據。對于高質(zhì)量的數據,就可以進(jìn)行分析,從而對數據進(jìn)行分類(lèi),或者發(fā)現數據之間的相互關(guān)系,得到知識。
  注:第三與第四個(gè)步驟,現存后清洗和先清洗再存,在真是的業(yè)務(wù)場(chǎng)景中可以適當互換。
  5)數據的檢索和挖掘
  檢索就是搜索,所謂外事問(wèn)google,內事問(wèn)百度。挖掘,僅僅搜索出來(lái)已經(jīng)不能滿(mǎn)足人們的要求了,還需要從信息中挖掘出相互的關(guān)系。
  6)數據的加載與應用
  怎么友好的展示與傳遞給用戶(hù)為數據挖掘工作做好閉環(huán)。
  (2)數據挖掘工具類(lèi)1)數據采集工具
  1、針對日志文件類(lèi)
  工具
  定義
  Logstash
  Logstash是一個(gè)開(kāi)源數據收集引擎,具有實(shí)時(shí)管道功能。Logstash可以動(dòng)態(tài)地將來(lái)自不同數據源的數據統一起來(lái),并將數據標準化到所選擇的目的地。
  Filebeat
  Filebeat 作為一個(gè)輕量級的日志傳輸工具可以將日志推送到中心 Logstash。
  Fluentd
  Fluentd 創(chuàng )建的初衷主要是盡可能的使用 JSON 作為日志輸出,所以傳輸工具及其下游的傳輸線(xiàn)不需要猜測子字符串里面各個(gè)字段的類(lèi)型。這樣,它為幾乎所有的語(yǔ)言都提供庫,即可以將它插入到自定義的程序中。
  Logagent
  Logagent 是 Sematext 提供的傳輸工具,它用來(lái)將日志傳輸到 Logsene(一個(gè)基于 SaaS 平臺的 Elasticsearch API)。
  Rsylog
  絕大多數 Linux 發(fā)布版本默認的守護進(jìn)程,rsyslog 讀取并寫(xiě)入 /var/log/messages 。它可以提取文件、解析、緩沖(磁盤(pán)和內存)以及將它們傳輸到多個(gè)目的地,包括 Elasticsearch ??梢詮拇颂幷业饺绾翁幚?Apache 以及系統日志。
  Logtail
  阿里云日志服務(wù)的生產(chǎn)者,目前在阿里集團內部機器上運行,經(jīng)過(guò)3年多時(shí)間的考驗,目前為阿里公有云用戶(hù)提供日志收集服務(wù)。
  2、針對爬蟲(chóng)類(lèi)
  頁(yè)面下載 --> 頁(yè)面解析 --> 數據存儲
 ?。?)頁(yè)面下載器
  對于下載器而言,python的庫requests能滿(mǎn)足大部分測試+抓取需求,進(jìn)階工程化scrapy,動(dòng)態(tài)網(wǎng)頁(yè)優(yōu)先找API接口,如果有簡(jiǎn)單加密就破解,實(shí)在困難就使用splash渲染。
 ?。?)頁(yè)面解析器
 ?、貰eautifulSoup(入門(mén)級):Python爬蟲(chóng)入門(mén)BeautifulSoup模塊
 ?、趐yquery(類(lèi)似jQuery):Python爬蟲(chóng):pyquery模塊解析網(wǎng)頁(yè)
 ?、踠xml:Python爬蟲(chóng):使用lxml解析網(wǎng)頁(yè)內容
 ?、躳arsel:Extract text using CSS or XPath selectors
 ?、輘crapy的Selector (強烈推薦, 比較高級的封裝,基于parsel)
 ?、捱x擇器(Selectors):python爬蟲(chóng):scrapy框架xpath和css選擇器語(yǔ)法
  ---------------------
  總結:
  解析器直接使用scrapy的Selector 就行,簡(jiǎn)單、直接、高效。
 ?。?)數據存儲
 ?、賢xt文本:Python全棧之路:文件file常用操作
 ?、赾sv文件:python讀取寫(xiě)入csv文件
 ?、踫qlite3 (python自帶):Python編程:使用數據庫sqlite3
 ?、躆ySQL:SQL:pymysql模塊讀寫(xiě)mysql數據
 ?、軲ongoDB:Python編程:mongodb的基本增刪改查操作
  ---------------------
  總結:
  數據存儲沒(méi)有什么可深究的,按照業(yè)務(wù)需求來(lái)就行,一般快速測試使用MongoDB,業(yè)務(wù)使用MySQL
 ?。?)其他工具
 ?、賓xecjs :執行js
  Python爬蟲(chóng):execjs在python中運行javascript代碼
 ?、趐yv8: 執行js
  mac安裝pyv8模塊-JavaScript翻譯成python
 ?、踙tml5lib
  Python爬蟲(chóng):scrapy利用html5lib解析不規范的html文本
  2)數據清洗工具
  1、DataWrangler
  基于網(wǎng)絡(luò )的服務(wù)是斯坦福大學(xué)的可視化組設計來(lái)清洗和重排數據的.文本編輯非常簡(jiǎn)單。例如,當我選擇大標題為“Reported crime in Alabama”的樣本數據的某行的“Alabama”,然后選擇另一組數據的“Alaska”,它會(huì )建議提取每州的名字。把鼠標停留在建議上,就可以看到用紅色突出顯示的行。
  2、Google Refine
  它可以導入導出多種格式的數據,如標簽或逗號分隔的文本文件、Excel、XML和JSON文件。Refine設有內置算法,可以發(fā)現一些拼寫(xiě)不一樣但實(shí)際上應分為一組的文本。導入你的數據后,選擇編輯單元格->聚類(lèi),編輯,然后選擇要用的算法。數據選項,提供快速簡(jiǎn)單的數據分布概貌。這個(gè)功能可以揭示那些可能由于輸入錯誤導致的異?!?,工資記錄不是80,000美元而竟然是800,000美元;或指出不一致的地方——例如薪酬數據記錄之間的差異,有的是計時(shí)工資,有的是每周支付,有的是年薪。除了數據管家功能,Google Refine還提供了一些有用的分析工具,例如排序和篩選。
  3、Logstash
  Logstash 是一款強大的數據處理工具,它可以實(shí)現數據傳輸,格式處理,格式化輸出,還有強大的插件功能,常用于日志處理。
  3)數據存儲工具
  數據存儲主要分為結構化數據的存儲和非結構化數據的存儲。
  1、結構化數據
 ?。?)定義
  一般指存儲在數據庫中,具有一定邏輯結構和物理結構的數據,最為常見(jiàn)的是存儲在關(guān)系數據庫中的數據;非結構化數據:一般指結構化數據以外的數據,這些數據不存儲在數據庫中,而是以各種類(lèi)型的文本形式存放,其中Web上的一些數據(內嵌于HTML或XML標記中)又具有一定的邏輯結構和物理結構,被稱(chēng)為半結構數據。
 ?。?)存儲系統
  目前比較成熟的結構化存儲系統有Oracle、MySQL、Hadoop等。
  2、非結構化數據
 ?。?)定義
  非結構化數據是數據結構不規則或不完整,沒(méi)有預定義的數據模型,不方便用數據庫二維邏輯表來(lái)表現的數據。包括所有格式的辦公文檔、文本、圖片、XML, HTML、各類(lèi)報表、圖像和音頻/視頻信息等等。
 ?。?)存儲方式
  1)使用文件系統存儲文件,而在數據庫中存儲訪(fǎng)問(wèn)路徑。這種方式的優(yōu)點(diǎn)是實(shí)現簡(jiǎn)單,不需要DBMS的高級功能,但是這種方式無(wú)法實(shí)現文件的事務(wù)性訪(fǎng)問(wèn),不便于數據備份和恢復,不便于數據遷移等;
  2)使用阿里云OSS的文件存儲功能。
  4)數據計算工具
  數據計算分為實(shí)時(shí)計算、在線(xiàn)計算、離線(xiàn)計算。
  1、數據實(shí)時(shí)計算
  ApacheStorm
  2、數據在線(xiàn)計算
  Elasticsearch
  MySQL
  3、數據離線(xiàn)計算
  HaDoop Hive
  5)數據分析工具
  1、對數據矩陣科學(xué)計算:Python的numpy庫
  2、對數據切片等常規處理:強大的pandas庫
  3、對數據建模處理:sklearn庫
  6)數據加載工具
  1、數據的可視化處理:Python中的matplotlib和seaborn庫
  2、常用的BI可視化工具:Tableu和帆軟
  3、ECharts
  ——————————————
  閱讀推薦

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 239 次瀏覽 ? 2022-06-07 07:15 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 148 次瀏覽 ? 2022-06-06 08:26 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

深入淺出云原生環(huán)境信息收集技術(shù)(一)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 62 次瀏覽 ? 2022-06-05 10:27 ? 來(lái)自相關(guān)話(huà)題

  深入淺出云原生環(huán)境信息收集技術(shù)(一)
  前言
  信息收集在攻擊和防御兩端都是非常重要的一環(huán)。從宏觀(guān)的角度來(lái)說(shuō),大多數信息相關(guān)的工作都可以看作信息收集和信息處理交替進(jìn)行的循環(huán)。優(yōu)質(zhì)的信息收集成果是后續工作順利展開(kāi)的首要條件?!秾O子兵法》有云:故善戰人之勢,如轉圓石于千仞之山者,勢也。在掌握了充足信息后,攻防工作將“如轉圓石于千仞之山”。
  然而,信息的瑣碎性和云原生本身的復雜組成為云原生環(huán)境下的信息收集工作帶來(lái)了一定挑戰。有些朋友也許會(huì )說(shuō),這有何難?比如,執行uname -a命令,就能收集到內核信息了。沒(méi)錯,信息收集確實(shí)是一步步進(jìn)行、一項項完成的。但是,如果只是想當然地進(jìn)行,收集到的信息難免陷于凌亂瑣碎,也很可能不全面。
  對此,筆者結合在攻、防兩端積累的經(jīng)驗,希望與大家探討四個(gè)問(wèn)題:
  1. 站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  2. 站在攻擊者視角,云原生環(huán)境下的信息分類(lèi)維度有哪些?
  3. 站在攻擊者視角,收集到的云原生環(huán)境信息有什么價(jià)值?
  4. 站在攻擊者視角,有沒(méi)有可能阻礙或影響防守者收集信息?
  就“信息收集”這個(gè)話(huà)題而言,毫無(wú)疑問(wèn),防守者是占盡天時(shí)地利的,無(wú)論是能夠收集到的信息種類(lèi)、規模,還是信息收集開(kāi)始的時(shí)間、收集信息所需的權限,都遠遠在攻擊者之上。防守者更需要關(guān)注的是如何使用、分析收集到的信息。因此,我們從攻擊者的角度出發(fā)進(jìn)行探討。這并不意味著(zhù)防守的同學(xué)不需要關(guān)注。相反,只有對攻擊者的技術(shù)了然于胸,才能更好地識別攻擊行為、判定攻擊意圖。
  作為本系列的第一篇文章,本文將展開(kāi)討論第一個(gè)問(wèn)題:站在攻擊者視角,云原生環(huán)境下的收集信息方式有哪些?
  注:文中案例相關(guān)操作均在實(shí)驗環(huán)境中進(jìn)行,相關(guān)技術(shù)僅供研究交流,請勿應用于未授權的滲透測試。
  站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  思路重在“有章可循”。先有一個(gè)點(diǎn),再進(jìn)行發(fā)散。信息收集方式通常與攻擊場(chǎng)景緊密相關(guān)。在云原生環(huán)境下,攻擊場(chǎng)景通常有三種:
  1.攻擊從遠程發(fā)起。遠程發(fā)起的攻擊十分常見(jiàn),例如,通過(guò)存在未授權訪(fǎng)問(wèn)漏洞的KubernetesAPI Server或DockerDaemon執行命令。
  2.攻擊從容器內發(fā)起。容器內發(fā)起的攻擊通常屬于一次滲透測試的后滲透階段——它的前提是獲得了容器內某種權限的Shell,或者是Containeras a Service(后文簡(jiǎn)稱(chēng)CaaS)的場(chǎng)景——攻擊者本身就是容器服務(wù)的“客戶(hù)”。
  3.依托于鏡像的軟件供應鏈攻擊。包括“鏡像漏洞利用”和“鏡像投毒”等,《云原生安全:攻防實(shí)踐與體系構建》第三章對此進(jìn)行了詳細介紹。
  相應地,信息收集方式主要也有這三種,與攻擊場(chǎng)景相伴而生。讓我們來(lái)一起看一下。
  1通過(guò)遠程交互收集信息
  綜合來(lái)看,云原生環(huán)境中開(kāi)放的遠程服務(wù)主要有兩類(lèi):云原生控制面服務(wù)和容器化業(yè)務(wù)服務(wù)。遠程交互,顧名思義,網(wǎng)絡(luò )可達就行,別無(wú)限制。收集到的信息數量和價(jià)值主要取決于目標的訪(fǎng)問(wèn)控制機制。
 ?。?)從云原生控制面服務(wù)收集信息
  如前所述,如果遇到存在未授權訪(fǎng)問(wèn)漏洞的Kubernetes API Server,不費吹灰之力即可控制整個(gè)云原生集群;如果目標設置了合理的訪(fǎng)問(wèn)控制機制,則獲取到的有價(jià)值信息將大大減少,但也并非毫無(wú)所得。例如,許多Kubernetes API Server允許匿名用戶(hù)訪(fǎng)問(wèn)部分API endpoints。在下面的示例中,攻擊者通過(guò)訪(fǎng)問(wèn)/version,獲得了目標Kubernetes的版本號:
  rambo@t-matrix:~$ curl -k https://1.1.1.1:6443/version{ "major": "1", "minor": "16", "gitVersion": "v1.16.2", "gitCommit": "c97fe5036ef3df2967d086711e6c0c405941e14b", "gitTreeState": "clean", "buildDate": "2019-10-15T19:09:08Z", "goVersion": "go1.12.10", "compiler": "gc", "platform": "linux/amd64"}
  通過(guò)版本匹配,攻擊者能夠判斷目標Kubernetes可能存在哪些漏洞,從而進(jìn)一步利用,這便是版本信息的價(jià)值。
  即使目標設置了非常嚴格的訪(fǎng)問(wèn)控制,攻擊者通常也能夠獲得一些信息。例如,即使訪(fǎng)問(wèn)/version失敗,根據失敗信息我們能夠得知目標是一個(gè)Kubernetes集群,從而利用Kubernetes的背景知識,去探測其他Kubernetes控制面服務(wù)端口(如kubelet、etcd等)。
 ?。?)從容器化業(yè)務(wù)服務(wù)收集信息
  大多數情況下,就信息收集而言,容器化與非容器化業(yè)務(wù)服務(wù)沒(méi)有顯著(zhù)不同之處,收集到的信息均與業(yè)務(wù)服務(wù)(如Web服務(wù)、數據庫服務(wù)等)本身強相關(guān)。關(guān)于這些信息的收集方法,安全社區已經(jīng)有很多總結,本文不再展開(kāi)講述。
  然而,許多業(yè)務(wù)在云原生化的過(guò)程中,其自身架構或部署形態(tài)也會(huì )發(fā)生變化,引入微服務(wù)治理(如服務(wù)網(wǎng)格)、API治理(如API網(wǎng)關(guān))的特征。這些特征有時(shí)是有價(jià)值的信息,提供了承載業(yè)務(wù)的云原生環(huán)境的一些線(xiàn)索,同樣值得收集。例如,如果我們發(fā)現與服務(wù)交互的HTTP返回頭中包含了x-envoy-開(kāi)頭的項,可以推測該服務(wù)處于一個(gè)由Istio/Envoy進(jìn)行服務(wù)網(wǎng)格管理的云原生環(huán)境中。其中,x-envoy-peer-metadata-id更是包含了服務(wù)所在的Pod、Pod的內部IP和Kubernetes命名空間等重要信息[1]:
  rambo@t-matrix:~$ curl -k https://1.1.1.1 -vv 2>&1 | grepx-envoy-peer-metadata-id< x-envoy-peer-metadata-id:sidecar~2.2.2.2~PodName.Namespace~Namespace.svc.cluster.local
  事實(shí)上,網(wǎng)絡(luò )空間測繪的關(guān)鍵步驟之一就是通過(guò)遠程交互收集信息。我們通過(guò)測繪發(fā)現,Istio、Envoy和Kong等云原生治理程序都會(huì )給被治理的業(yè)務(wù)服務(wù)添加一個(gè)或多個(gè)特征,這些特征對于探索業(yè)務(wù)網(wǎng)絡(luò )拓撲具有積極意義。
  2容器內收集信息
  多見(jiàn)于針對云原生環(huán)境滲透測試的后滲透階段,例如,攻擊者事先通過(guò)Web服務(wù)文件上傳漏洞上傳了一個(gè)Webshell。除此之外,云服務(wù)商提供的CaaS也允許攻擊者作為用戶(hù)直接創(chuàng )建并訪(fǎng)問(wèn)容器。
 ?。?)容器內通過(guò)本地操作收集信息
  雖然起點(diǎn)不同,但這兩個(gè)場(chǎng)景中攻擊者的目的是類(lèi)似的:突破容器到宿主機或其他容器。不過(guò),兩個(gè)場(chǎng)景下攻擊者擁有的初始權限可能不同。隨著(zhù)人們安全意識的增強,許多容器化業(yè)務(wù)已經(jīng)采用Rootless Container部署,業(yè)務(wù)進(jìn)程本身以低權限用戶(hù)(如www-data)運行,這通常也是攻擊者獲得的Webshell的權限;然而,作為CaaS的用戶(hù),攻擊者通??梢該碛腥萜鲀萺oot權限。與前文介紹的訪(fǎng)問(wèn)控制機制類(lèi)似,容器內權限大小對于容器內信息收集也有影響。但是,本文并不單獨討論權限問(wèn)題給信息收集工作帶來(lái)的影響,而是倡導一種“因地制宜”的隨機應變能力。
  “在容器內收集信息”或許是不少朋友看到本文標題后想到的第一個(gè)場(chǎng)景。沒(méi)錯,從容器內部能夠收集到當前環(huán)境的大量信息?!度萜魈右菁夹g(shù)概覽》[2]中曾介紹過(guò)的通過(guò)判定/.dockerenv文件是否存在來(lái)推斷是否處于Docker創(chuàng )建的容器環(huán)境等手法,就是典型的容器內信息收集。
 ?。?)容器內通過(guò)網(wǎng)絡(luò )交互收集信息
  與前文介紹的遠程交互方式相比,容器內的網(wǎng)絡(luò )交互對于攻擊者來(lái)說(shuō)具有獨特優(yōu)勢。因此,我們將這部分內容放在這里,強調“容器內”,而不是在前面一起介紹。這種優(yōu)勢主要有兩個(gè)方面:
  1.訪(fǎng)問(wèn)內部網(wǎng)絡(luò )。容器擁有云原生集群的內部IP,默認配置下還會(huì )有CAP_NET_RAW權限,攻擊者可以通過(guò)網(wǎng)絡(luò )掃描等方式摸清集群內部網(wǎng)絡(luò )拓撲,發(fā)現有價(jià)值的服務(wù),某些場(chǎng)景下甚至能夠訪(fǎng)問(wèn)到節點(diǎn)主機的元數據服務(wù)。這種網(wǎng)絡(luò )可達的優(yōu)勢是值得重視的,外部攻擊者通常需要借助SSRF等手段才能達到相同的目的。
  2.獲得一定權限。云原生集群的容器內可能會(huì )有某種形式的訪(fǎng)問(wèn)憑證,如Pod攜帶的ServiceAccount token等。利用此token可以向Kubernetes API Server發(fā)起訪(fǎng)問(wèn),縱使權限很小,至少不再是“匿名訪(fǎng)問(wèn)”,能夠訪(fǎng)問(wèn)/version獲得版本信息。
  3基于鏡像收集信息
  近年來(lái),軟件供應鏈安全事件頻發(fā),人們的重視程度也日漸提高。容器從鏡像創(chuàng )建而來(lái),就像進(jìn)程從程序創(chuàng )建而來(lái)一樣。因此,依托于鏡像,攻擊者能夠收集到許多有價(jià)值的信息,方式主要有兩種:
  1.利用鏡像和鏡像倉庫收集信息。有時(shí),攻擊者在容器中的權限是有限的,無(wú)法讀寫(xiě)關(guān)鍵文件及其元數據。如果能夠獲取到目標環(huán)境使用的鏡像甚至找到其公開(kāi)的鏡像倉庫,就能夠分析其鏡像組件的脆弱性,找到突破口。
  2.利用特殊鏡像收集運行時(shí)環(huán)境信息。由于runC等容器運行時(shí)的設計問(wèn)題,攻擊者能夠通過(guò)在目標環(huán)境部署特殊鏡像來(lái)獲取環(huán)境中的容器運行時(shí)二進(jìn)制程序文件,進(jìn)而獲得版本信息,發(fā)現潛在脆弱性?!度萜鬟\行時(shí)信息收集技術(shù)介紹》[3]一文對該技術(shù)進(jìn)行了詳細介紹。
  總結
  本文是“深入淺出云原生環(huán)境信息收集技術(shù)”系列的開(kāi)篇,幫助大家梳理了云原生環(huán)境下常見(jiàn)的信息收集方式。有了這些知識作為基礎,我們就能夠逐漸展開(kāi)討論如何在云原生環(huán)境下體系化地收集瑣碎復雜的信息。以攻促防,知攻知防。一起來(lái)守護云原生安全。
  后續文章更加精彩,敬請期待!
  參考文獻
  1. %20CIS%20-%20Attack%20in%20a%20Service%20Mesh%20-%20Public.pptx.pdf
  2.
  3.
  關(guān)于星云實(shí)驗室
  星云實(shí)驗室專(zhuān)注于云計算安全、解決方案研究與虛擬化網(wǎng)絡(luò )安全問(wèn)題研究?;贗aaS環(huán)境的安全防護,利用SDN/NFV等新技術(shù)和新理念,提出了軟件定義安全的云安全防護體系。承擔并完成多個(gè)國家、省、市以及行業(yè)重點(diǎn)單位創(chuàng )新研究課題,已成功孵化落地綠盟科技云安全解決方案。 查看全部

  深入淺出云原生環(huán)境信息收集技術(shù)(一)
  前言
  信息收集在攻擊和防御兩端都是非常重要的一環(huán)。從宏觀(guān)的角度來(lái)說(shuō),大多數信息相關(guān)的工作都可以看作信息收集和信息處理交替進(jìn)行的循環(huán)。優(yōu)質(zhì)的信息收集成果是后續工作順利展開(kāi)的首要條件?!秾O子兵法》有云:故善戰人之勢,如轉圓石于千仞之山者,勢也。在掌握了充足信息后,攻防工作將“如轉圓石于千仞之山”。
  然而,信息的瑣碎性和云原生本身的復雜組成為云原生環(huán)境下的信息收集工作帶來(lái)了一定挑戰。有些朋友也許會(huì )說(shuō),這有何難?比如,執行uname -a命令,就能收集到內核信息了。沒(méi)錯,信息收集確實(shí)是一步步進(jìn)行、一項項完成的。但是,如果只是想當然地進(jìn)行,收集到的信息難免陷于凌亂瑣碎,也很可能不全面。
  對此,筆者結合在攻、防兩端積累的經(jīng)驗,希望與大家探討四個(gè)問(wèn)題:
  1. 站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  2. 站在攻擊者視角,云原生環(huán)境下的信息分類(lèi)維度有哪些?
  3. 站在攻擊者視角,收集到的云原生環(huán)境信息有什么價(jià)值?
  4. 站在攻擊者視角,有沒(méi)有可能阻礙或影響防守者收集信息?
  就“信息收集”這個(gè)話(huà)題而言,毫無(wú)疑問(wèn),防守者是占盡天時(shí)地利的,無(wú)論是能夠收集到的信息種類(lèi)、規模,還是信息收集開(kāi)始的時(shí)間、收集信息所需的權限,都遠遠在攻擊者之上。防守者更需要關(guān)注的是如何使用、分析收集到的信息。因此,我們從攻擊者的角度出發(fā)進(jìn)行探討。這并不意味著(zhù)防守的同學(xué)不需要關(guān)注。相反,只有對攻擊者的技術(shù)了然于胸,才能更好地識別攻擊行為、判定攻擊意圖。
  作為本系列的第一篇文章,本文將展開(kāi)討論第一個(gè)問(wèn)題:站在攻擊者視角,云原生環(huán)境下的收集信息方式有哪些?
  注:文中案例相關(guān)操作均在實(shí)驗環(huán)境中進(jìn)行,相關(guān)技術(shù)僅供研究交流,請勿應用于未授權的滲透測試。
  站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  思路重在“有章可循”。先有一個(gè)點(diǎn),再進(jìn)行發(fā)散。信息收集方式通常與攻擊場(chǎng)景緊密相關(guān)。在云原生環(huán)境下,攻擊場(chǎng)景通常有三種:
  1.攻擊從遠程發(fā)起。遠程發(fā)起的攻擊十分常見(jiàn),例如,通過(guò)存在未授權訪(fǎng)問(wèn)漏洞的KubernetesAPI Server或DockerDaemon執行命令。
  2.攻擊從容器內發(fā)起。容器內發(fā)起的攻擊通常屬于一次滲透測試的后滲透階段——它的前提是獲得了容器內某種權限的Shell,或者是Containeras a Service(后文簡(jiǎn)稱(chēng)CaaS)的場(chǎng)景——攻擊者本身就是容器服務(wù)的“客戶(hù)”。
  3.依托于鏡像的軟件供應鏈攻擊。包括“鏡像漏洞利用”和“鏡像投毒”等,《云原生安全:攻防實(shí)踐與體系構建》第三章對此進(jìn)行了詳細介紹。
  相應地,信息收集方式主要也有這三種,與攻擊場(chǎng)景相伴而生。讓我們來(lái)一起看一下。
  1通過(guò)遠程交互收集信息
  綜合來(lái)看,云原生環(huán)境中開(kāi)放的遠程服務(wù)主要有兩類(lèi):云原生控制面服務(wù)和容器化業(yè)務(wù)服務(wù)。遠程交互,顧名思義,網(wǎng)絡(luò )可達就行,別無(wú)限制。收集到的信息數量和價(jià)值主要取決于目標的訪(fǎng)問(wèn)控制機制。
 ?。?)從云原生控制面服務(wù)收集信息
  如前所述,如果遇到存在未授權訪(fǎng)問(wèn)漏洞的Kubernetes API Server,不費吹灰之力即可控制整個(gè)云原生集群;如果目標設置了合理的訪(fǎng)問(wèn)控制機制,則獲取到的有價(jià)值信息將大大減少,但也并非毫無(wú)所得。例如,許多Kubernetes API Server允許匿名用戶(hù)訪(fǎng)問(wèn)部分API endpoints。在下面的示例中,攻擊者通過(guò)訪(fǎng)問(wèn)/version,獲得了目標Kubernetes的版本號:
  rambo@t-matrix:~$ curl -k https://1.1.1.1:6443/version{ "major": "1", "minor": "16", "gitVersion": "v1.16.2", "gitCommit": "c97fe5036ef3df2967d086711e6c0c405941e14b", "gitTreeState": "clean", "buildDate": "2019-10-15T19:09:08Z", "goVersion": "go1.12.10", "compiler": "gc", "platform": "linux/amd64"}
  通過(guò)版本匹配,攻擊者能夠判斷目標Kubernetes可能存在哪些漏洞,從而進(jìn)一步利用,這便是版本信息的價(jià)值。
  即使目標設置了非常嚴格的訪(fǎng)問(wèn)控制,攻擊者通常也能夠獲得一些信息。例如,即使訪(fǎng)問(wèn)/version失敗,根據失敗信息我們能夠得知目標是一個(gè)Kubernetes集群,從而利用Kubernetes的背景知識,去探測其他Kubernetes控制面服務(wù)端口(如kubelet、etcd等)。
 ?。?)從容器化業(yè)務(wù)服務(wù)收集信息
  大多數情況下,就信息收集而言,容器化與非容器化業(yè)務(wù)服務(wù)沒(méi)有顯著(zhù)不同之處,收集到的信息均與業(yè)務(wù)服務(wù)(如Web服務(wù)、數據庫服務(wù)等)本身強相關(guān)。關(guān)于這些信息的收集方法,安全社區已經(jīng)有很多總結,本文不再展開(kāi)講述。
  然而,許多業(yè)務(wù)在云原生化的過(guò)程中,其自身架構或部署形態(tài)也會(huì )發(fā)生變化,引入微服務(wù)治理(如服務(wù)網(wǎng)格)、API治理(如API網(wǎng)關(guān))的特征。這些特征有時(shí)是有價(jià)值的信息,提供了承載業(yè)務(wù)的云原生環(huán)境的一些線(xiàn)索,同樣值得收集。例如,如果我們發(fā)現與服務(wù)交互的HTTP返回頭中包含了x-envoy-開(kāi)頭的項,可以推測該服務(wù)處于一個(gè)由Istio/Envoy進(jìn)行服務(wù)網(wǎng)格管理的云原生環(huán)境中。其中,x-envoy-peer-metadata-id更是包含了服務(wù)所在的Pod、Pod的內部IP和Kubernetes命名空間等重要信息[1]:
  rambo@t-matrix:~$ curl -k https://1.1.1.1 -vv 2>&1 | grepx-envoy-peer-metadata-id< x-envoy-peer-metadata-id:sidecar~2.2.2.2~PodName.Namespace~Namespace.svc.cluster.local
  事實(shí)上,網(wǎng)絡(luò )空間測繪的關(guān)鍵步驟之一就是通過(guò)遠程交互收集信息。我們通過(guò)測繪發(fā)現,Istio、Envoy和Kong等云原生治理程序都會(huì )給被治理的業(yè)務(wù)服務(wù)添加一個(gè)或多個(gè)特征,這些特征對于探索業(yè)務(wù)網(wǎng)絡(luò )拓撲具有積極意義。
  2容器內收集信息
  多見(jiàn)于針對云原生環(huán)境滲透測試的后滲透階段,例如,攻擊者事先通過(guò)Web服務(wù)文件上傳漏洞上傳了一個(gè)Webshell。除此之外,云服務(wù)商提供的CaaS也允許攻擊者作為用戶(hù)直接創(chuàng )建并訪(fǎng)問(wèn)容器。
 ?。?)容器內通過(guò)本地操作收集信息
  雖然起點(diǎn)不同,但這兩個(gè)場(chǎng)景中攻擊者的目的是類(lèi)似的:突破容器到宿主機或其他容器。不過(guò),兩個(gè)場(chǎng)景下攻擊者擁有的初始權限可能不同。隨著(zhù)人們安全意識的增強,許多容器化業(yè)務(wù)已經(jīng)采用Rootless Container部署,業(yè)務(wù)進(jìn)程本身以低權限用戶(hù)(如www-data)運行,這通常也是攻擊者獲得的Webshell的權限;然而,作為CaaS的用戶(hù),攻擊者通??梢該碛腥萜鲀萺oot權限。與前文介紹的訪(fǎng)問(wèn)控制機制類(lèi)似,容器內權限大小對于容器內信息收集也有影響。但是,本文并不單獨討論權限問(wèn)題給信息收集工作帶來(lái)的影響,而是倡導一種“因地制宜”的隨機應變能力。
  “在容器內收集信息”或許是不少朋友看到本文標題后想到的第一個(gè)場(chǎng)景。沒(méi)錯,從容器內部能夠收集到當前環(huán)境的大量信息?!度萜魈右菁夹g(shù)概覽》[2]中曾介紹過(guò)的通過(guò)判定/.dockerenv文件是否存在來(lái)推斷是否處于Docker創(chuàng )建的容器環(huán)境等手法,就是典型的容器內信息收集。
 ?。?)容器內通過(guò)網(wǎng)絡(luò )交互收集信息
  與前文介紹的遠程交互方式相比,容器內的網(wǎng)絡(luò )交互對于攻擊者來(lái)說(shuō)具有獨特優(yōu)勢。因此,我們將這部分內容放在這里,強調“容器內”,而不是在前面一起介紹。這種優(yōu)勢主要有兩個(gè)方面:
  1.訪(fǎng)問(wèn)內部網(wǎng)絡(luò )。容器擁有云原生集群的內部IP,默認配置下還會(huì )有CAP_NET_RAW權限,攻擊者可以通過(guò)網(wǎng)絡(luò )掃描等方式摸清集群內部網(wǎng)絡(luò )拓撲,發(fā)現有價(jià)值的服務(wù),某些場(chǎng)景下甚至能夠訪(fǎng)問(wèn)到節點(diǎn)主機的元數據服務(wù)。這種網(wǎng)絡(luò )可達的優(yōu)勢是值得重視的,外部攻擊者通常需要借助SSRF等手段才能達到相同的目的。
  2.獲得一定權限。云原生集群的容器內可能會(huì )有某種形式的訪(fǎng)問(wèn)憑證,如Pod攜帶的ServiceAccount token等。利用此token可以向Kubernetes API Server發(fā)起訪(fǎng)問(wèn),縱使權限很小,至少不再是“匿名訪(fǎng)問(wèn)”,能夠訪(fǎng)問(wèn)/version獲得版本信息。
  3基于鏡像收集信息
  近年來(lái),軟件供應鏈安全事件頻發(fā),人們的重視程度也日漸提高。容器從鏡像創(chuàng )建而來(lái),就像進(jìn)程從程序創(chuàng )建而來(lái)一樣。因此,依托于鏡像,攻擊者能夠收集到許多有價(jià)值的信息,方式主要有兩種:
  1.利用鏡像和鏡像倉庫收集信息。有時(shí),攻擊者在容器中的權限是有限的,無(wú)法讀寫(xiě)關(guān)鍵文件及其元數據。如果能夠獲取到目標環(huán)境使用的鏡像甚至找到其公開(kāi)的鏡像倉庫,就能夠分析其鏡像組件的脆弱性,找到突破口。
  2.利用特殊鏡像收集運行時(shí)環(huán)境信息。由于runC等容器運行時(shí)的設計問(wèn)題,攻擊者能夠通過(guò)在目標環(huán)境部署特殊鏡像來(lái)獲取環(huán)境中的容器運行時(shí)二進(jìn)制程序文件,進(jìn)而獲得版本信息,發(fā)現潛在脆弱性?!度萜鬟\行時(shí)信息收集技術(shù)介紹》[3]一文對該技術(shù)進(jìn)行了詳細介紹。
  總結
  本文是“深入淺出云原生環(huán)境信息收集技術(shù)”系列的開(kāi)篇,幫助大家梳理了云原生環(huán)境下常見(jiàn)的信息收集方式。有了這些知識作為基礎,我們就能夠逐漸展開(kāi)討論如何在云原生環(huán)境下體系化地收集瑣碎復雜的信息。以攻促防,知攻知防。一起來(lái)守護云原生安全。
  后續文章更加精彩,敬請期待!
  參考文獻
  1. %20CIS%20-%20Attack%20in%20a%20Service%20Mesh%20-%20Public.pptx.pdf
  2.
  3.
  關(guān)于星云實(shí)驗室
  星云實(shí)驗室專(zhuān)注于云計算安全、解決方案研究與虛擬化網(wǎng)絡(luò )安全問(wèn)題研究?;贗aaS環(huán)境的安全防護,利用SDN/NFV等新技術(shù)和新理念,提出了軟件定義安全的云安全防護體系。承擔并完成多個(gè)國家、省、市以及行業(yè)重點(diǎn)單位創(chuàng )新研究課題,已成功孵化落地綠盟科技云安全解決方案。

聊聊Spring Boot服務(wù)監控,健康檢查,線(xiàn)程信息,JVM堆信息,指標收集

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 97 次瀏覽 ? 2022-06-02 11:23 ? 來(lái)自相關(guān)話(huà)題

  聊聊Spring Boot服務(wù)監控,健康檢查,線(xiàn)程信息,JVM堆信息,指標收集
  點(diǎn)擊上方“芋道源碼”,選擇“”
  管她前浪,還是后浪?
  能浪的浪,才是好浪!
  每天 10:33更新文章,每天掉億點(diǎn)點(diǎn)頭發(fā)...
  源碼精品專(zhuān)欄
  去年我們項目做了微服務(wù)1.0的架構轉型,但是服務(wù)監控這塊卻沒(méi)有跟上。這不,最近我就被分配了要將我們核心的微服務(wù)應用全部監控起來(lái)的任務(wù)。我們的微服務(wù)應用都是SpringBoot 應用,因此就自然而然的想到了借助Spring Boot 的Actuator 模塊。(沒(méi)吃過(guò)豬肉總聽(tīng)過(guò)豬叫見(jiàn)過(guò)豬跑吧)。
  本篇是我在完成這個(gè)工單之后,對Spring Boot Actuator模塊 學(xué)習應用的總結。在本篇文章中,你可以學(xué)習到:
  之后我還會(huì )介紹:
  推薦下自己做的 Spring Boot 的實(shí)戰項目:
  Spring Boot Actuator 模塊提供了生產(chǎn)級別的功能,比如健康檢查,審計,指標收集,HTTP 跟蹤等,幫助我們監控和管理Spring Boot 應用。這個(gè)模塊是一個(gè)采集應用內部信息暴露給外部的模塊,上述的功能都可以通過(guò)HTTP 和 JMX 訪(fǎng)問(wèn)。
  因為暴露內部信息的特性,Actuator 也可以和一些外部的應用監控系統整合(Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等)。這些監控系統提供了出色的儀表板,圖形,分析和警報,可幫助你通過(guò)一個(gè)統一友好的界面,監視和管理你的應用程序。
  Actuator使用Micrometer與這些外部應用程序監視系統集成。這樣一來(lái),只需很少的配置即可輕松集成外部的監控系統。
  Micrometer 為 Java 平臺上的性能數據收集提供了一個(gè)通用的 API,應用程序只需要使用 Micrometer 的通用 API 來(lái)收集性能指標即可。Micrometer 會(huì )負責完成與不同監控系統的適配工作。這就使得切換監控系統變得很容易。
  對比 Slf4j 之于 Java Logger 中的定位。
  推薦下自己做的 Spring Cloud 的實(shí)戰項目:
  我們先創(chuàng )建一個(gè)demo應用。
  spring?init?-d=web,actuator?-n=actuator-demo?actuator-demo<br /><br />
  image.png
  <br />????...<br />?<br />??org.springframework.boot<br />??spring-boot-starter-actuator<br />?<br />????...<br /><br /><br /><br />
  dependencies?{<br />?compile("org.springframework.boot:spring-boot-starter-actuator")<br />}<br /><br />
  Spring Boot 提供了所謂的 endpoints (下文翻譯為端點(diǎn))給外部來(lái)與應用程序進(jìn)行訪(fǎng)問(wèn)和交互。
  打比方來(lái)說(shuō),/health 端點(diǎn) 提供了關(guān)于應用健康情況的一些基礎信息。metrics 端點(diǎn)提供了一些有用的應用程序指標(JVM 內存使用、系統CPU使用等)。
  這些 Actuator 模塊本來(lái)就有的端點(diǎn)我們稱(chēng)之為原生端點(diǎn)。根據端點(diǎn)的作用的話(huà),我們大概可以分為三大類(lèi):
  詳細的原生端點(diǎn)介紹,請以官網(wǎng)為準,這里就不贅述徒增篇幅。
  需要注意的就是:
  我們可以通過(guò)以下配置,來(lái)配置通過(guò)JMX 和 HTTP 暴露的端點(diǎn)。
  PropertyDefault
  management.endpoints.jmx.exposure.exclude
  management.endpoints.jmx.exposure.include
  *
  management.endpoints.web.exposure.exclude
  management.endpoints.web.exposure.include
  info, healt
  可以打開(kāi)所有的監控點(diǎn)
  management.endpoints.web.exposure.include=*<br /><br />
  也可以選擇打開(kāi)部分,"*" 代表暴露所有的端點(diǎn),如果指定多個(gè)端點(diǎn),用","分開(kāi)
  management.endpoints.web.exposure.exclude=beans,trace<br /><br />
  Actuator 默認所有的監控點(diǎn)路徑都在/actuator/*,當然如果有需要這個(gè)路徑也支持定制。
  management.endpoints.web.base-path=/minitor<br /><br />
  設置完重啟后,再次訪(fǎng)問(wèn)地址就會(huì )變成/minitor/*。
  現在我們按照如下配置:
  #?"*"?代表暴露所有的端點(diǎn)?如果指定多個(gè)端點(diǎn),用","分開(kāi)<br />management.endpoints.web.exposure.include=*<br />#?賦值規則同上<br />management.endpoints.web.exposure.exclude=<br /><br />
  啟動(dòng)DEMO程序,訪(fǎng)問(wèn):8080/actuator,查看暴露出來(lái)的端點(diǎn):
  image.png
  上面這樣顯示是因為chrome 瀏覽器安裝了 JSON-handle 插件,實(shí)際上就是返回一大段json
  下面,我會(huì )著(zhù)重介紹幾個(gè)比較重要的端點(diǎn)。
  /health端點(diǎn)會(huì )聚合你程序的健康指標,來(lái)檢查程序的健康情況。端點(diǎn)公開(kāi)的應用健康信息取決于:
  management.endpoint.health.show-details=always<br /><br />
  該屬性可以使用以下值之一進(jìn)行配置:
  NameDescription
  never
  不展示詳細信息,up或者down的狀態(tài),默認配置
  when-authorized
  詳細信息將會(huì )展示給通過(guò)認證的用戶(hù)。授權的角色可以通過(guò)management.endpoint.health.roles配置
  always
  對所有用戶(hù)暴露詳細信息
  按照上述配置,配置成always之后,我們啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/health端口,可以看到這樣的信息:
  image.png
  是不是感覺(jué)好像健康信息有點(diǎn)少?先別急,那是因為我們創(chuàng )建的是一個(gè)最基礎的Demo項目,沒(méi)有依賴(lài)很多的組件。
  /health端點(diǎn)有很多自動(dòng)配置的健康指示器:如redis、rabbitmq、db等組件。當你的項目有依賴(lài)對應組件的時(shí)候,這些健康指示器就會(huì )被自動(dòng)裝配,繼而采集對應的信息。如上面的 diskSpace 節點(diǎn)信息就是DiskSpaceHealthIndicator 在起作用。
  
  image.png
  上述截圖取自官方文檔
  這是我另一個(gè)項目的/health端點(diǎn)信息。
  image.png
  當如上的組件有一個(gè)狀態(tài)異常,應用服務(wù)的整體狀態(tài)即為down。我們也可以通過(guò)配置禁用某個(gè)組件的健康監測。
  management.health.mongo.enabled:?false<br /><br />
  或者禁用所有自動(dòng)配置的健康指示器:
  management.health.defaults.enabled:?false<br /><br />
  當然你也可以自定義一個(gè)Health Indicator,只需要實(shí)現HealthIndicator 接口或者繼承AbstractHealthIndicator類(lèi)。
  /**<br />?*?@author?Richard_yyf<br />?*?@version?1.0?2020/1/16<br />?*/<br />@Component<br />public?class?CustomHealthIndicator?extends?AbstractHealthIndicator?{<br /><br />????@Override<br />????protected?void?doHealthCheck(Health.Builder?builder)?throws?Exception?{<br />????????//?使用?builder?來(lái)創(chuàng )建健康狀態(tài)信息<br />????????//?如果你throw?了一個(gè)?exception,那么status?就會(huì )被置為DOWN,異常信息會(huì )被記錄下來(lái)<br />????????builder.up()<br />????????????????.withDetail("app",?"這個(gè)項目很健康")<br />????????????????.withDetail("error",?"Nothing,?I'm?very?good");<br />????}<br />}<br /><br />
  最終效果:
  image.png
  /metrics端點(diǎn)用來(lái)返回當前應用的各類(lèi)重要度量指標,比如:內存信息、線(xiàn)程信息、垃圾回收信息、tomcat、數據庫連接池等。
  {<br />????"names":?[<br />????????"tomcat.threads.busy",<br />????????"jvm.threads.states",<br />????????"jdbc.connections.active",<br />????????"jvm.gc.memory.promoted",<br />????????"http.server.requests",<br />????????"hikaricp.connections.max",<br />????????"hikaricp.connections.min",<br />????????"jvm.memory.used",<br />????????"jvm.gc.max.data.size",<br />????????"jdbc.connections.max",<br />?????????....<br />????]<br />}<br /><br />
  不同于1.x,Actuator在這個(gè)界面看不到具體的指標信息,只是展示了一個(gè)指標列表。 為了獲取到某個(gè)指標的詳細信息,我們可以請求具體的指標信息,像這樣:
  http://localhost:8080/actuator/metrics/{MetricName}<br /><br />
  比如我訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max,返回信息如下:
  image.png
  你也可以用query param的方式查看單獨的一塊區域。比如你可以訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max?tag=id:Metaspace。結果就是:
  image.png
  /loggers 端點(diǎn)暴露了我們程序內部配置的所有logger的信息。我們訪(fǎng)問(wèn)/actuator/loggers可以看到,
  image.png
  你也可以通過(guò)下述方式訪(fǎng)問(wèn)單獨一個(gè)logger,
  http://localhost:8080/actuator/loggers/{name}<br /><br />
  比如我現在訪(fǎng)問(wèn) root logger,:8080/actuator/loggers/root
  {<br />????"configuredLevel":?"INFO",<br />????"effectiveLevel":?"INFO"<br />}<br /><br />
  /loggers端點(diǎn)我最想提的就是這個(gè)功能,能夠動(dòng)態(tài)修改你的日志等級。
  比如,我們可以通過(guò)下述方式來(lái)修改 root logger的日志等級。我們只需要發(fā)起一個(gè)URL 為:8080/actuator/loggers/root的POST請求,POST報文如下:
  {<br />???"configuredLevel":?"DEBUG"<br />}<br /><br />
  image.png
  仔細想想,這個(gè)功能是不是非常有用。如果在生產(chǎn)環(huán)境中,你想要你的應用輸出一些Debug信息以便于你診斷一些異常情況,你你只需要按照上述方式就可以修改,而不需要重啟應用。
  如果想重置成默認值,把value 改成 null
  /info端點(diǎn)可以用來(lái)展示你程序的信息。我理解過(guò)來(lái)就是一些程序的基礎信息。并且你可以按照自己的需求在配置文件application.properties中個(gè)性化配置(默認情況下,該端點(diǎn)只會(huì )返回一個(gè)空的json內容。):
  info.app.name=actuator-test-demo<br />info.app.encoding=UTF-8<br />info.app.java.source=1.8<br />info.app.java.target=1.8<br />#?在?maven?項目中你可以直接用下列方式引用?maven?properties的值<br />#?info.app.encoding=@project.build.sourceEncoding@<br />#?info.app.java.source=@java.version@<br />#?info.app.java.target=@java.version@<br /><br />
  啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/info:
  {<br />????"app":?{<br />????????"encoding":?"UTF-8",<br />????????"java":?{<br />????????????"source":?"1.8.0_131",<br />????????????"target":?"1.8.0_131"<br />????????},<br />????????"name":?"actuator-test-demo"<br />????}<br />}<br /><br />
  /beans端點(diǎn)會(huì )返回Spring 容器中所有bean的別名、類(lèi)型、是否單例、依賴(lài)等信息。
  訪(fǎng)問(wèn):8080/actuator/beans,返回如下:
  image.png
  訪(fǎng)問(wèn)::8080/actuator/heapdump會(huì )自動(dòng)生成一個(gè) Jvm 的堆文件 heapdump。我們可以使用 JDK 自帶的 Jvm 監控工具 VisualVM 打開(kāi)此文件查看內存快照。
  image.png
  這個(gè)端點(diǎn)我個(gè)人覺(jué)得特別有用,方便我們在日常定位問(wèn)題的時(shí)候查看線(xiàn)程的情況。主要展示了線(xiàn)程名、線(xiàn)程ID、線(xiàn)程的狀態(tài)、是否等待鎖資源、線(xiàn)程堆棧等信息。就是可能查看起來(lái)不太直觀(guān)。訪(fǎng)問(wèn):8080/actuator/threaddump返回如下:
  image.png
  這個(gè)端點(diǎn)屬于操作控制類(lèi)端點(diǎn),可以?xún)?yōu)雅關(guān)閉 Spring Boot 應用。要使用這個(gè)功能首先需要在配置文件中開(kāi)啟:
  management.endpoint.shutdown.enabled=true<br /><br />
  由于 shutdown 接口默認只支持 POST 請求 ,我們啟動(dòng)Demo項目,向:8080/actuator/shutdown發(fā)起POST請求。返回信息:
  {<br />????"message":?"Shutting?down,?bye..."<br />}<br /><br />
  然后應用程序被關(guān)閉。
  由于開(kāi)放關(guān)閉應用的操作本身是一件非常危險 的事,所以真正在線(xiàn)上使用的時(shí)候,我們需要對其加入一定的保護機制,比如:定制Actuator的端點(diǎn)路徑、整合Spring Security進(jìn)行安全校驗 等。(不是特別必要的話(huà),這個(gè)端點(diǎn)不用開(kāi))
  由于端點(diǎn)的信息和產(chǎn)生的交互都是非常敏感的,必須防止未經(jīng)授權的外部訪(fǎng)問(wèn)。如果您的應用程序中存在Spring Security 的依賴(lài),則默認情況下使用基于表單的HTTP身份驗證 來(lái)保護端點(diǎn)。
  如果沒(méi)有,只需要增加對應的依賴(lài)即可:
  <br />???org.springframework.boot<br />???spring-boot-starter-security<br /><br /><br />
  添加之后,我們需要定義安全校驗規則,來(lái)覆蓋Spring Security 的默認配置。
  這里我給出了兩個(gè)版本的模板配置:
  import?org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;<br />import?org.springframework.boot.actuate.context.ShutdownEndpoint;<br />import?org.springframework.boot.autoconfigure.security.servlet.PathRequest;<br />import?org.springframework.context.annotation.Configuration;<br />import?org.springframework.security.config.annotation.web.builders.HttpSecurity;<br />import?org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;<br /><br />/**<br />?*?@author?Richard_yyf<br />?*/<br />@Configuration<br />public?class?ActuatorSecurityConfig?extends?WebSecurityConfigurerAdapter?{<br /><br />????/*<br />?????*?version1:<br />?????*?1.?限制?'/shutdown'端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)其他的端點(diǎn)<br />?????*?3.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?4.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?5.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*?version2:<br />?????*?1.?限制所有端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?3.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?4.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*/<br /><br />????@Override<br />????protected?void?configure(HttpSecurity?http)?throws?Exception?{<br />????????//?version1<br />//????????http<br />//????????????????.authorizeRequests()<br />//????????????????????.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))<br />//????????????????????????.hasRole("ACTUATOR_ADMIN")<br />//????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />//????????????????????.permitAll()<br />//????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/")<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/**")<br />//????????????????????.authenticated()<br />//????????????????.and()<br />//????????????????.httpBasic();<br /><br />????????//?version2<br />????????http<br />????????????????.authorizeRequests()<br />????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />????????????????????.hasRole("ACTUATOR_ADMIN")<br />????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />????????????????????.permitAll()<br />????????????????.antMatchers("/")<br />????????????????????.permitAll()<br />????????????????.antMatchers("/**")<br />????????????????????.authenticated()<br />????????????????.and()<br />????????????????.httpBasic();<br />????}<br />}<br /><br />
  application.properties的相關(guān)配置如下:
  #?Spring?Security?Default?user?name?and?password<br />spring.security.user.name=actuator<br />spring.security.user.password=actuator<br />spring.security.user.roles=ACTUATOR_ADMIN<br />
  - END -
  歡迎加入我的知識星球,一起探討架構,交流源碼。加入方式,長(cháng)按下方二維碼噢:
  
  已在知識星球更新源碼解析如下:
  最近更新《芋道 SpringBoot 2.X 入門(mén)》系列,已經(jīng) 101 余篇,覆蓋了MyBatis、Redis、MongoDB、ES、分庫分表、讀寫(xiě)分離、SpringMVC、Webflux、權限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能測試等等內容。
  提供近 3W 行代碼的 SpringBoot 示例,以及超 4W 行代碼的電商微服務(wù)項目。 查看全部

  聊聊Spring Boot服務(wù)監控,健康檢查,線(xiàn)程信息,JVM堆信息,指標收集
  點(diǎn)擊上方“芋道源碼”,選擇“”
  管她前浪,還是后浪?
  能浪的浪,才是好浪!
  每天 10:33更新文章,每天掉億點(diǎn)點(diǎn)頭發(fā)...
  源碼精品專(zhuān)欄
  去年我們項目做了微服務(wù)1.0的架構轉型,但是服務(wù)監控這塊卻沒(méi)有跟上。這不,最近我就被分配了要將我們核心的微服務(wù)應用全部監控起來(lái)的任務(wù)。我們的微服務(wù)應用都是SpringBoot 應用,因此就自然而然的想到了借助Spring Boot 的Actuator 模塊。(沒(méi)吃過(guò)豬肉總聽(tīng)過(guò)豬叫見(jiàn)過(guò)豬跑吧)。
  本篇是我在完成這個(gè)工單之后,對Spring Boot Actuator模塊 學(xué)習應用的總結。在本篇文章中,你可以學(xué)習到:
  之后我還會(huì )介紹:
  推薦下自己做的 Spring Boot 的實(shí)戰項目:
  Spring Boot Actuator 模塊提供了生產(chǎn)級別的功能,比如健康檢查,審計,指標收集,HTTP 跟蹤等,幫助我們監控和管理Spring Boot 應用。這個(gè)模塊是一個(gè)采集應用內部信息暴露給外部的模塊,上述的功能都可以通過(guò)HTTP 和 JMX 訪(fǎng)問(wèn)。
  因為暴露內部信息的特性,Actuator 也可以和一些外部的應用監控系統整合(Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等)。這些監控系統提供了出色的儀表板,圖形,分析和警報,可幫助你通過(guò)一個(gè)統一友好的界面,監視和管理你的應用程序。
  Actuator使用Micrometer與這些外部應用程序監視系統集成。這樣一來(lái),只需很少的配置即可輕松集成外部的監控系統。
  Micrometer 為 Java 平臺上的性能數據收集提供了一個(gè)通用的 API,應用程序只需要使用 Micrometer 的通用 API 來(lái)收集性能指標即可。Micrometer 會(huì )負責完成與不同監控系統的適配工作。這就使得切換監控系統變得很容易。
  對比 Slf4j 之于 Java Logger 中的定位。
  推薦下自己做的 Spring Cloud 的實(shí)戰項目:
  我們先創(chuàng )建一個(gè)demo應用。
  spring?init?-d=web,actuator?-n=actuator-demo?actuator-demo<br /><br />
  image.png
  <br />????...<br />?<br />??org.springframework.boot<br />??spring-boot-starter-actuator<br />?<br />????...<br /><br /><br /><br />
  dependencies?{<br />?compile("org.springframework.boot:spring-boot-starter-actuator")<br />}<br /><br />
  Spring Boot 提供了所謂的 endpoints (下文翻譯為端點(diǎn))給外部來(lái)與應用程序進(jìn)行訪(fǎng)問(wèn)和交互。
  打比方來(lái)說(shuō),/health 端點(diǎn) 提供了關(guān)于應用健康情況的一些基礎信息。metrics 端點(diǎn)提供了一些有用的應用程序指標(JVM 內存使用、系統CPU使用等)。
  這些 Actuator 模塊本來(lái)就有的端點(diǎn)我們稱(chēng)之為原生端點(diǎn)。根據端點(diǎn)的作用的話(huà),我們大概可以分為三大類(lèi):
  詳細的原生端點(diǎn)介紹,請以官網(wǎng)為準,這里就不贅述徒增篇幅。
  需要注意的就是:
  我們可以通過(guò)以下配置,來(lái)配置通過(guò)JMX 和 HTTP 暴露的端點(diǎn)。
  PropertyDefault
  management.endpoints.jmx.exposure.exclude
  management.endpoints.jmx.exposure.include
  *
  management.endpoints.web.exposure.exclude
  management.endpoints.web.exposure.include
  info, healt
  可以打開(kāi)所有的監控點(diǎn)
  management.endpoints.web.exposure.include=*<br /><br />
  也可以選擇打開(kāi)部分,"*" 代表暴露所有的端點(diǎn),如果指定多個(gè)端點(diǎn),用","分開(kāi)
  management.endpoints.web.exposure.exclude=beans,trace<br /><br />
  Actuator 默認所有的監控點(diǎn)路徑都在/actuator/*,當然如果有需要這個(gè)路徑也支持定制。
  management.endpoints.web.base-path=/minitor<br /><br />
  設置完重啟后,再次訪(fǎng)問(wèn)地址就會(huì )變成/minitor/*。
  現在我們按照如下配置:
  #?"*"?代表暴露所有的端點(diǎn)?如果指定多個(gè)端點(diǎn),用","分開(kāi)<br />management.endpoints.web.exposure.include=*<br />#?賦值規則同上<br />management.endpoints.web.exposure.exclude=<br /><br />
  啟動(dòng)DEMO程序,訪(fǎng)問(wèn):8080/actuator,查看暴露出來(lái)的端點(diǎn):
  image.png
  上面這樣顯示是因為chrome 瀏覽器安裝了 JSON-handle 插件,實(shí)際上就是返回一大段json
  下面,我會(huì )著(zhù)重介紹幾個(gè)比較重要的端點(diǎn)。
  /health端點(diǎn)會(huì )聚合你程序的健康指標,來(lái)檢查程序的健康情況。端點(diǎn)公開(kāi)的應用健康信息取決于:
  management.endpoint.health.show-details=always<br /><br />
  該屬性可以使用以下值之一進(jìn)行配置:
  NameDescription
  never
  不展示詳細信息,up或者down的狀態(tài),默認配置
  when-authorized
  詳細信息將會(huì )展示給通過(guò)認證的用戶(hù)。授權的角色可以通過(guò)management.endpoint.health.roles配置
  always
  對所有用戶(hù)暴露詳細信息
  按照上述配置,配置成always之后,我們啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/health端口,可以看到這樣的信息:
  image.png
  是不是感覺(jué)好像健康信息有點(diǎn)少?先別急,那是因為我們創(chuàng )建的是一個(gè)最基礎的Demo項目,沒(méi)有依賴(lài)很多的組件。
  /health端點(diǎn)有很多自動(dòng)配置的健康指示器:如redis、rabbitmq、db等組件。當你的項目有依賴(lài)對應組件的時(shí)候,這些健康指示器就會(huì )被自動(dòng)裝配,繼而采集對應的信息。如上面的 diskSpace 節點(diǎn)信息就是DiskSpaceHealthIndicator 在起作用。
  
  image.png
  上述截圖取自官方文檔
  這是我另一個(gè)項目的/health端點(diǎn)信息。
  image.png
  當如上的組件有一個(gè)狀態(tài)異常,應用服務(wù)的整體狀態(tài)即為down。我們也可以通過(guò)配置禁用某個(gè)組件的健康監測。
  management.health.mongo.enabled:?false<br /><br />
  或者禁用所有自動(dòng)配置的健康指示器:
  management.health.defaults.enabled:?false<br /><br />
  當然你也可以自定義一個(gè)Health Indicator,只需要實(shí)現HealthIndicator 接口或者繼承AbstractHealthIndicator類(lèi)。
  /**<br />?*?@author?Richard_yyf<br />?*?@version?1.0?2020/1/16<br />?*/<br />@Component<br />public?class?CustomHealthIndicator?extends?AbstractHealthIndicator?{<br /><br />????@Override<br />????protected?void?doHealthCheck(Health.Builder?builder)?throws?Exception?{<br />????????//?使用?builder?來(lái)創(chuàng )建健康狀態(tài)信息<br />????????//?如果你throw?了一個(gè)?exception,那么status?就會(huì )被置為DOWN,異常信息會(huì )被記錄下來(lái)<br />????????builder.up()<br />????????????????.withDetail("app",?"這個(gè)項目很健康")<br />????????????????.withDetail("error",?"Nothing,?I'm?very?good");<br />????}<br />}<br /><br />
  最終效果:
  image.png
  /metrics端點(diǎn)用來(lái)返回當前應用的各類(lèi)重要度量指標,比如:內存信息、線(xiàn)程信息、垃圾回收信息、tomcat、數據庫連接池等。
  {<br />????"names":?[<br />????????"tomcat.threads.busy",<br />????????"jvm.threads.states",<br />????????"jdbc.connections.active",<br />????????"jvm.gc.memory.promoted",<br />????????"http.server.requests",<br />????????"hikaricp.connections.max",<br />????????"hikaricp.connections.min",<br />????????"jvm.memory.used",<br />????????"jvm.gc.max.data.size",<br />????????"jdbc.connections.max",<br />?????????....<br />????]<br />}<br /><br />
  不同于1.x,Actuator在這個(gè)界面看不到具體的指標信息,只是展示了一個(gè)指標列表。 為了獲取到某個(gè)指標的詳細信息,我們可以請求具體的指標信息,像這樣:
  http://localhost:8080/actuator/metrics/{MetricName}<br /><br />
  比如我訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max,返回信息如下:
  image.png
  你也可以用query param的方式查看單獨的一塊區域。比如你可以訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max?tag=id:Metaspace。結果就是:
  image.png
  /loggers 端點(diǎn)暴露了我們程序內部配置的所有logger的信息。我們訪(fǎng)問(wèn)/actuator/loggers可以看到,
  image.png
  你也可以通過(guò)下述方式訪(fǎng)問(wèn)單獨一個(gè)logger,
  http://localhost:8080/actuator/loggers/{name}<br /><br />
  比如我現在訪(fǎng)問(wèn) root logger,:8080/actuator/loggers/root
  {<br />????"configuredLevel":?"INFO",<br />????"effectiveLevel":?"INFO"<br />}<br /><br />
  /loggers端點(diǎn)我最想提的就是這個(gè)功能,能夠動(dòng)態(tài)修改你的日志等級。
  比如,我們可以通過(guò)下述方式來(lái)修改 root logger的日志等級。我們只需要發(fā)起一個(gè)URL 為:8080/actuator/loggers/root的POST請求,POST報文如下:
  {<br />???"configuredLevel":?"DEBUG"<br />}<br /><br />
  image.png
  仔細想想,這個(gè)功能是不是非常有用。如果在生產(chǎn)環(huán)境中,你想要你的應用輸出一些Debug信息以便于你診斷一些異常情況,你你只需要按照上述方式就可以修改,而不需要重啟應用。
  如果想重置成默認值,把value 改成 null
  /info端點(diǎn)可以用來(lái)展示你程序的信息。我理解過(guò)來(lái)就是一些程序的基礎信息。并且你可以按照自己的需求在配置文件application.properties中個(gè)性化配置(默認情況下,該端點(diǎn)只會(huì )返回一個(gè)空的json內容。):
  info.app.name=actuator-test-demo<br />info.app.encoding=UTF-8<br />info.app.java.source=1.8<br />info.app.java.target=1.8<br />#?在?maven?項目中你可以直接用下列方式引用?maven?properties的值<br />#?info.app.encoding=@project.build.sourceEncoding@<br />#?info.app.java.source=@java.version@<br />#?info.app.java.target=@java.version@<br /><br />
  啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/info:
  {<br />????"app":?{<br />????????"encoding":?"UTF-8",<br />????????"java":?{<br />????????????"source":?"1.8.0_131",<br />????????????"target":?"1.8.0_131"<br />????????},<br />????????"name":?"actuator-test-demo"<br />????}<br />}<br /><br />
  /beans端點(diǎn)會(huì )返回Spring 容器中所有bean的別名、類(lèi)型、是否單例、依賴(lài)等信息。
  訪(fǎng)問(wèn):8080/actuator/beans,返回如下:
  image.png
  訪(fǎng)問(wèn)::8080/actuator/heapdump會(huì )自動(dòng)生成一個(gè) Jvm 的堆文件 heapdump。我們可以使用 JDK 自帶的 Jvm 監控工具 VisualVM 打開(kāi)此文件查看內存快照。
  image.png
  這個(gè)端點(diǎn)我個(gè)人覺(jué)得特別有用,方便我們在日常定位問(wèn)題的時(shí)候查看線(xiàn)程的情況。主要展示了線(xiàn)程名、線(xiàn)程ID、線(xiàn)程的狀態(tài)、是否等待鎖資源、線(xiàn)程堆棧等信息。就是可能查看起來(lái)不太直觀(guān)。訪(fǎng)問(wèn):8080/actuator/threaddump返回如下:
  image.png
  這個(gè)端點(diǎn)屬于操作控制類(lèi)端點(diǎn),可以?xún)?yōu)雅關(guān)閉 Spring Boot 應用。要使用這個(gè)功能首先需要在配置文件中開(kāi)啟:
  management.endpoint.shutdown.enabled=true<br /><br />
  由于 shutdown 接口默認只支持 POST 請求 ,我們啟動(dòng)Demo項目,向:8080/actuator/shutdown發(fā)起POST請求。返回信息:
  {<br />????"message":?"Shutting?down,?bye..."<br />}<br /><br />
  然后應用程序被關(guān)閉。
  由于開(kāi)放關(guān)閉應用的操作本身是一件非常危險 的事,所以真正在線(xiàn)上使用的時(shí)候,我們需要對其加入一定的保護機制,比如:定制Actuator的端點(diǎn)路徑、整合Spring Security進(jìn)行安全校驗 等。(不是特別必要的話(huà),這個(gè)端點(diǎn)不用開(kāi))
  由于端點(diǎn)的信息和產(chǎn)生的交互都是非常敏感的,必須防止未經(jīng)授權的外部訪(fǎng)問(wèn)。如果您的應用程序中存在Spring Security 的依賴(lài),則默認情況下使用基于表單的HTTP身份驗證 來(lái)保護端點(diǎn)。
  如果沒(méi)有,只需要增加對應的依賴(lài)即可:
  <br />???org.springframework.boot<br />???spring-boot-starter-security<br /><br /><br />
  添加之后,我們需要定義安全校驗規則,來(lái)覆蓋Spring Security 的默認配置。
  這里我給出了兩個(gè)版本的模板配置:
  import?org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;<br />import?org.springframework.boot.actuate.context.ShutdownEndpoint;<br />import?org.springframework.boot.autoconfigure.security.servlet.PathRequest;<br />import?org.springframework.context.annotation.Configuration;<br />import?org.springframework.security.config.annotation.web.builders.HttpSecurity;<br />import?org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;<br /><br />/**<br />?*?@author?Richard_yyf<br />?*/<br />@Configuration<br />public?class?ActuatorSecurityConfig?extends?WebSecurityConfigurerAdapter?{<br /><br />????/*<br />?????*?version1:<br />?????*?1.?限制?'/shutdown'端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)其他的端點(diǎn)<br />?????*?3.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?4.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?5.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*?version2:<br />?????*?1.?限制所有端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?3.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?4.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*/<br /><br />????@Override<br />????protected?void?configure(HttpSecurity?http)?throws?Exception?{<br />????????//?version1<br />//????????http<br />//????????????????.authorizeRequests()<br />//????????????????????.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))<br />//????????????????????????.hasRole("ACTUATOR_ADMIN")<br />//????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />//????????????????????.permitAll()<br />//????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/")<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/**")<br />//????????????????????.authenticated()<br />//????????????????.and()<br />//????????????????.httpBasic();<br /><br />????????//?version2<br />????????http<br />????????????????.authorizeRequests()<br />????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />????????????????????.hasRole("ACTUATOR_ADMIN")<br />????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />????????????????????.permitAll()<br />????????????????.antMatchers("/")<br />????????????????????.permitAll()<br />????????????????.antMatchers("/**")<br />????????????????????.authenticated()<br />????????????????.and()<br />????????????????.httpBasic();<br />????}<br />}<br /><br />
  application.properties的相關(guān)配置如下:
  #?Spring?Security?Default?user?name?and?password<br />spring.security.user.name=actuator<br />spring.security.user.password=actuator<br />spring.security.user.roles=ACTUATOR_ADMIN<br />
  - END -
  歡迎加入我的知識星球,一起探討架構,交流源碼。加入方式,長(cháng)按下方二維碼噢:
  
  已在知識星球更新源碼解析如下:
  最近更新《芋道 SpringBoot 2.X 入門(mén)》系列,已經(jīng) 101 余篇,覆蓋了MyBatis、Redis、MongoDB、ES、分庫分表、讀寫(xiě)分離、SpringMVC、Webflux、權限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能測試等等內容。
  提供近 3W 行代碼的 SpringBoot 示例,以及超 4W 行代碼的電商微服務(wù)項目。

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 161 次瀏覽 ? 2022-05-29 05:57 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 91 次瀏覽 ? 2022-05-28 21:46 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

springcloud微服務(wù)實(shí)踐:天氣數據API微服務(wù)的實(shí)現

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 69 次瀏覽 ? 2022-05-27 20:42 ? 來(lái)自相關(guān)話(huà)題

  springcloud微服務(wù)實(shí)踐:天氣數據API微服務(wù)的實(shí)現
  天氣數據API 微服務(wù)的實(shí)現
  天氣數據API微服務(wù)包含了天氣數據查詢(xún)組件。天氣數據查詢(xún)組件提供了天氣數據查詢(xún)的接口。
  我們的數據已經(jīng)通過(guò)天氣數據采集微服務(wù)集成到了Redis 存儲中,天氣數據API微服務(wù)只需要從Redis獲取數據,而后從接口中暴露出去即可。
  在micro-weather-report應用的基礎上,我們將對其進(jìn)行逐步的拆分,形成一個(gè)新的微服務(wù)msa-weather-data-server應用。
  
  所需環(huán)境
  為了演示本例子,需要采用如下開(kāi)發(fā)環(huán)境。
  修改天氣數據服務(wù)接口及實(shí)現
  在com.waylau.spring.cloud. weather.service包下,我們之前已經(jīng)定義了該應用的天氣數據服務(wù)接口WeatherDataService。
  public interface WeatherDataService {<br />*★<br />*根據城市ID查詢(xún)天氣數據<br />@param cityId<br />@return <br />WeatherResponse getDataByCityId(String cityId);<br />/**<br />★根據城市名稱(chēng)查詢(xún)天氣數據<br />* @param cityId<br />* Creturn<br />*/<br />Wea therResponse getDataByCityName (String cityName) ;<br />}
  對于該微服務(wù)而言,我們并不需要同步天氣的業(yè)務(wù)需求,所以把之前定義的syncDataByCityId方法刪除了。
  WeatherDataServicelmpl是對WeatherDataService 接口的實(shí)現,也要做出相應的調整,將同步天氣的代碼邏輯都刪除,保留以下代碼。
  package com. way1au . spr ing.cloud.weather .service;<br />import java. io. IOException;<br />import org.slf4j . Logger;<br />import org.slf4j. LoggerFactory;<br />import org.springf ramework. beans. factory . annotation.Autowired;<br />import org. springfr amework. data. redis. core. StringRedisTemplate;<br />import org. springf ramework. data. redis.core. ValueOperations;<br />import org. springf ramework. stereotype. Service;<br />import com. fasterxml. jackson.databind. objectMapper;<br />import com. waylau. spring. cloud . weather . vo . Wea therResponse;<br />/**<br />k天氣數據服務(wù).<br />@since 1.0.0 2017年10月29日 <br />* @author Way Lau<br />@Service<br />public class WeatherDataServiceImpl implements WeatherDataService {<br />private final static Logger logger = LoggerFactory .getLogger (Weather<br />DataServiceImpl.class) ;<br />@Autowi red<br />private StringRedisTemplate stringRedisTemplate; <br />private final String WEATHER API = "http://wthrcdn. etouch . cn/weather_<br />mini";<br />@Override<br />public WeatherResponse getDa taByCityId(String cityId) {<br />String uri = WEATHER API + "?citykey=" + cityId;<br />return this. doGetWea therData (uri) ;<br />@Override<br />public WeatherResponse getDataByCityName (String cityName) {<br />String uri = WEATHER_ API + "?city=" + cityName ;<br />return this. doGe tWeatherData (uri) ;<br />private WeatherResponse doGetWea therData (String uri)<br />valueOperations ops = this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />/先查緩存,查不到拋出異常<br />if (!this. stringRedisTemplate . hasKey (key)) {<br />logger .error("不存在key "+ key) ;<br />throw new Runt imeException ("沒(méi)有相應的天氣信息") ;<br />} else {<br />logger.info("存在key"+ key + ", value=" + ops.get (key));<br />strBody = ops.get (key) ;<br />}<br />0bj ectMapper mapper = new ObjectMapper () ;<br />WeatherResponse weather = null;<br />try {<br />weather = mapper . readvalue (strBody, WeatherResponse.class) ;<br />} catch (IOException e) {<br />logger . error ("JSON反序列化異常! ",e);<br />throw new RuntimeException ("天氣信息解析失敗") ;<br />return weather;<br />}<br />}
  其中需要注意的是:
  ●原有的RestTemplate用作REST客戶(hù)端來(lái)進(jìn)行天氣數據的同步,這個(gè)類(lèi)相關(guān)的代碼都可以刪除了;
  ●服務(wù) 會(huì )先從緩存中進(jìn)行查詢(xún),查不到數據就拋出異常(有可能該城市的天氣數據未同步,或者是數據已經(jīng)過(guò)期) ;
  在執行反序列化JSON過(guò)程中也可能遭遇異常,同樣將異常信息拋出。
  除上述WeatherDataServicelmpl、WeatherDataService 外,其他服務(wù)層的代碼都可以刪除了。
  調整控制層的代碼
  除了WeatherController 外,其他控制層的代碼都不需要了。
  WeatherController仍然是原有的代碼保持不變。
  
  刪除配置類(lèi)、天氣數據同步任務(wù)和工具類(lèi)
  配置類(lèi)RestConfiguration、QuartzConfiguration 及任務(wù)類(lèi)WeatherDataSyncJob、 工具類(lèi)Xml-
  Builder的代碼都可以刪除了。
  清理值對象
  值對象我們需要保留解析天氣相關(guān)的類(lèi)即可,其他值對象(如City. CityList等)都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并 刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并 刪除。
  最后,要清理Thymeleaf在application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。 查看全部

  springcloud微服務(wù)實(shí)踐:天氣數據API微服務(wù)的實(shí)現
  天氣數據API 微服務(wù)的實(shí)現
  天氣數據API微服務(wù)包含了天氣數據查詢(xún)組件。天氣數據查詢(xún)組件提供了天氣數據查詢(xún)的接口。
  我們的數據已經(jīng)通過(guò)天氣數據采集微服務(wù)集成到了Redis 存儲中,天氣數據API微服務(wù)只需要從Redis獲取數據,而后從接口中暴露出去即可。
  在micro-weather-report應用的基礎上,我們將對其進(jìn)行逐步的拆分,形成一個(gè)新的微服務(wù)msa-weather-data-server應用。
  
  所需環(huán)境
  為了演示本例子,需要采用如下開(kāi)發(fā)環(huán)境。
  修改天氣數據服務(wù)接口及實(shí)現
  在com.waylau.spring.cloud. weather.service包下,我們之前已經(jīng)定義了該應用的天氣數據服務(wù)接口WeatherDataService。
  public interface WeatherDataService {<br />*★<br />*根據城市ID查詢(xún)天氣數據<br />@param cityId<br />@return <br />WeatherResponse getDataByCityId(String cityId);<br />/**<br />★根據城市名稱(chēng)查詢(xún)天氣數據<br />* @param cityId<br />* Creturn<br />*/<br />Wea therResponse getDataByCityName (String cityName) ;<br />}
  對于該微服務(wù)而言,我們并不需要同步天氣的業(yè)務(wù)需求,所以把之前定義的syncDataByCityId方法刪除了。
  WeatherDataServicelmpl是對WeatherDataService 接口的實(shí)現,也要做出相應的調整,將同步天氣的代碼邏輯都刪除,保留以下代碼。
  package com. way1au . spr ing.cloud.weather .service;<br />import java. io. IOException;<br />import org.slf4j . Logger;<br />import org.slf4j. LoggerFactory;<br />import org.springf ramework. beans. factory . annotation.Autowired;<br />import org. springfr amework. data. redis. core. StringRedisTemplate;<br />import org. springf ramework. data. redis.core. ValueOperations;<br />import org. springf ramework. stereotype. Service;<br />import com. fasterxml. jackson.databind. objectMapper;<br />import com. waylau. spring. cloud . weather . vo . Wea therResponse;<br />/**<br />k天氣數據服務(wù).<br />@since 1.0.0 2017年10月29日 <br />* @author Way Lau<br />@Service<br />public class WeatherDataServiceImpl implements WeatherDataService {<br />private final static Logger logger = LoggerFactory .getLogger (Weather<br />DataServiceImpl.class) ;<br />@Autowi red<br />private StringRedisTemplate stringRedisTemplate; <br />private final String WEATHER API = "http://wthrcdn. etouch . cn/weather_<br />mini";<br />@Override<br />public WeatherResponse getDa taByCityId(String cityId) {<br />String uri = WEATHER API + "?citykey=" + cityId;<br />return this. doGetWea therData (uri) ;<br />@Override<br />public WeatherResponse getDataByCityName (String cityName) {<br />String uri = WEATHER_ API + "?city=" + cityName ;<br />return this. doGe tWeatherData (uri) ;<br />private WeatherResponse doGetWea therData (String uri)<br />valueOperations ops = this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />/先查緩存,查不到拋出異常<br />if (!this. stringRedisTemplate . hasKey (key)) {<br />logger .error("不存在key "+ key) ;<br />throw new Runt imeException ("沒(méi)有相應的天氣信息") ;<br />} else {<br />logger.info("存在key"+ key + ", value=" + ops.get (key));<br />strBody = ops.get (key) ;<br />}<br />0bj ectMapper mapper = new ObjectMapper () ;<br />WeatherResponse weather = null;<br />try {<br />weather = mapper . readvalue (strBody, WeatherResponse.class) ;<br />} catch (IOException e) {<br />logger . error ("JSON反序列化異常! ",e);<br />throw new RuntimeException ("天氣信息解析失敗") ;<br />return weather;<br />}<br />}
  其中需要注意的是:
  ●原有的RestTemplate用作REST客戶(hù)端來(lái)進(jìn)行天氣數據的同步,這個(gè)類(lèi)相關(guān)的代碼都可以刪除了;
  ●服務(wù) 會(huì )先從緩存中進(jìn)行查詢(xún),查不到數據就拋出異常(有可能該城市的天氣數據未同步,或者是數據已經(jīng)過(guò)期) ;
  在執行反序列化JSON過(guò)程中也可能遭遇異常,同樣將異常信息拋出。
  除上述WeatherDataServicelmpl、WeatherDataService 外,其他服務(wù)層的代碼都可以刪除了。
  調整控制層的代碼
  除了WeatherController 外,其他控制層的代碼都不需要了。
  WeatherController仍然是原有的代碼保持不變。
  
  刪除配置類(lèi)、天氣數據同步任務(wù)和工具類(lèi)
  配置類(lèi)RestConfiguration、QuartzConfiguration 及任務(wù)類(lèi)WeatherDataSyncJob、 工具類(lèi)Xml-
  Builder的代碼都可以刪除了。
  清理值對象
  值對象我們需要保留解析天氣相關(guān)的類(lèi)即可,其他值對象(如City. CityList等)都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并 刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并 刪除。
  最后,要清理Thymeleaf在application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。

天氣數據采集微服務(wù)的實(shí)現:數據采集組件、數據存儲組件

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 130 次瀏覽 ? 2022-05-25 13:07 ? 來(lái)自相關(guān)話(huà)題

  天氣數據采集微服務(wù)的實(shí)現:數據采集組件、數據存儲組件
  . Spring Boot Data Redis Starter 2.0.0.M4。
  .Redis 3.2.100。
  . Spring Boot Quartz Starter 2.0.0.M4。
  . Quartz Scheduler 2.3.0。
  新增天氣數據采集服務(wù)接口及實(shí)現
  在
  com.waylau.spring.cloud.weather.service包下,我們定義了該應用的天氣數據采集服務(wù)接口WeatherDataCollectionService。
  public interface WeatherDataCollectionService {<br />/**<br />*根據城市工D同步天氣數據<br />*<br />*@param cityId<br />*@return<br />*/<br />void syncDataByCityId(String cityId);<br />}
  WeatherDataCollectionService只有一個(gè)同步天氣數據的方法。WeatherDataCollectionServicelmpl是對WeatherDataCollectionService接口的實(shí)現。
  package com.waylau.spring.cloud.weather.service;<br />import java.util.concurrent.TimeUnit;<br />import org.slf4j.Logger;<br />import org.slf4j-LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.data.redis.core.StringRedisTemplate;<br />import org.springframework.data.redis.core.ValueOperations;<br />import org.springframework.http.ResponseEntity;<br />import org.springframework.stereotype.Service;<br />import org.springframework.web.client.RestTemplate;<br />/*★<br />*天氣數據采集服務(wù).<br />*<br />*@since 1.o.0 2017年10月29日<br />* @author Way Lau<br />*/<br />@service<br />public class WeatherDataCollectionServicelmpl implements WeatherData<br />CollectionService {<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatacollectionServicelmpl.class);<br />@Autowired<br />private RestTemplate restTemplate;<br />@Autowired<br />private stringRedisTemplate stringRedisTemplate;<br />private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";<br />private final Long TIME_OUT = 1800L;//緩存超時(shí)時(shí)間<br />@override<br />public void syncDataByCityId(String cityId) {<br />logger.info ("Start同步天氣.cityId: "+cityId);<br />String uri = WEATHER_API +"?citykey=" +cityId;<br />this.saveweatherData (uri);<br />logger.info("End同步天氣");<br />private void saveWeatherData(String uri) {<br />ValueOperations ops= this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />ResponseEntity response = restTemplate.getForEntity(uri,<br />String.class);<br />if(response.getStatusCodeValue()=-200) f<br />strBody=response.getBody(;<br />ops.set(key,strBody,TIME_OUT,TimeUnit.SECONDS);<br />}<br />}
  WeatherDataCollectionServiceImpl的實(shí)現過(guò)程,我們在之前的章節中也已經(jīng)詳細介紹過(guò),大家也已經(jīng)非常熟悉了。無(wú)非就是通過(guò)REST客戶(hù)端去調用第三方的天氣數據接口,并將返回的數據直接放入Redis存儲中。
  同時(shí),我們需要設置Redis數據的過(guò)期時(shí)間。
  修改天氣數據同步任務(wù)
  對于天氣數據同步任務(wù)WeatherDataSyncJob,我們要做一些調整。把之前所依賴(lài)的CityData-Service、WeatherDataService改為
  WeatherDataCollectionService。
  import java.util.ArrayList;<br />import java.util.List;<br />import org.quartz.JobExecutionContext;<br />import org.quartz.JobExecutionException;<br />import org.slf4j-Logger;<br />import org.slf4j.LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.scheduling.quartz.QuartzJobBean;<br />import com.waylau.spring.cloud.weather.service.WeatherDataCollection<br />service;<br />import com.waylau.spring.cloud.weather.vo.City;<br />*★<br />天氣數據同步任務(wù).<br />*<br />*@since 1.0.0 2017年10月29日<br />* author <a href=span style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;color: rgb(0, 117, 59);""https://waylau.com"/span>Way Lau</a><br />*/<br />public class WeatherDataSyncJob extends QuartzJobBean<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatasyncJob.class);<br />@Autowired<br />private WeatherDataCollectionService weatherDataCollectionService;<br />@override<br />protected void executeInternal (JobExecutionContext context) throws<br />JobExecutionException{<br />logger.info("'Start天氣數據同步任務(wù)");<br />/TODO改為由城市數據API微服務(wù)來(lái)提供數據<br />工istcityList =null;<br />trY {<br />//TODO 調用城市數據APT<br />cityList = new ArrayEist();<br />City city = new City();<br />city.setCityId("101280601");<br />cityList.add(city);<br />}catch(Exception e){<br />logger.error("獲取城市信息異常!",e);<br />throw new RuntimeException("獲取城市信息異常!",e);<br />}<br />for(City city : cityList){<br />String cityld = city.getCityld(;<br />logger.info("天氣數據同步任務(wù)中,cityId:" +cityId);<br />//根據城市ID同步天氣數據<br />weatherDataCollectionService.syncDataByCityId(cityId);<br />logger.info("End 天氣數據同步任務(wù)");<br />}<br />}
  這里需要注意的是,定時(shí)器仍然對城市ID列表有依賴(lài),只不過(guò)這個(gè)依賴(lài)最終會(huì )由其他應用(城市數據API微服務(wù))來(lái)提供,所以這里暫時(shí)還沒(méi)有辦法完全寫(xiě)完,先用“TODO”來(lái)標識這個(gè)方法,后期還需要改進(jìn)。但為了能讓整個(gè)程序可以完整地走下去,我們在程序里面假設返回了一個(gè)城市ID為“101280601”的城市信息。
  配置類(lèi)
  配置類(lèi)仍然保留之前的RestConfiguration、QuartzConfiguration的代碼不變,如下所示。
  1.RestConfiguration
  RestConfiguration用于配置REST客戶(hù)端。
  import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.boot.web.client.RestTemplateBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import org.springframework.web.client.RestTemplate;<br />/**<br />*REST 配置類(lèi).<br />*<br />*@since 1.0.0 2017年10月18日<br />* @author Way Lau<br />*/<br />@configuration<br />public class RestConfiguration {<br />@Autowired<br />private RestTemplateBuilder builder;<br />CBean<br />public RestTemplate restTemplate(){<br />return builder.build();<br />}<br />}
  2.QuartzConfiguration
  QuartzConfiguration類(lèi)用于定時(shí)任務(wù)。
  import org.quartz.JobBuilder;<br />import org.quartz.JobDetail;<br />import org.quartz.SimpleScheduleBuilder;<br />import org.quartz.Trigger;<br />import org.quartz.TriggerBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import com.waylau.spring.cloud.weather.job.WeatherDataSyncJob;<br />/*★<br />*Quartz配置類(lèi).<br />*<br />*since 1.0.0 2017年10月23日<br />* author Way Lau<br />*/<br />@configuration<br />public class QuartzConfiguration <br />private final int TIME=1800;1/更新頻率<br />@Bean<br />public JobDetail weatherDataSyncJobJobDetail(){<br />return JobBuilder.newJob(WeatherDataSyncJob.class).withIdentity<br />("weatherDataSyncJob")<br />.storeDurably() .build(;<br />}<br />CBean<br />public Trigger sampleJobTrigger({<br />SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.<br />simpleschedule()<br />.withIntervalInSeconds (TIME).repeatForever();<br />return TriggerBuilder.newTrigger().forJob(weatherDataSyncJob-<br />JobDetail())<br />.withIdentity("weatherDataSyncTrigger").withSchedule<br />(scheduleBuilder).build();<br />}<br />}
  值對象
  值對象我們只需要保留City即可,其他值對象都可以刪除了。需要注意的是,由于天氣數據采集微服務(wù)并未涉及對XML數據的解析,所以之前在City上添加的相關(guān)的JABX注解,都是可以一并刪除的。
  以下是新的City類(lèi)。
  public class City {<br />private String cityId;<br />private string cityName;<br />private string cityCode;<br />private String province;<br />1/省略getter/setter方法}
  工具類(lèi)
  工具類(lèi)XmlBuilder的代碼都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并刪除。
  最后,要清理Thymeleaf在 application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。
  測試和運行
  首先,在進(jìn)行測試前,需要將Redis服務(wù)器啟動(dòng)起來(lái)。
  而后再啟動(dòng)應用。啟動(dòng)應用之后,定時(shí)器就自動(dòng)開(kāi)始執行。整個(gè)同步過(guò)程可以通過(guò)以下控制臺信息看到。
  2017-10-29 22:26:41.748 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.WeatherDatasyncJob<br />:Start天氣數據同步任務(wù)<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.weatherDataSyncJob:天氣數據同步任務(wù)中,cityId:101280601<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] s.c.w.s.Weather<br />DataCollectionServiceImpl: Start同步天氣.cityId:101280601<br />2017-10-29 22:26:41.836 INFO 13956 ---[<br />main]o.s.b.w.embedded.<br />tomcat.TomcatwebServer: Tomcat started on port(s):8080 (http)<br />2017-10-29 22:26:41.840 INFO 13956 ---[<br />main]c.w.spring.<br />cloud.weather.Application:Started Application in 4.447 seconds<br />(JVM running for 4.788)<br />2017-10-29 22:26:41.919 INFO 13956---[eduler_Worker-1] S.c.w.s.eather<br />DatacollectionServiceImpl :End同步天氣<br />2017-10-29 22:26:41.920 INFO 13956---[eduler Worker-1] C.W.s.c.weather.<br />job.WeatherDataSyncJob:End 天氣數據同步任務(wù)
  由于我們只是在代碼里面“硬編碼”了一個(gè)城市ID為“101280601”的城市信息,所以,只有一條同步記錄。
  當然,我們也能通過(guò)Redis Desktop Manager,來(lái)方便查看存儲到Redis里面的數據,如圖7-3所示。
  
  本篇內容給大家講解的是天氣數據采集微服務(wù)的實(shí)現
  下篇文章給大家講解天氣數據API微服務(wù)的實(shí)現;
  覺(jué)得文章不錯的朋友可以轉發(fā)此文關(guān)注小編;
  感謝大家的支持??!
  本文就是愿天堂沒(méi)有BUG給大家分享的內容,大家有收獲的話(huà)可以分享下,想學(xué)習更多的話(huà)可以到微信公眾號里找我,我等你哦。 查看全部

  天氣數據采集微服務(wù)的實(shí)現:數據采集組件、數據存儲組件
  . Spring Boot Data Redis Starter 2.0.0.M4。
  .Redis 3.2.100。
  . Spring Boot Quartz Starter 2.0.0.M4。
  . Quartz Scheduler 2.3.0。
  新增天氣數據采集服務(wù)接口及實(shí)現
  在
  com.waylau.spring.cloud.weather.service包下,我們定義了該應用的天氣數據采集服務(wù)接口WeatherDataCollectionService。
  public interface WeatherDataCollectionService {<br />/**<br />*根據城市工D同步天氣數據<br />*<br />*@param cityId<br />*@return<br />*/<br />void syncDataByCityId(String cityId);<br />}
  WeatherDataCollectionService只有一個(gè)同步天氣數據的方法。WeatherDataCollectionServicelmpl是對WeatherDataCollectionService接口的實(shí)現。
  package com.waylau.spring.cloud.weather.service;<br />import java.util.concurrent.TimeUnit;<br />import org.slf4j.Logger;<br />import org.slf4j-LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.data.redis.core.StringRedisTemplate;<br />import org.springframework.data.redis.core.ValueOperations;<br />import org.springframework.http.ResponseEntity;<br />import org.springframework.stereotype.Service;<br />import org.springframework.web.client.RestTemplate;<br />/*★<br />*天氣數據采集服務(wù).<br />*<br />*@since 1.o.0 2017年10月29日<br />* @author Way Lau<br />*/<br />@service<br />public class WeatherDataCollectionServicelmpl implements WeatherData<br />CollectionService {<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatacollectionServicelmpl.class);<br />@Autowired<br />private RestTemplate restTemplate;<br />@Autowired<br />private stringRedisTemplate stringRedisTemplate;<br />private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";<br />private final Long TIME_OUT = 1800L;//緩存超時(shí)時(shí)間<br />@override<br />public void syncDataByCityId(String cityId) {<br />logger.info ("Start同步天氣.cityId: "+cityId);<br />String uri = WEATHER_API +"?citykey=" +cityId;<br />this.saveweatherData (uri);<br />logger.info("End同步天氣");<br />private void saveWeatherData(String uri) {<br />ValueOperations ops= this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />ResponseEntity response = restTemplate.getForEntity(uri,<br />String.class);<br />if(response.getStatusCodeValue()=-200) f<br />strBody=response.getBody(;<br />ops.set(key,strBody,TIME_OUT,TimeUnit.SECONDS);<br />}<br />}
  WeatherDataCollectionServiceImpl的實(shí)現過(guò)程,我們在之前的章節中也已經(jīng)詳細介紹過(guò),大家也已經(jīng)非常熟悉了。無(wú)非就是通過(guò)REST客戶(hù)端去調用第三方的天氣數據接口,并將返回的數據直接放入Redis存儲中。
  同時(shí),我們需要設置Redis數據的過(guò)期時(shí)間。
  修改天氣數據同步任務(wù)
  對于天氣數據同步任務(wù)WeatherDataSyncJob,我們要做一些調整。把之前所依賴(lài)的CityData-Service、WeatherDataService改為
  WeatherDataCollectionService。
  import java.util.ArrayList;<br />import java.util.List;<br />import org.quartz.JobExecutionContext;<br />import org.quartz.JobExecutionException;<br />import org.slf4j-Logger;<br />import org.slf4j.LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.scheduling.quartz.QuartzJobBean;<br />import com.waylau.spring.cloud.weather.service.WeatherDataCollection<br />service;<br />import com.waylau.spring.cloud.weather.vo.City;<br />*★<br />天氣數據同步任務(wù).<br />*<br />*@since 1.0.0 2017年10月29日<br />* author <a href=span style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;color: rgb(0, 117, 59);""https://waylau.com"/span>Way Lau</a><br />*/<br />public class WeatherDataSyncJob extends QuartzJobBean<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatasyncJob.class);<br />@Autowired<br />private WeatherDataCollectionService weatherDataCollectionService;<br />@override<br />protected void executeInternal (JobExecutionContext context) throws<br />JobExecutionException{<br />logger.info("'Start天氣數據同步任務(wù)");<br />/TODO改為由城市數據API微服務(wù)來(lái)提供數據<br />工istcityList =null;<br />trY {<br />//TODO 調用城市數據APT<br />cityList = new ArrayEist();<br />City city = new City();<br />city.setCityId("101280601");<br />cityList.add(city);<br />}catch(Exception e){<br />logger.error("獲取城市信息異常!",e);<br />throw new RuntimeException("獲取城市信息異常!",e);<br />}<br />for(City city : cityList){<br />String cityld = city.getCityld(;<br />logger.info("天氣數據同步任務(wù)中,cityId:" +cityId);<br />//根據城市ID同步天氣數據<br />weatherDataCollectionService.syncDataByCityId(cityId);<br />logger.info("End 天氣數據同步任務(wù)");<br />}<br />}
  這里需要注意的是,定時(shí)器仍然對城市ID列表有依賴(lài),只不過(guò)這個(gè)依賴(lài)最終會(huì )由其他應用(城市數據API微服務(wù))來(lái)提供,所以這里暫時(shí)還沒(méi)有辦法完全寫(xiě)完,先用“TODO”來(lái)標識這個(gè)方法,后期還需要改進(jìn)。但為了能讓整個(gè)程序可以完整地走下去,我們在程序里面假設返回了一個(gè)城市ID為“101280601”的城市信息。
  配置類(lèi)
  配置類(lèi)仍然保留之前的RestConfiguration、QuartzConfiguration的代碼不變,如下所示。
  1.RestConfiguration
  RestConfiguration用于配置REST客戶(hù)端。
  import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.boot.web.client.RestTemplateBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import org.springframework.web.client.RestTemplate;<br />/**<br />*REST 配置類(lèi).<br />*<br />*@since 1.0.0 2017年10月18日<br />* @author Way Lau<br />*/<br />@configuration<br />public class RestConfiguration {<br />@Autowired<br />private RestTemplateBuilder builder;<br />CBean<br />public RestTemplate restTemplate(){<br />return builder.build();<br />}<br />}
  2.QuartzConfiguration
  QuartzConfiguration類(lèi)用于定時(shí)任務(wù)。
  import org.quartz.JobBuilder;<br />import org.quartz.JobDetail;<br />import org.quartz.SimpleScheduleBuilder;<br />import org.quartz.Trigger;<br />import org.quartz.TriggerBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import com.waylau.spring.cloud.weather.job.WeatherDataSyncJob;<br />/*★<br />*Quartz配置類(lèi).<br />*<br />*since 1.0.0 2017年10月23日<br />* author Way Lau<br />*/<br />@configuration<br />public class QuartzConfiguration <br />private final int TIME=1800;1/更新頻率<br />@Bean<br />public JobDetail weatherDataSyncJobJobDetail(){<br />return JobBuilder.newJob(WeatherDataSyncJob.class).withIdentity<br />("weatherDataSyncJob")<br />.storeDurably() .build(;<br />}<br />CBean<br />public Trigger sampleJobTrigger({<br />SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.<br />simpleschedule()<br />.withIntervalInSeconds (TIME).repeatForever();<br />return TriggerBuilder.newTrigger().forJob(weatherDataSyncJob-<br />JobDetail())<br />.withIdentity("weatherDataSyncTrigger").withSchedule<br />(scheduleBuilder).build();<br />}<br />}
  值對象
  值對象我們只需要保留City即可,其他值對象都可以刪除了。需要注意的是,由于天氣數據采集微服務(wù)并未涉及對XML數據的解析,所以之前在City上添加的相關(guān)的JABX注解,都是可以一并刪除的。
  以下是新的City類(lèi)。
  public class City {<br />private String cityId;<br />private string cityName;<br />private string cityCode;<br />private String province;<br />1/省略getter/setter方法}
  工具類(lèi)
  工具類(lèi)XmlBuilder的代碼都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并刪除。
  最后,要清理Thymeleaf在 application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。
  測試和運行
  首先,在進(jìn)行測試前,需要將Redis服務(wù)器啟動(dòng)起來(lái)。
  而后再啟動(dòng)應用。啟動(dòng)應用之后,定時(shí)器就自動(dòng)開(kāi)始執行。整個(gè)同步過(guò)程可以通過(guò)以下控制臺信息看到。
  2017-10-29 22:26:41.748 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.WeatherDatasyncJob<br />:Start天氣數據同步任務(wù)<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.weatherDataSyncJob:天氣數據同步任務(wù)中,cityId:101280601<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] s.c.w.s.Weather<br />DataCollectionServiceImpl: Start同步天氣.cityId:101280601<br />2017-10-29 22:26:41.836 INFO 13956 ---[<br />main]o.s.b.w.embedded.<br />tomcat.TomcatwebServer: Tomcat started on port(s):8080 (http)<br />2017-10-29 22:26:41.840 INFO 13956 ---[<br />main]c.w.spring.<br />cloud.weather.Application:Started Application in 4.447 seconds<br />(JVM running for 4.788)<br />2017-10-29 22:26:41.919 INFO 13956---[eduler_Worker-1] S.c.w.s.eather<br />DatacollectionServiceImpl :End同步天氣<br />2017-10-29 22:26:41.920 INFO 13956---[eduler Worker-1] C.W.s.c.weather.<br />job.WeatherDataSyncJob:End 天氣數據同步任務(wù)
  由于我們只是在代碼里面“硬編碼”了一個(gè)城市ID為“101280601”的城市信息,所以,只有一條同步記錄。
  當然,我們也能通過(guò)Redis Desktop Manager,來(lái)方便查看存儲到Redis里面的數據,如圖7-3所示。
  
  本篇內容給大家講解的是天氣數據采集微服務(wù)的實(shí)現
  下篇文章給大家講解天氣數據API微服務(wù)的實(shí)現;
  覺(jué)得文章不錯的朋友可以轉發(fā)此文關(guān)注小編;
  感謝大家的支持??!
  本文就是愿天堂沒(méi)有BUG給大家分享的內容,大家有收獲的話(huà)可以分享下,想學(xué)習更多的話(huà)可以到微信公眾號里找我,我等你哦。

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 133 次瀏覽 ? 2022-05-21 18:02 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

再見(jiàn)笨重的ELK!這套輕量級日志收集方案要火!

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 78 次瀏覽 ? 2022-05-21 01:39 ? 來(lái)自相關(guān)話(huà)題

  再見(jiàn)笨重的ELK!這套輕量級日志收集方案要火!
  之前一直使用的日志收集方案是ELK,動(dòng)輒占用幾個(gè)G的內存,有些配置不好的服務(wù)器有點(diǎn)頂不??!最近發(fā)現一套輕量級日志收集方案:Loki+Promtail+Grafana(簡(jiǎn)稱(chēng)LPG), 幾百M內存就夠了,而且界面也挺不錯的,推薦給大家!
  簡(jiǎn)介
  LPG日志收集方案內存占用很少,經(jīng)濟且高效!它不像ELK日志系統那樣為日志建立索引,而是為每個(gè)日志流設置一組標簽。下面分別介紹下它的核心組件:
  
  日志收集流程圖安裝
  實(shí)現這套日志收集方案需要安裝Loki、Promtail、Grafana這些服務(wù),直接使用docker-compose來(lái)安裝非常方便。
  version:?"3"<br /><br />services:<br />??#?日志存儲和解析<br />??loki:<br />????image:?grafana/loki<br />????container_name:?lpg-loki<br />????volumes:<br />??????-?/mydata/loki/:/etc/loki/<br />????#?修改loki默認配置文件路徑<br />????command:?-config.file=/etc/loki/loki.yml<br />????ports:<br />??????-?3100:3100<br /><br />??#?日志收集器<br />??promtail:<br />????image:?grafana/promtail<br />????container_name:?lpg-promtail<br />????volumes:<br />??????#?將需要收集的日志所在目錄掛載到promtail容器中<br />??????-?/mydata/app/mall-tiny-loki/logs/:/var/log/<br />??????-?/mydata/promtail:/etc/promtail/<br />????#?修改promtail默認配置文件路徑<br />????command:?-config.file=/etc/promtail/promtail.yml<br /><br />??#?日志可視化<br />??grafana:<br />????image:?grafana/grafana<br />????container_name:?lpg-grafana<br />????ports:<br />??????-?3000:3000<br />
  auth_enabled:?false<br /><br />server:<br />??http_listen_port:?3100<br /><br />ingester:<br />??lifecycler:<br />????address:?127.0.0.1<br />????ring:<br />??????kvstore:<br />????????store:?inmemory<br />??????replication_factor:?1<br />????final_sleep:?0s<br />??chunk_idle_period:?1h???????#?Any?chunk?not?receiving?new?logs?in?this?time?will?be?flushed<br />??max_chunk_age:?1h???????????#?All?chunks?will?be?flushed?when?they?hit?this?age,?default?is?1h<br />??chunk_target_size:?1048576??#?Loki?will?attempt?to?build?chunks?up?to?1.5MB,?flushing?first?if?chunk_idle_period?or?max_chunk_age?is?reached?first<br />??chunk_retain_period:?30s????#?Must?be?greater?than?index?read?cache?TTL?if?using?an?index?cache?(Default?index?read?cache?TTL?is?5m)<br />??max_transfer_retries:?0?????#?Chunk?transfers?disabled<br /><br />schema_config:<br />??configs:<br />????-?from:?2020-10-24<br />??????store:?boltdb-shipper<br />??????object_store:?filesystem<br />??????schema:?v11<br />??????index:<br />????????prefix:?index_<br />????????period:?24h<br /><br />storage_config:<br />??boltdb_shipper:<br />????active_index_directory:?/loki/boltdb-shipper-active<br />????cache_location:?/loki/boltdb-shipper-cache<br />????cache_ttl:?24h?????????#?Can?be?increased?for?faster?performance?over?longer?query?periods,?uses?more?disk?space<br />????shared_store:?filesystem<br />??filesystem:<br />????directory:?/loki/chunks<br /><br />compactor:<br />??working_directory:?/loki/boltdb-shipper-compactor<br />??shared_store:?filesystem<br /><br />limits_config:<br />??reject_old_samples:?true<br />??reject_old_samples_max_age:?168h<br /><br />chunk_store_config:<br />??max_look_back_period:?0s<br /><br />table_manager:<br />??retention_deletes_enabled:?false<br />??retention_period:?0s<br /><br />ruler:<br />??storage:<br />????type:?local<br />????local:<br />??????directory:?/loki/rules<br />??rule_path:?/loki/rules-temp<br />??alertmanager_url:?http://localhost:9093<br />??ring:<br />????kvstore:<br />??????store:?inmemory<br />??enable_api:?true<br />
  server:<br />??http_listen_port:?9080<br />??grpc_listen_port:?0<br /><br />positions:<br />??filename:?/tmp/positions.yaml<br /><br />clients:<br />??-?url:?http://loki:3100/loki/api/v1/push<br /><br />scrape_configs:<br />-?job_name:?system<br />??static_configs:<br />??-?targets:<br />??????-?localhost<br />????labels:<br />??????job:?varlogs<br />??????__path__:?/var/log/*log<br />
  docker-compose?up?-d<br />
  [root@local-linux?lpg]#?docker?ps?|grep?lpg<br />64761b407423????????grafana/loki????????????????????????????"/usr/bin/loki?-conf…"???3?minutes?ago???????Up?3?minutes????????0.0.0.0:3100->3100/tcp???????????????????????????lpg-loki<br />67f0f0912971????????grafana/grafana?????????????????????????"/run.sh"????????????????3?minutes?ago???????Up?3?minutes????????0.0.0.0:3000->3000/tcp???????????????????????????lpg-grafana<br />f2d78eb188d1????????grafana/promtail????????????????????????"/usr/bin/promtail?-…"???3?minutes?ago???????Up?3?minutes?????????????????????????????????????????????????????????lpg-promtail<br />
  使用
  接下來(lái)我們將使用LPG日志收集系統來(lái)收集SpringBoot應用的日志,SpringBoot應用基本不用做特殊配置。
  spring:<br />??application:<br />????name:?mall-tiny-loki<br /><br />logging:<br />??path:?/var/logs<br />??level:<br />????com.macro.mall.tiny:?debug<br />
  docker?run?-p?8088:8088?--name?mall-tiny-loki?\<br />-v?/etc/localtime:/etc/localtime?\<br />-v?/mydata/app/mall-tiny-loki/logs:/var/logs?\<br />-e?TZ="Asia/Shanghai"?\<br />-d?mall-tiny/mall-tiny-loki:1.0-SNAPSHOT<br />
  
  
  
  
  總結
  本文主要介紹了LPG日志系統的搭建及使用它收集SpringBoot應用的日志,LPG日志收集方案確實(shí)非常輕量級,性能也不錯!不過(guò)如果你有對日志進(jìn)行全文搜索的需求的話(huà),還是得使用ELK系統。如果你對Grafana還不熟悉的話(huà),可以參考下這篇文章。
  參考資料項目源碼地址
  微信8.0將好友放開(kāi)到了一萬(wàn),小伙伴可以加我大號了,先到先得,再滿(mǎn)就真沒(méi)了
  掃描下方二維碼即可加我微信啦,2021,抱團取暖,一起牛逼。
   查看全部

  再見(jiàn)笨重的ELK!這套輕量級日志收集方案要火!
  之前一直使用的日志收集方案是ELK,動(dòng)輒占用幾個(gè)G的內存,有些配置不好的服務(wù)器有點(diǎn)頂不??!最近發(fā)現一套輕量級日志收集方案:Loki+Promtail+Grafana(簡(jiǎn)稱(chēng)LPG), 幾百M內存就夠了,而且界面也挺不錯的,推薦給大家!
  簡(jiǎn)介
  LPG日志收集方案內存占用很少,經(jīng)濟且高效!它不像ELK日志系統那樣為日志建立索引,而是為每個(gè)日志流設置一組標簽。下面分別介紹下它的核心組件:
  
  日志收集流程圖安裝
  實(shí)現這套日志收集方案需要安裝Loki、Promtail、Grafana這些服務(wù),直接使用docker-compose來(lái)安裝非常方便。
  version:?"3"<br /><br />services:<br />??#?日志存儲和解析<br />??loki:<br />????image:?grafana/loki<br />????container_name:?lpg-loki<br />????volumes:<br />??????-?/mydata/loki/:/etc/loki/<br />????#?修改loki默認配置文件路徑<br />????command:?-config.file=/etc/loki/loki.yml<br />????ports:<br />??????-?3100:3100<br /><br />??#?日志收集器<br />??promtail:<br />????image:?grafana/promtail<br />????container_name:?lpg-promtail<br />????volumes:<br />??????#?將需要收集的日志所在目錄掛載到promtail容器中<br />??????-?/mydata/app/mall-tiny-loki/logs/:/var/log/<br />??????-?/mydata/promtail:/etc/promtail/<br />????#?修改promtail默認配置文件路徑<br />????command:?-config.file=/etc/promtail/promtail.yml<br /><br />??#?日志可視化<br />??grafana:<br />????image:?grafana/grafana<br />????container_name:?lpg-grafana<br />????ports:<br />??????-?3000:3000<br />
  auth_enabled:?false<br /><br />server:<br />??http_listen_port:?3100<br /><br />ingester:<br />??lifecycler:<br />????address:?127.0.0.1<br />????ring:<br />??????kvstore:<br />????????store:?inmemory<br />??????replication_factor:?1<br />????final_sleep:?0s<br />??chunk_idle_period:?1h???????#?Any?chunk?not?receiving?new?logs?in?this?time?will?be?flushed<br />??max_chunk_age:?1h???????????#?All?chunks?will?be?flushed?when?they?hit?this?age,?default?is?1h<br />??chunk_target_size:?1048576??#?Loki?will?attempt?to?build?chunks?up?to?1.5MB,?flushing?first?if?chunk_idle_period?or?max_chunk_age?is?reached?first<br />??chunk_retain_period:?30s????#?Must?be?greater?than?index?read?cache?TTL?if?using?an?index?cache?(Default?index?read?cache?TTL?is?5m)<br />??max_transfer_retries:?0?????#?Chunk?transfers?disabled<br /><br />schema_config:<br />??configs:<br />????-?from:?2020-10-24<br />??????store:?boltdb-shipper<br />??????object_store:?filesystem<br />??????schema:?v11<br />??????index:<br />????????prefix:?index_<br />????????period:?24h<br /><br />storage_config:<br />??boltdb_shipper:<br />????active_index_directory:?/loki/boltdb-shipper-active<br />????cache_location:?/loki/boltdb-shipper-cache<br />????cache_ttl:?24h?????????#?Can?be?increased?for?faster?performance?over?longer?query?periods,?uses?more?disk?space<br />????shared_store:?filesystem<br />??filesystem:<br />????directory:?/loki/chunks<br /><br />compactor:<br />??working_directory:?/loki/boltdb-shipper-compactor<br />??shared_store:?filesystem<br /><br />limits_config:<br />??reject_old_samples:?true<br />??reject_old_samples_max_age:?168h<br /><br />chunk_store_config:<br />??max_look_back_period:?0s<br /><br />table_manager:<br />??retention_deletes_enabled:?false<br />??retention_period:?0s<br /><br />ruler:<br />??storage:<br />????type:?local<br />????local:<br />??????directory:?/loki/rules<br />??rule_path:?/loki/rules-temp<br />??alertmanager_url:?http://localhost:9093<br />??ring:<br />????kvstore:<br />??????store:?inmemory<br />??enable_api:?true<br />
  server:<br />??http_listen_port:?9080<br />??grpc_listen_port:?0<br /><br />positions:<br />??filename:?/tmp/positions.yaml<br /><br />clients:<br />??-?url:?http://loki:3100/loki/api/v1/push<br /><br />scrape_configs:<br />-?job_name:?system<br />??static_configs:<br />??-?targets:<br />??????-?localhost<br />????labels:<br />??????job:?varlogs<br />??????__path__:?/var/log/*log<br />
  docker-compose?up?-d<br />
  [root@local-linux?lpg]#?docker?ps?|grep?lpg<br />64761b407423????????grafana/loki????????????????????????????"/usr/bin/loki?-conf…"???3?minutes?ago???????Up?3?minutes????????0.0.0.0:3100->3100/tcp???????????????????????????lpg-loki<br />67f0f0912971????????grafana/grafana?????????????????????????"/run.sh"????????????????3?minutes?ago???????Up?3?minutes????????0.0.0.0:3000->3000/tcp???????????????????????????lpg-grafana<br />f2d78eb188d1????????grafana/promtail????????????????????????"/usr/bin/promtail?-…"???3?minutes?ago???????Up?3?minutes?????????????????????????????????????????????????????????lpg-promtail<br />
  使用
  接下來(lái)我們將使用LPG日志收集系統來(lái)收集SpringBoot應用的日志,SpringBoot應用基本不用做特殊配置。
  spring:<br />??application:<br />????name:?mall-tiny-loki<br /><br />logging:<br />??path:?/var/logs<br />??level:<br />????com.macro.mall.tiny:?debug<br />
  docker?run?-p?8088:8088?--name?mall-tiny-loki?\<br />-v?/etc/localtime:/etc/localtime?\<br />-v?/mydata/app/mall-tiny-loki/logs:/var/logs?\<br />-e?TZ="Asia/Shanghai"?\<br />-d?mall-tiny/mall-tiny-loki:1.0-SNAPSHOT<br />
  
  
  
  
  總結
  本文主要介紹了LPG日志系統的搭建及使用它收集SpringBoot應用的日志,LPG日志收集方案確實(shí)非常輕量級,性能也不錯!不過(guò)如果你有對日志進(jìn)行全文搜索的需求的話(huà),還是得使用ELK系統。如果你對Grafana還不熟悉的話(huà),可以參考下這篇文章。
  參考資料項目源碼地址
  微信8.0將好友放開(kāi)到了一萬(wàn),小伙伴可以加我大號了,先到先得,再滿(mǎn)就真沒(méi)了
  掃描下方二維碼即可加我微信啦,2021,抱團取暖,一起牛逼。
  

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 140 次瀏覽 ? 2022-05-21 01:37 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

數據治理之數字畫(huà)像

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 179 次瀏覽 ? 2022-07-10 02:00 ? 來(lái)自相關(guān)話(huà)題

  數據治理之數字畫(huà)像
  00引言
  隨著(zhù)全網(wǎng)步入大數據時(shí)代,企業(yè)的目光日益聚焦在利用大數據服務(wù)精細化營(yíng)銷(xiāo)、精細化運營(yíng)上,各類(lèi)客戶(hù)畫(huà)像、員工畫(huà)像理論如雨后春筍般興起,而數據應用的底層——數據治理,卻鮮有整體的理論體系。如何避免治理工作自身“無(wú)的放矢”,如何量化數據基礎建設的貢獻,我們需要為數據治理工作描繪一張“數字畫(huà)像”。這個(gè)命題的內涵外延非常豐富,在此我們選取用戶(hù)體驗、架構質(zhì)量?jì)蓚€(gè)角度進(jìn)行討論。
  01用戶(hù)體驗的數字畫(huà)像
  基于不同的感知角度,將用戶(hù)分為外部客戶(hù)、內部用戶(hù)、管理層、技術(shù)人員四類(lèi),針對特定的業(yè)務(wù)場(chǎng)景刻畫(huà)四類(lèi)使用者所體會(huì )到的“科技賦能”。
  1、外部客戶(hù)
  功能體驗指標:功能體驗指標用于衡量操作平臺的易用性及直觀(guān)程度??梢酝ㄟ^(guò)各類(lèi)埋點(diǎn),對用戶(hù)的點(diǎn)擊行為、頁(yè)面停留時(shí)間、頁(yè)面瀏覽深度進(jìn)行跟蹤。從而挖掘用戶(hù)常使用的功能,探究的實(shí)際需要,對于常用功能可以開(kāi)展功能的改版優(yōu)化,進(jìn)行同業(yè)產(chǎn)品比較、用戶(hù)反饋調查等,重點(diǎn)關(guān)注主要功能的流暢度、實(shí)用性。
  平臺服務(wù)指標:
 ?。?)服務(wù)平臺一般利用API接口向外提供數據,因此,通過(guò)計算API調用率可以計算出其向外輸出服務(wù)的活躍程度。
 ?。?)由數據服務(wù)帶來(lái)的產(chǎn)品升值也是需要衡量的一大重要指標。營(yíng)銷(xiāo)、運營(yíng)等商業(yè)活動(dòng)價(jià)值提升以一定的比例分配給其相應的數據治理工作,從業(yè)務(wù)部門(mén)有感的角度評估數據治理工作對營(yíng)銷(xiāo)等活動(dòng)的賦能。
  2、內部用戶(hù)
  
  便利性:過(guò)去業(yè)務(wù)部門(mén)向數據管理部門(mén)申請使用數據,通常使用郵件或行政流程的方式,這種方法無(wú)法實(shí)時(shí)跟蹤申請進(jìn)度,也無(wú)法在統一的頁(yè)面集中管理,導致相關(guān)工作人員在查詢(xún)與溝通上花費了大量精力。建立自動(dòng)化、規范化流程以及線(xiàn)上運營(yíng)工具,將極大地便利流程,因此,手工提取工單壓降比率可以作為度量?jì)炔坑脩?hù)程度的指標。
  時(shí)效性:線(xiàn)上化數據治理意味著(zhù)資產(chǎn)地圖、標準架構即存放在用戶(hù)指尖,關(guān)鍵作業(yè)的全鏈路交付時(shí)間是觸達內部用戶(hù)的另一直觀(guān)感受。該指標可以通過(guò)統計各節點(diǎn)的流轉時(shí)間,計算相應平均值獲得。
  貢獻度:不僅是底層的數據管理,數據的應用輸出同樣能夠為用戶(hù)帶來(lái)實(shí)際業(yè)務(wù)價(jià)值。BI工具的使用、模型提供數量等指標標志著(zhù)用戶(hù)對于應用類(lèi)數據成果的滿(mǎn)意度。
  3、管理層
  質(zhì)量提升:對于管理層而言,保障數據倉庫、數據湖的“清澈”是他們關(guān)心的問(wèn)題。由于監管報送結果是銀保監對銀行的重點(diǎn)考核指標,報送規定的達標率成為對于管理層數據治理成效最直觀(guān)的反映?;贒QC的一系列指標同樣可作為面向管理層的數據清潔度體現。
  效率提升:除監管要求之外,數據運營(yíng)成本對于全行管理也是至關(guān)重要的。只有建立規范和高效的數據架構,壓降數據報表,降低儲存、運維成本,才能實(shí)現精細化營(yíng)運,維持高效率盈利。
  4、技術(shù)人員
  數據字典評分:當企業(yè)實(shí)施開(kāi)發(fā)過(guò)程強管控時(shí),數據字典的角色可看作是法律之于社會(huì ),其整體邏輯必須經(jīng)得起反復推敲。在數據字典的查詢(xún)頁(yè)面設立評分反饋是一種簡(jiǎn)單但行之有效的方法。頁(yè)面上有計劃的引導,反映設計者關(guān)注開(kāi)發(fā)人員的使用體驗,從而讓“吐槽”變成建議,優(yōu)化和解決使用數據字典時(shí)遇到的問(wèn)題。
  02架構質(zhì)量的數字畫(huà)像
  全行統一的數據架構應在追求高效率的同時(shí)降低成本,根據《華為數據之道》中信息架構的經(jīng)典四范式,我們將從模型、分布、標準、資產(chǎn)四個(gè)角度對架構賦能能力進(jìn)行度量。
  1、模型
  公共層加工頻率:公共層中存放有事實(shí)數據、維表數據等等,它們支撐著(zhù)指標體系中的一級指標層。在建立指標時(shí),將規范化、集約化,提高公共指標的復用性,減少重復加工的操作,故公共層數據模型的復用率可作為公共層架構評估的指數之一。
  
  應用層引用頻率:類(lèi)似于人際關(guān)系網(wǎng)絡(luò )拓撲結構中的核心人物算法,該指數直接衡量應用層中數據的系統性重要程度,引導資產(chǎn)盤(pán)點(diǎn)的目標。數據血緣關(guān)系是一種有向的、無(wú)權值、無(wú)自環(huán)的網(wǎng)絡(luò )圖。被引用頻率高的資產(chǎn)一般來(lái)源于關(guān)鍵業(yè)務(wù)實(shí)體中最準確和最及時(shí)的業(yè)務(wù)記錄。這一些資產(chǎn)被跨部門(mén)、跨業(yè)務(wù)領(lǐng)域調用的概率最大,需要實(shí)現所有部門(mén)可訪(fǎng)問(wèn)并且訪(fǎng)問(wèn)到相同的數據。該指數還能夠有效地篩選出“孤兒表”、臨時(shí)表,減少資源投入和儲存成本。
  2、分布
  數據覆蓋:對于大型銀行而言,數以百計的系統,數以萬(wàn)計的庫表在全國范圍內分布式儲存。采集是資產(chǎn)盤(pán)點(diǎn)的第一步,測量采集數在全量系統的覆蓋率幫助我們明確當前采集的進(jìn)度,定位未采集的數據來(lái)源。
  數據冗余:數據冗余指同層數據的冗余,具體可分為兩個(gè)來(lái)源。第一,多個(gè)物理位置中存儲了相同意義的數據;第二,架構模型本身在設計上有較多的重復交叉項。
  數據容量:數據容量是對數據中臺的整體描述,它包括當前中臺所囊括的整體數據體量的絕對值,也包含該體量隨時(shí)間的增長(cháng)比例。數據容量并非越高或者越低更理想,它需要結合銀行的現狀辯證性地看待。
  3、標準
  標準穩定性:數據標準規范化了數據含義、結構等等,應當滿(mǎn)足內容統一、不交叉定義等條件,避免數據標準內部發(fā)生“數據打架”。
  標準落標率:在標準的技術(shù)規范完備,主題齊全,標準已權威發(fā)布的前提下,標準落標率反映了數據標準“最后一公里”的執行情況。借助自動(dòng)化工具,能夠計算出各類(lèi)分層、切片后的數據落標率,智能化地發(fā)現落標潛在問(wèn)題。
  4、資產(chǎn)
  技術(shù)元數據統計:技術(shù)元數據打通了源數據和,記錄了數據從產(chǎn)生到消亡的過(guò)程。我們從中挑選出系統覆蓋率、系統內表級覆蓋率、表名以及字段名的有效率、枚舉值的有效率等統計指標表示數據架構中技術(shù)類(lèi)資產(chǎn)的產(chǎn)出效益。
  企業(yè)活動(dòng)命中率:數據資產(chǎn)是從業(yè)務(wù)流程、業(yè)務(wù)模型中抽取出來(lái)的數字化描述。標簽資產(chǎn)對業(yè)務(wù)行為的命中率、指標資產(chǎn)對報表統計的命中率、報表資產(chǎn)的用戶(hù)訪(fǎng)問(wèn)量等數值越高,代表著(zhù)資產(chǎn)內容映射企業(yè)活動(dòng)的準確度越高。
  03結語(yǔ)
  伴隨著(zhù)企業(yè)數字化轉型不斷深入,“數據治理的數字畫(huà)像”從方法論到實(shí)踐都將趨于完善,內容價(jià)值、安全性能、用戶(hù)體驗也會(huì )隨之提高。如何動(dòng)態(tài)地衡量數據治理工作成效,建立適合自身企業(yè)的“北極星指標”,是每一家處于智慧轉型階段的公司所必須研究的,它的成功將創(chuàng )造出不可估量的商業(yè)價(jià)值。 查看全部

  數據治理之數字畫(huà)像
  00引言
  隨著(zhù)全網(wǎng)步入大數據時(shí)代,企業(yè)的目光日益聚焦在利用大數據服務(wù)精細化營(yíng)銷(xiāo)、精細化運營(yíng)上,各類(lèi)客戶(hù)畫(huà)像、員工畫(huà)像理論如雨后春筍般興起,而數據應用的底層——數據治理,卻鮮有整體的理論體系。如何避免治理工作自身“無(wú)的放矢”,如何量化數據基礎建設的貢獻,我們需要為數據治理工作描繪一張“數字畫(huà)像”。這個(gè)命題的內涵外延非常豐富,在此我們選取用戶(hù)體驗、架構質(zhì)量?jì)蓚€(gè)角度進(jìn)行討論。
  01用戶(hù)體驗的數字畫(huà)像
  基于不同的感知角度,將用戶(hù)分為外部客戶(hù)、內部用戶(hù)、管理層、技術(shù)人員四類(lèi),針對特定的業(yè)務(wù)場(chǎng)景刻畫(huà)四類(lèi)使用者所體會(huì )到的“科技賦能”。
  1、外部客戶(hù)
  功能體驗指標:功能體驗指標用于衡量操作平臺的易用性及直觀(guān)程度??梢酝ㄟ^(guò)各類(lèi)埋點(diǎn),對用戶(hù)的點(diǎn)擊行為、頁(yè)面停留時(shí)間、頁(yè)面瀏覽深度進(jìn)行跟蹤。從而挖掘用戶(hù)常使用的功能,探究的實(shí)際需要,對于常用功能可以開(kāi)展功能的改版優(yōu)化,進(jìn)行同業(yè)產(chǎn)品比較、用戶(hù)反饋調查等,重點(diǎn)關(guān)注主要功能的流暢度、實(shí)用性。
  平臺服務(wù)指標:
 ?。?)服務(wù)平臺一般利用API接口向外提供數據,因此,通過(guò)計算API調用率可以計算出其向外輸出服務(wù)的活躍程度。
 ?。?)由數據服務(wù)帶來(lái)的產(chǎn)品升值也是需要衡量的一大重要指標。營(yíng)銷(xiāo)、運營(yíng)等商業(yè)活動(dòng)價(jià)值提升以一定的比例分配給其相應的數據治理工作,從業(yè)務(wù)部門(mén)有感的角度評估數據治理工作對營(yíng)銷(xiāo)等活動(dòng)的賦能。
  2、內部用戶(hù)
  
  便利性:過(guò)去業(yè)務(wù)部門(mén)向數據管理部門(mén)申請使用數據,通常使用郵件或行政流程的方式,這種方法無(wú)法實(shí)時(shí)跟蹤申請進(jìn)度,也無(wú)法在統一的頁(yè)面集中管理,導致相關(guān)工作人員在查詢(xún)與溝通上花費了大量精力。建立自動(dòng)化、規范化流程以及線(xiàn)上運營(yíng)工具,將極大地便利流程,因此,手工提取工單壓降比率可以作為度量?jì)炔坑脩?hù)程度的指標。
  時(shí)效性:線(xiàn)上化數據治理意味著(zhù)資產(chǎn)地圖、標準架構即存放在用戶(hù)指尖,關(guān)鍵作業(yè)的全鏈路交付時(shí)間是觸達內部用戶(hù)的另一直觀(guān)感受。該指標可以通過(guò)統計各節點(diǎn)的流轉時(shí)間,計算相應平均值獲得。
  貢獻度:不僅是底層的數據管理,數據的應用輸出同樣能夠為用戶(hù)帶來(lái)實(shí)際業(yè)務(wù)價(jià)值。BI工具的使用、模型提供數量等指標標志著(zhù)用戶(hù)對于應用類(lèi)數據成果的滿(mǎn)意度。
  3、管理層
  質(zhì)量提升:對于管理層而言,保障數據倉庫、數據湖的“清澈”是他們關(guān)心的問(wèn)題。由于監管報送結果是銀保監對銀行的重點(diǎn)考核指標,報送規定的達標率成為對于管理層數據治理成效最直觀(guān)的反映?;贒QC的一系列指標同樣可作為面向管理層的數據清潔度體現。
  效率提升:除監管要求之外,數據運營(yíng)成本對于全行管理也是至關(guān)重要的。只有建立規范和高效的數據架構,壓降數據報表,降低儲存、運維成本,才能實(shí)現精細化營(yíng)運,維持高效率盈利。
  4、技術(shù)人員
  數據字典評分:當企業(yè)實(shí)施開(kāi)發(fā)過(guò)程強管控時(shí),數據字典的角色可看作是法律之于社會(huì ),其整體邏輯必須經(jīng)得起反復推敲。在數據字典的查詢(xún)頁(yè)面設立評分反饋是一種簡(jiǎn)單但行之有效的方法。頁(yè)面上有計劃的引導,反映設計者關(guān)注開(kāi)發(fā)人員的使用體驗,從而讓“吐槽”變成建議,優(yōu)化和解決使用數據字典時(shí)遇到的問(wèn)題。
  02架構質(zhì)量的數字畫(huà)像
  全行統一的數據架構應在追求高效率的同時(shí)降低成本,根據《華為數據之道》中信息架構的經(jīng)典四范式,我們將從模型、分布、標準、資產(chǎn)四個(gè)角度對架構賦能能力進(jìn)行度量。
  1、模型
  公共層加工頻率:公共層中存放有事實(shí)數據、維表數據等等,它們支撐著(zhù)指標體系中的一級指標層。在建立指標時(shí),將規范化、集約化,提高公共指標的復用性,減少重復加工的操作,故公共層數據模型的復用率可作為公共層架構評估的指數之一。
  
  應用層引用頻率:類(lèi)似于人際關(guān)系網(wǎng)絡(luò )拓撲結構中的核心人物算法,該指數直接衡量應用層中數據的系統性重要程度,引導資產(chǎn)盤(pán)點(diǎn)的目標。數據血緣關(guān)系是一種有向的、無(wú)權值、無(wú)自環(huán)的網(wǎng)絡(luò )圖。被引用頻率高的資產(chǎn)一般來(lái)源于關(guān)鍵業(yè)務(wù)實(shí)體中最準確和最及時(shí)的業(yè)務(wù)記錄。這一些資產(chǎn)被跨部門(mén)、跨業(yè)務(wù)領(lǐng)域調用的概率最大,需要實(shí)現所有部門(mén)可訪(fǎng)問(wèn)并且訪(fǎng)問(wèn)到相同的數據。該指數還能夠有效地篩選出“孤兒表”、臨時(shí)表,減少資源投入和儲存成本。
  2、分布
  數據覆蓋:對于大型銀行而言,數以百計的系統,數以萬(wàn)計的庫表在全國范圍內分布式儲存。采集是資產(chǎn)盤(pán)點(diǎn)的第一步,測量采集數在全量系統的覆蓋率幫助我們明確當前采集的進(jìn)度,定位未采集的數據來(lái)源。
  數據冗余:數據冗余指同層數據的冗余,具體可分為兩個(gè)來(lái)源。第一,多個(gè)物理位置中存儲了相同意義的數據;第二,架構模型本身在設計上有較多的重復交叉項。
  數據容量:數據容量是對數據中臺的整體描述,它包括當前中臺所囊括的整體數據體量的絕對值,也包含該體量隨時(shí)間的增長(cháng)比例。數據容量并非越高或者越低更理想,它需要結合銀行的現狀辯證性地看待。
  3、標準
  標準穩定性:數據標準規范化了數據含義、結構等等,應當滿(mǎn)足內容統一、不交叉定義等條件,避免數據標準內部發(fā)生“數據打架”。
  標準落標率:在標準的技術(shù)規范完備,主題齊全,標準已權威發(fā)布的前提下,標準落標率反映了數據標準“最后一公里”的執行情況。借助自動(dòng)化工具,能夠計算出各類(lèi)分層、切片后的數據落標率,智能化地發(fā)現落標潛在問(wèn)題。
  4、資產(chǎn)
  技術(shù)元數據統計:技術(shù)元數據打通了源數據和,記錄了數據從產(chǎn)生到消亡的過(guò)程。我們從中挑選出系統覆蓋率、系統內表級覆蓋率、表名以及字段名的有效率、枚舉值的有效率等統計指標表示數據架構中技術(shù)類(lèi)資產(chǎn)的產(chǎn)出效益。
  企業(yè)活動(dòng)命中率:數據資產(chǎn)是從業(yè)務(wù)流程、業(yè)務(wù)模型中抽取出來(lái)的數字化描述。標簽資產(chǎn)對業(yè)務(wù)行為的命中率、指標資產(chǎn)對報表統計的命中率、報表資產(chǎn)的用戶(hù)訪(fǎng)問(wèn)量等數值越高,代表著(zhù)資產(chǎn)內容映射企業(yè)活動(dòng)的準確度越高。
  03結語(yǔ)
  伴隨著(zhù)企業(yè)數字化轉型不斷深入,“數據治理的數字畫(huà)像”從方法論到實(shí)踐都將趨于完善,內容價(jià)值、安全性能、用戶(hù)體驗也會(huì )隨之提高。如何動(dòng)態(tài)地衡量數據治理工作成效,建立適合自身企業(yè)的“北極星指標”,是每一家處于智慧轉型階段的公司所必須研究的,它的成功將創(chuàng )造出不可估量的商業(yè)價(jià)值。

文章采集api Zabbix 任性,文末送書(shū) X 5

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 125 次瀏覽 ? 2022-07-02 05:59 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Zabbix 任性,文末送書(shū) X 5
  Zabbix版本不斷升級,以滿(mǎn)足日益增長(cháng)的用戶(hù)需求,支持高可用HA,k8s、指標topN、機器學(xué)習、定制前端品牌logo等!
  舊版本需要腳本才實(shí)現的功能,升級至最新版本可輕松解決!Zabbix6.0為業(yè)務(wù)服務(wù)提供商、DevOps和ITOps團隊提供了附加值,優(yōu)化了整體監控工作流程,并在許多不同層面提供了新見(jiàn)解。
  目錄
  業(yè)務(wù)服務(wù)監控達到全新高度
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  開(kāi)箱即用的Zabbix server高可用群集
  機器學(xué)習
  Kubernetes監控
  詳細高效的Zabbix審計日志模式
  可視化數據的新方法
  Zabbix性能優(yōu)化
  提升Zabbix Agent2模塊化,新的Zabbix Agent 監控項和功能
  原生TLS/SSL網(wǎng)站證書(shū)監控
  通用性改進(jìn)
  通過(guò)自定義密碼復雜程度要求來(lái)保護您的Zabbix登錄
  支持定制前端展示品牌logo
  新增模板和集成
  其它新功能和優(yōu)化
  01
  BMS業(yè)務(wù)服務(wù)監控達到全新高度
  優(yōu)化Services部分,顯示業(yè)務(wù)服務(wù)的狀態(tài)和當前SLA級別
  通過(guò)對現有Services頁(yè)面和功能的重大改進(jìn)和優(yōu)化,業(yè)務(wù)服務(wù)監控提升到了一個(gè)新高度。業(yè)務(wù)服務(wù)監控功能(BSM)非常適合多組件服務(wù)場(chǎng)景,例如服務(wù)器群集、負載平衡器和其它具有冗余組件的服務(wù)。
  Zabbix 6.0提供多種功能自定義業(yè)務(wù)服務(wù)樹(shù)實(shí)現BMS業(yè)務(wù)服務(wù)監控:
  ? 重新設計 Zabbix 6.0 Services頁(yè)面和功能
  ? 支持單個(gè)Zabbix實(shí)例監控超過(guò)10萬(wàn)個(gè)業(yè)務(wù)服務(wù)
  ? 支持新的靈活服務(wù)狀態(tài)計算邏輯
  ? 能夠自定義業(yè)務(wù)服務(wù)的訪(fǎng)問(wèn)權限
  ? 能夠為特定業(yè)務(wù)服務(wù)自定義只讀和讀寫(xiě)權限
  ? 業(yè)務(wù)服務(wù)權限既可以基于顯式服務(wù)列表,也可以基于服務(wù)標簽的訪(fǎng)問(wèn)限制
  ? 導出和導入業(yè)務(wù)服務(wù)樹(shù)
  ? 新的Service動(dòng)作類(lèi)型能讓用戶(hù)接收告警并對業(yè)務(wù)服務(wù)狀態(tài)更改作出反應
  02
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  提供大量可供選擇的服務(wù)狀態(tài)計算規則,能支持靈活的服務(wù)定義
  業(yè)務(wù)服務(wù)狀態(tài)計算邏輯在Zabbix 6.0中得到了極大擴展,增加了許多新功能,例如:
  ? 能夠為每項業(yè)務(wù)服務(wù)分配權重
  ? 僅當N個(gè)子服務(wù)都處于X嚴重級別的問(wèn)題狀態(tài)時(shí)才更改狀態(tài)
  ? 對處于問(wèn)題狀態(tài)下的子服務(wù)的權重進(jìn)行分析并作出反應
  ? 僅當特定百分比的子服務(wù)處于問(wèn)題狀態(tài)時(shí)才作出反應
  ? 其它計算規則
  用戶(hù)還可以自定義和訪(fǎng)問(wèn)指定服務(wù)的SLA報告。
  03
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  根因問(wèn)題會(huì )立即顯示在service下
  對業(yè)務(wù)服務(wù)執行根因分析。利用根因分析功能找出可能導致業(yè)務(wù)服務(wù)SLA下降的潛在問(wèn)題列表:
  ? 在Zabbix前端Services頁(yè)面查看根因問(wèn)題列表
  ? 接收告警中的根因問(wèn)題列表
  ? 通過(guò)Zabbix API收集根因問(wèn)題信息
  04
  開(kāi)箱即用的Zabbix server高可用群集
  在系統信息組件中跟蹤集群集節點(diǎn)狀態(tài)
  Zabbix server高可用防止硬件故障或計劃維護期的停機:
  ? 原生選擇加入HA群集配置
  ? 定義一個(gè)或多個(gè)備用節點(diǎn)
  
  ?實(shí)時(shí)監控Zabbix server群集節點(diǎn)的狀態(tài)
  ? 不需要外部工具即可將Zabbix server配置為HA群集模式
  05
  機器學(xué)習
  使用新函數對意外異常率或與指標基準的偏差做出反應
  新的基線(xiàn)監控和異常檢測趨勢功能以動(dòng)態(tài)方式檢測問(wèn)題,而不是靜態(tài)閾值方式:
  ?新的趨勢函數-baselinewma and baselinedev ,能計算指標基線(xiàn)和偏離值
  ?新的趨勢函數-trendstl,能檢測異常指標行為
  ?能夠指定異常檢測偏差算法及季節性
  06
  Kubernetes監控
  Zabbix 6.0 LTS添加了多個(gè)新模板,用于監控不同的Kubernetes組件
  Zabbix 6.0 LTS新增Kubernetes監控功能,可以在Kubernetes系統從多個(gè)維度采集指標:
  ?Kubernetes節點(diǎn)和pods的自動(dòng)發(fā)現和監控
  ?無(wú)代理方式采集Kubernetes pods和節點(diǎn)的信息
  ?獲取Kubernetes節點(diǎn)主機高水平信息
  Kubernetes監控還能夠監控Kubernetes組件,例如
  ?kube-controller-manager
  ?kube-proxy
  ?kube-apiserver
  ?kube-scheduler
  ?kubelet
  07
  詳細高效的Zabbix審計日志模式
  重新設計的審計日志能提供全新的詳細信息,并優(yōu)化篩選功能。
  新的審計日志模式允許用戶(hù)對Zabbix前端、Zabbix API和Zabbix server記錄執行詳細審計。通過(guò)修改審計日志,對Zabbix實(shí)例執行的所有更改都將記錄在審計日志中:
  ?創(chuàng )建、修改或刪除新對象
  ?通過(guò)LLD發(fā)現新實(shí)體
  ?API命令
  ?定期登錄/退出
  ?Zabbix實(shí)例中發(fā)生的所有其它事情
  新的審計日志模式在設計時(shí)考慮了最佳性能,因此擴展的功能不會(huì )影響Zabbix實(shí)例的性能。審計日志模式的工作是一項持續的工作,會(huì )在后續Zabbix發(fā)布周期中持續進(jìn)行。
  08
  可視化數據的新方法
  主機排序組件可顯示按監控項值排序的前N個(gè)或后N個(gè)主機的列表
  Zabbix 6.0新增的構件提供了展示信息的許多新方法。
  ?地理地圖構件能在地圖上顯示主機和問(wèn)題
  ?數據表構件能創(chuàng )建有關(guān)主機指標狀態(tài)的摘要視圖
  ?數據表構件的前N和后N函數能展示最高或最低的監控項值
  ?單一監控項構件能展示單個(gè)指標的值
  ?對現有矢量圖的許多改進(jìn),例如新的矢量圖類(lèi)型、引用單一監控項等
  ?SLA構件能顯示特定業(yè)務(wù)服務(wù)的當前SLA
  09
  Zabbix性能優(yōu)化
  針對不同的Zabbix組件進(jìn)行多項性能優(yōu)化:
  ?提升鏈接模板時(shí)的性能
  ?提升Zabbix proxy性能和內存使用率
  歷史數據表使用主鍵,這有多種好處,例如:
  ?提高Zabbix server和Zabbix前端的性能
  ?減少歷史數據表的大小
  10
  提升Zabbix Agent2模塊化,
  新的Zabbix Agent 監控項和功能
  優(yōu)化的Zabbix agent現在能夠開(kāi)箱即用監控一組指標
  Zabbix 6.0為Zabbix Agent和Agent2提供了一套新的監控項。支持以下功能:
  ?獲取額外文件信息,如文件所有者和文件權限
  ?采集agent主機元數據作為指標
  
  ?計數匹配的TCP/UDP sockets
  某些已有的監控項支持新的功能:
  ?vfs.fs.discovery-在Windows上添加了對{#FSLABEL}宏的支持
  ?vfs.fs.get-在Windows上添加了對{#FSLABEL}宏的支持
  ? vfs.file.size-添加了一個(gè)新的模式參數。設置以字節數或行數為單位
  Zabbix Agent2現在支持加載獨立插件,而無(wú)需重新編譯Agent2。
  11
  原生TLS/SSL網(wǎng)站證書(shū)監控
  使用新的Zabbix agent2 監控項監控SSL/TLS證書(shū)
  支持使用新的Zabbix agent 2監控項來(lái)監控SSL/TLS證書(shū)。監控項可用于驗證TLS/SSL證書(shū),并提供其它證書(shū)詳細信息。
  12
  通用性改進(jìn)
  通過(guò)優(yōu)化的創(chuàng )建主機UI,使創(chuàng )建新主機從未如此簡(jiǎn)單
  Zabbix 6.0使Zabbix配置工作流程更精簡(jiǎn)!Zabbix用戶(hù)現在可直接在Monitoring頁(yè)面創(chuàng )建主機和監控項:
  ?直接從Monitoring -Hosts頁(yè)面創(chuàng )建主機
  ?直接從Monitoring -Latest data頁(yè)面創(chuàng )建監控項
  ?刪除了Monitoring -Overview頁(yè)面。為了改善用戶(hù)體驗,現在只能通過(guò)儀表盤(pán)構件訪(fǎng)問(wèn)觸發(fā)器和數據概覽功能。
  現在將根據監控項的鍵值自動(dòng)選擇監控項的默認信息類(lèi)型。
  拓撲圖標簽和圖形名稱(chēng)中的簡(jiǎn)單宏已替換為表達式宏,以確保與新的觸發(fā)器表達式語(yǔ)法一致。
  13
  通過(guò)自定義密碼復雜程度要求
  來(lái)保護您的Zabbix登錄
  設置密碼復雜程度確保前端登錄安全
  Zabbix超級管理員現在能夠定義密碼復雜程度要求?,F在可以:
  ?設置最小密碼長(cháng)度
  ?定義密碼字符要求
  ?通過(guò)禁止使用最常見(jiàn)的密碼字符串來(lái)降低字典攻擊的風(fēng)險。
  14
  支持定制前端展示品牌logo
  定制Zabbix實(shí)例代表您的公司。將現有的Zabbix品牌和幫助頁(yè)面URL替換為您自己的公司品牌和自定義網(wǎng)站URL。
  改名功能不會(huì )違反Zabbix許可協(xié)議-可以自由更換Zabbix品牌!
  15
  新增模板和集成
  Zabbix 6.0為最受歡迎的供應商提供了許多新模板:
  ?f5 BIG-IP
  ?Cisco ASAv
  ?HPE ProLiant servers
  ?Cloudflare
  ?InfluxDB
  ?Travis CI
  ?Dell PowerEdge
  Zabbix 6.0還帶來(lái)了一個(gè)新的Github webhook集成,能基于Zabbix問(wèn)題或恢復事件生成Github問(wèn)題!
  所有官方的Zabbix模板現在都是獨立的,不需要依賴(lài)導入其他模板。
  請查看當前可用集成的完整列表。
  16
  其它新功能和優(yōu)化
  更多改進(jìn)功能(部分):
  ?使用新聚合函數計數返回值或匹配監控項的數量-count和item_count函數
  ?在未配置交換空間的情況下提升system.swap監控項行為
  ?使用新的單調歷史函數檢測連續增加或減少的值
  ?支持兩個(gè)新的Prometheus預處理標簽匹配運算符!= 及 !~
  ?當從構件鏈接導航到列表樣式頁(yè)面時(shí),構件顯示能更可靠地轉換為不同的篩選器選項
  ?使用新配置參數ListenBacklog為Zabbix server、Zabbix proxy、Zabbix agent配置TCP隊列中掛起連接的最大數量
  ?文檔頁(yè)面字體和可讀性的改進(jìn)
  ?調整許多現有模板和修復小bug
  ?新增utf8mb4作為受支持的MySQL字符集和校對集
  ?新增對Webhook的額外HTTP方法的支持
  ?對Zabbix命令行工具的超時(shí)設置
  Zabbix官方首本工具書(shū)《Zabbix監控系統之深度解析和實(shí)踐》現已出版,歡迎閱讀。 查看全部

  文章采集api Zabbix 任性,文末送書(shū) X 5
  Zabbix版本不斷升級,以滿(mǎn)足日益增長(cháng)的用戶(hù)需求,支持高可用HA,k8s、指標topN、機器學(xué)習、定制前端品牌logo等!
  舊版本需要腳本才實(shí)現的功能,升級至最新版本可輕松解決!Zabbix6.0為業(yè)務(wù)服務(wù)提供商、DevOps和ITOps團隊提供了附加值,優(yōu)化了整體監控工作流程,并在許多不同層面提供了新見(jiàn)解。
  目錄
  業(yè)務(wù)服務(wù)監控達到全新高度
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  開(kāi)箱即用的Zabbix server高可用群集
  機器學(xué)習
  Kubernetes監控
  詳細高效的Zabbix審計日志模式
  可視化數據的新方法
  Zabbix性能優(yōu)化
  提升Zabbix Agent2模塊化,新的Zabbix Agent 監控項和功能
  原生TLS/SSL網(wǎng)站證書(shū)監控
  通用性改進(jìn)
  通過(guò)自定義密碼復雜程度要求來(lái)保護您的Zabbix登錄
  支持定制前端展示品牌logo
  新增模板和集成
  其它新功能和優(yōu)化
  01
  BMS業(yè)務(wù)服務(wù)監控達到全新高度
  優(yōu)化Services部分,顯示業(yè)務(wù)服務(wù)的狀態(tài)和當前SLA級別
  通過(guò)對現有Services頁(yè)面和功能的重大改進(jìn)和優(yōu)化,業(yè)務(wù)服務(wù)監控提升到了一個(gè)新高度。業(yè)務(wù)服務(wù)監控功能(BSM)非常適合多組件服務(wù)場(chǎng)景,例如服務(wù)器群集、負載平衡器和其它具有冗余組件的服務(wù)。
  Zabbix 6.0提供多種功能自定義業(yè)務(wù)服務(wù)樹(shù)實(shí)現BMS業(yè)務(wù)服務(wù)監控:
  ? 重新設計 Zabbix 6.0 Services頁(yè)面和功能
  ? 支持單個(gè)Zabbix實(shí)例監控超過(guò)10萬(wàn)個(gè)業(yè)務(wù)服務(wù)
  ? 支持新的靈活服務(wù)狀態(tài)計算邏輯
  ? 能夠自定義業(yè)務(wù)服務(wù)的訪(fǎng)問(wèn)權限
  ? 能夠為特定業(yè)務(wù)服務(wù)自定義只讀和讀寫(xiě)權限
  ? 業(yè)務(wù)服務(wù)權限既可以基于顯式服務(wù)列表,也可以基于服務(wù)標簽的訪(fǎng)問(wèn)限制
  ? 導出和導入業(yè)務(wù)服務(wù)樹(shù)
  ? 新的Service動(dòng)作類(lèi)型能讓用戶(hù)接收告警并對業(yè)務(wù)服務(wù)狀態(tài)更改作出反應
  02
  高階業(yè)務(wù)服務(wù)SLA計算邏輯
  提供大量可供選擇的服務(wù)狀態(tài)計算規則,能支持靈活的服務(wù)定義
  業(yè)務(wù)服務(wù)狀態(tài)計算邏輯在Zabbix 6.0中得到了極大擴展,增加了許多新功能,例如:
  ? 能夠為每項業(yè)務(wù)服務(wù)分配權重
  ? 僅當N個(gè)子服務(wù)都處于X嚴重級別的問(wèn)題狀態(tài)時(shí)才更改狀態(tài)
  ? 對處于問(wèn)題狀態(tài)下的子服務(wù)的權重進(jìn)行分析并作出反應
  ? 僅當特定百分比的子服務(wù)處于問(wèn)題狀態(tài)時(shí)才作出反應
  ? 其它計算規則
  用戶(hù)還可以自定義和訪(fǎng)問(wèn)指定服務(wù)的SLA報告。
  03
  通過(guò)根因分析增強業(yè)務(wù)服務(wù)監控能力
  根因問(wèn)題會(huì )立即顯示在service下
  對業(yè)務(wù)服務(wù)執行根因分析。利用根因分析功能找出可能導致業(yè)務(wù)服務(wù)SLA下降的潛在問(wèn)題列表:
  ? 在Zabbix前端Services頁(yè)面查看根因問(wèn)題列表
  ? 接收告警中的根因問(wèn)題列表
  ? 通過(guò)Zabbix API收集根因問(wèn)題信息
  04
  開(kāi)箱即用的Zabbix server高可用群集
  在系統信息組件中跟蹤集群集節點(diǎn)狀態(tài)
  Zabbix server高可用防止硬件故障或計劃維護期的停機:
  ? 原生選擇加入HA群集配置
  ? 定義一個(gè)或多個(gè)備用節點(diǎn)
  
  ?實(shí)時(shí)監控Zabbix server群集節點(diǎn)的狀態(tài)
  ? 不需要外部工具即可將Zabbix server配置為HA群集模式
  05
  機器學(xué)習
  使用新函數對意外異常率或與指標基準的偏差做出反應
  新的基線(xiàn)監控和異常檢測趨勢功能以動(dòng)態(tài)方式檢測問(wèn)題,而不是靜態(tài)閾值方式:
  ?新的趨勢函數-baselinewma and baselinedev ,能計算指標基線(xiàn)和偏離值
  ?新的趨勢函數-trendstl,能檢測異常指標行為
  ?能夠指定異常檢測偏差算法及季節性
  06
  Kubernetes監控
  Zabbix 6.0 LTS添加了多個(gè)新模板,用于監控不同的Kubernetes組件
  Zabbix 6.0 LTS新增Kubernetes監控功能,可以在Kubernetes系統從多個(gè)維度采集指標:
  ?Kubernetes節點(diǎn)和pods的自動(dòng)發(fā)現和監控
  ?無(wú)代理方式采集Kubernetes pods和節點(diǎn)的信息
  ?獲取Kubernetes節點(diǎn)主機高水平信息
  Kubernetes監控還能夠監控Kubernetes組件,例如
  ?kube-controller-manager
  ?kube-proxy
  ?kube-apiserver
  ?kube-scheduler
  ?kubelet
  07
  詳細高效的Zabbix審計日志模式
  重新設計的審計日志能提供全新的詳細信息,并優(yōu)化篩選功能。
  新的審計日志模式允許用戶(hù)對Zabbix前端、Zabbix API和Zabbix server記錄執行詳細審計。通過(guò)修改審計日志,對Zabbix實(shí)例執行的所有更改都將記錄在審計日志中:
  ?創(chuàng )建、修改或刪除新對象
  ?通過(guò)LLD發(fā)現新實(shí)體
  ?API命令
  ?定期登錄/退出
  ?Zabbix實(shí)例中發(fā)生的所有其它事情
  新的審計日志模式在設計時(shí)考慮了最佳性能,因此擴展的功能不會(huì )影響Zabbix實(shí)例的性能。審計日志模式的工作是一項持續的工作,會(huì )在后續Zabbix發(fā)布周期中持續進(jìn)行。
  08
  可視化數據的新方法
  主機排序組件可顯示按監控項值排序的前N個(gè)或后N個(gè)主機的列表
  Zabbix 6.0新增的構件提供了展示信息的許多新方法。
  ?地理地圖構件能在地圖上顯示主機和問(wèn)題
  ?數據表構件能創(chuàng )建有關(guān)主機指標狀態(tài)的摘要視圖
  ?數據表構件的前N和后N函數能展示最高或最低的監控項值
  ?單一監控項構件能展示單個(gè)指標的值
  ?對現有矢量圖的許多改進(jìn),例如新的矢量圖類(lèi)型、引用單一監控項等
  ?SLA構件能顯示特定業(yè)務(wù)服務(wù)的當前SLA
  09
  Zabbix性能優(yōu)化
  針對不同的Zabbix組件進(jìn)行多項性能優(yōu)化:
  ?提升鏈接模板時(shí)的性能
  ?提升Zabbix proxy性能和內存使用率
  歷史數據表使用主鍵,這有多種好處,例如:
  ?提高Zabbix server和Zabbix前端的性能
  ?減少歷史數據表的大小
  10
  提升Zabbix Agent2模塊化,
  新的Zabbix Agent 監控項和功能
  優(yōu)化的Zabbix agent現在能夠開(kāi)箱即用監控一組指標
  Zabbix 6.0為Zabbix Agent和Agent2提供了一套新的監控項。支持以下功能:
  ?獲取額外文件信息,如文件所有者和文件權限
  ?采集agent主機元數據作為指標
  
  ?計數匹配的TCP/UDP sockets
  某些已有的監控項支持新的功能:
  ?vfs.fs.discovery-在Windows上添加了對{#FSLABEL}宏的支持
  ?vfs.fs.get-在Windows上添加了對{#FSLABEL}宏的支持
  ? vfs.file.size-添加了一個(gè)新的模式參數。設置以字節數或行數為單位
  Zabbix Agent2現在支持加載獨立插件,而無(wú)需重新編譯Agent2。
  11
  原生TLS/SSL網(wǎng)站證書(shū)監控
  使用新的Zabbix agent2 監控項監控SSL/TLS證書(shū)
  支持使用新的Zabbix agent 2監控項來(lái)監控SSL/TLS證書(shū)。監控項可用于驗證TLS/SSL證書(shū),并提供其它證書(shū)詳細信息。
  12
  通用性改進(jìn)
  通過(guò)優(yōu)化的創(chuàng )建主機UI,使創(chuàng )建新主機從未如此簡(jiǎn)單
  Zabbix 6.0使Zabbix配置工作流程更精簡(jiǎn)!Zabbix用戶(hù)現在可直接在Monitoring頁(yè)面創(chuàng )建主機和監控項:
  ?直接從Monitoring -Hosts頁(yè)面創(chuàng )建主機
  ?直接從Monitoring -Latest data頁(yè)面創(chuàng )建監控項
  ?刪除了Monitoring -Overview頁(yè)面。為了改善用戶(hù)體驗,現在只能通過(guò)儀表盤(pán)構件訪(fǎng)問(wèn)觸發(fā)器和數據概覽功能。
  現在將根據監控項的鍵值自動(dòng)選擇監控項的默認信息類(lèi)型。
  拓撲圖標簽和圖形名稱(chēng)中的簡(jiǎn)單宏已替換為表達式宏,以確保與新的觸發(fā)器表達式語(yǔ)法一致。
  13
  通過(guò)自定義密碼復雜程度要求
  來(lái)保護您的Zabbix登錄
  設置密碼復雜程度確保前端登錄安全
  Zabbix超級管理員現在能夠定義密碼復雜程度要求?,F在可以:
  ?設置最小密碼長(cháng)度
  ?定義密碼字符要求
  ?通過(guò)禁止使用最常見(jiàn)的密碼字符串來(lái)降低字典攻擊的風(fēng)險。
  14
  支持定制前端展示品牌logo
  定制Zabbix實(shí)例代表您的公司。將現有的Zabbix品牌和幫助頁(yè)面URL替換為您自己的公司品牌和自定義網(wǎng)站URL。
  改名功能不會(huì )違反Zabbix許可協(xié)議-可以自由更換Zabbix品牌!
  15
  新增模板和集成
  Zabbix 6.0為最受歡迎的供應商提供了許多新模板:
  ?f5 BIG-IP
  ?Cisco ASAv
  ?HPE ProLiant servers
  ?Cloudflare
  ?InfluxDB
  ?Travis CI
  ?Dell PowerEdge
  Zabbix 6.0還帶來(lái)了一個(gè)新的Github webhook集成,能基于Zabbix問(wèn)題或恢復事件生成Github問(wèn)題!
  所有官方的Zabbix模板現在都是獨立的,不需要依賴(lài)導入其他模板。
  請查看當前可用集成的完整列表。
  16
  其它新功能和優(yōu)化
  更多改進(jìn)功能(部分):
  ?使用新聚合函數計數返回值或匹配監控項的數量-count和item_count函數
  ?在未配置交換空間的情況下提升system.swap監控項行為
  ?使用新的單調歷史函數檢測連續增加或減少的值
  ?支持兩個(gè)新的Prometheus預處理標簽匹配運算符!= 及 !~
  ?當從構件鏈接導航到列表樣式頁(yè)面時(shí),構件顯示能更可靠地轉換為不同的篩選器選項
  ?使用新配置參數ListenBacklog為Zabbix server、Zabbix proxy、Zabbix agent配置TCP隊列中掛起連接的最大數量
  ?文檔頁(yè)面字體和可讀性的改進(jìn)
  ?調整許多現有模板和修復小bug
  ?新增utf8mb4作為受支持的MySQL字符集和校對集
  ?新增對Webhook的額外HTTP方法的支持
  ?對Zabbix命令行工具的超時(shí)設置
  Zabbix官方首本工具書(shū)《Zabbix監控系統之深度解析和實(shí)踐》現已出版,歡迎閱讀。

騰訊3面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 67 次瀏覽 ? 2022-06-28 05:55 ? 來(lái)自相關(guān)話(huà)題

  騰訊3面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在 騰訊三輪面試 的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計 ;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的 整體架構 和 可以做的亮點(diǎn)優(yōu)化 ;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀; 傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò) 削峰限流 和 數據加工 后,將原始日志存儲于 ES 中,再經(jīng)過(guò) 數據清洗 、數據聚合 后,將 issue(聚合的數據) 持久化存儲 于 MySQL ,最后提供 RESTful API 提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是 內核+插件 的插件式設計;每個(gè) SDK 首先繼承于平臺無(wú)關(guān)的 Core 層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè) web環(huán)境 下的數據,包括 Nodejs、微信小程序、Electron 等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè) SDK 可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護 ;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改 build 的腳本,對 packages 文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到 npm 平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK 也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件 的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加 Monitor 插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br />class?WebSdk?extends?Core?{<br />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br />??public?performanceInstance:?WebVitals;<br /><br />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br />??public?userInstance:?UserVitals;<br /><br />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br />??public?errorInstance:?ErrorVitals;<br /><br />??//?上報實(shí)例,這里面封裝上報方法<br />??public?transportInstance:?TransportInstance;<br /><br />??//?數據格式化實(shí)例<br />??public?builderInstance:?BuilderInstance;<br /><br />??//?維度實(shí)例,用以初始化?uid、sid等信息<br />??public?dimensionInstance:?DimensionInstance;<br /><br />??//?參數初始化實(shí)例<br />??public?configInstance:?ConfigInstance;<br /><br />??private?options:?initOptions;<br /><br />??constructor(options:?initOptions)?{<br />????super();<br />????this.configInstance?=?new?ConfigInstance(this,?options);<br />????//?各種初始化......<br />??}<br />}<br /><br />export?default?WebSdk;<br />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將 this 傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的 內核+插件 設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有 單元測試 的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對 每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè) SDK 都用 try catch 包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的 catch 里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò) JS 調用 new Date() 獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō) API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭 上有一個(gè)字段 Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的 Date 值后計算 Diff差值 存在本地;
  這樣子就可以提供一個(gè) 公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間 比較趨近于 服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br />export?const?diffTime?=?(date:?string)?=>?{<br />??const?serverDate?=?new?Date(date);<br />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br />??if?(diff?===?0?||?diff?>?inDiff)?{<br />????diff?=?inDiff;<br />??}<br />};<br /><br />export?const?getTime?=?()?=>?{<br />??return?new?Date(Date.now()?-?diff);<br />};<br />
  當然,這里還可以做的更精確一點(diǎn),我們可以讓后端服務(wù)在返回的時(shí)候,帶上 API 請求在后端服務(wù)執行完畢所消耗的時(shí)間 server-timing,放在響應頭里;我們取到數據后,將 ttfb 耗時(shí) 減去返回的 server-timing 再除以 2;就是單程傳輸的耗時(shí);那這樣我們上文的計算中差的 單程傳輸耗時(shí)的誤差 就可以補上了;
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成 錯誤mid 的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br />export?const?getErrorUid?=?(input:?string)?=>?{<br />??return?window.btoa(unescape(encodeURIComponent(input)));<br />};<br />
  
  所以說(shuō)我們傳入的參數,是 錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的 錯誤mid 都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br />??const?submitParams?=?{<br />????...data,<br />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br />??}?as?ExceptionMetrics;<br />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br />??if?(hasSubmitStatus)?return;<br />??this.submitErrorUids.push(submitParams.errorUid);<br />??//?記錄后清除?breadcrumbs<br />??this.engineInstance.userInstance.breadcrumbs.clear();<br />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br />??this.engineInstance.transportInstance.kernelTransportHandler(<br />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br />??);<br />};<br />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先 Beacon API[6] 是一個(gè)較新的 API
  然后 Ajax 請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是 Ajax;
  最后來(lái)說(shuō)一下 Image 上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用 sendBeacon + xmlHttpRequest 降級上報的方式,當瀏覽器不支持 sendBeacon 或者 傳輸的數據量超過(guò)了 sendBeacon 的限制,我們就降級采用 xmlHttpRequest 進(jìn)行上報數據;
  優(yōu)先選用 Beacon API 的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載 的傳輸方式。
  而降級使用 XMLHttpRequest 的原因是, Beacon API 現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且 sendbeacon 不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到 Ajax 來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用 Image 呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br />??//?PV訪(fǎng)問(wèn)數據<br />??PV?=?'pv',<br />??//?性能數據<br />??PERF?=?'perf',<br />??//?api?請求數據<br />??API?=?'api',<br />??//?報錯數據<br />??ERROR?=?'error',<br />??//?自定義行為<br />??CUS?=?'custom',<br />}<br /><br />export?interface?DimensionStructure?{<br />??//?用戶(hù)id,存儲于cookie<br />??uid:?string;<br />??//?會(huì )話(huà)id,存儲于cookiestorage<br />??sid:?string;<br />??//?應用id,使用方傳入<br />??pid:?string;<br />??//?應用版本號<br />??release:?string;<br />??//?應用環(huán)境<br />??environment:?string;<br />}<br /><br />export?interface?TransportStructure?{<br />??//?上報類(lèi)別<br />??category:?transportCategory;<br />??//?上報的維度信息<br />??dimension:?DimensionStructure;<br />??//?上報對象(正文)<br />??context?:?Object;<br />??//?上報對象數組<br />??contexts?:?Array;<br />??//?捕獲的sdk版本信息,版本號等...<br />??sdk:?Object;<br />}<br /><br />export?default?class?TransportInstance?{<br />??private?engineInstance:?EngineInstance;<br /><br />??public?kernelTransportHandler:?Function;<br /><br />??private?options:?TransportParams;<br /><br />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br />????this.engineInstance?=?engineInstance;<br />????this.options?=?options;<br />????this.kernelTransportHandler?=?this.initTransportHandler();<br />??}<br /><br />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br />????const?transportStructure?=?{<br />??????category,<br />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br />??????sdk:?getSdkVersion(),<br />????}?as?TransportStructure;<br />????if?(data?instanceof?Array)?{<br />??????transportStructure.contexts?=?data;<br />????}?else?{<br />??????transportStructure.context?=?data;<br />????}<br />????return?transportStructure;<br />??};<br /><br />??//?初始化上報方法<br />??initTransportHandler?=?()?=>?{<br />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br />??};<br /><br />??//?beacon?形式上報<br />??beaconTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br />??????if?(!status)?this.xmlTransport().apply(this,?data);<br />????};<br />????return?handler;<br />??};<br /><br />??//?XMLHttpRequest?形式上報<br />??xmlTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br />??????xhr.open('POST',?this.options.transportUrl,?true);<br />??????xhr.send(JSON.stringify(data));<br />????};<br />????return?handler;<br />??};<br />}<br />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是 Math.random() ,我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random() 查看全部

  騰訊3面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在 騰訊三輪面試 的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計 ;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的 整體架構 和 可以做的亮點(diǎn)優(yōu)化 ;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀; 傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò) 削峰限流 和 數據加工 后,將原始日志存儲于 ES 中,再經(jīng)過(guò) 數據清洗 、數據聚合 后,將 issue(聚合的數據) 持久化存儲 于 MySQL ,最后提供 RESTful API 提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是 內核+插件 的插件式設計;每個(gè) SDK 首先繼承于平臺無(wú)關(guān)的 Core 層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè) web環(huán)境 下的數據,包括 Nodejs、微信小程序、Electron 等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè) SDK 可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護 ;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改 build 的腳本,對 packages 文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到 npm 平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK 也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件 的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加 Monitor 插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br />class?WebSdk?extends?Core?{<br />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br />??public?performanceInstance:?WebVitals;<br /><br />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br />??public?userInstance:?UserVitals;<br /><br />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br />??public?errorInstance:?ErrorVitals;<br /><br />??//?上報實(shí)例,這里面封裝上報方法<br />??public?transportInstance:?TransportInstance;<br /><br />??//?數據格式化實(shí)例<br />??public?builderInstance:?BuilderInstance;<br /><br />??//?維度實(shí)例,用以初始化?uid、sid等信息<br />??public?dimensionInstance:?DimensionInstance;<br /><br />??//?參數初始化實(shí)例<br />??public?configInstance:?ConfigInstance;<br /><br />??private?options:?initOptions;<br /><br />??constructor(options:?initOptions)?{<br />????super();<br />????this.configInstance?=?new?ConfigInstance(this,?options);<br />????//?各種初始化......<br />??}<br />}<br /><br />export?default?WebSdk;<br />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將 this 傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的 內核+插件 設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有 單元測試 的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對 每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè) SDK 都用 try catch 包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的 catch 里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò) JS 調用 new Date() 獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō) API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭 上有一個(gè)字段 Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的 Date 值后計算 Diff差值 存在本地;
  這樣子就可以提供一個(gè) 公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間 比較趨近于 服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br />export?const?diffTime?=?(date:?string)?=>?{<br />??const?serverDate?=?new?Date(date);<br />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br />??if?(diff?===?0?||?diff?>?inDiff)?{<br />????diff?=?inDiff;<br />??}<br />};<br /><br />export?const?getTime?=?()?=>?{<br />??return?new?Date(Date.now()?-?diff);<br />};<br />
  當然,這里還可以做的更精確一點(diǎn),我們可以讓后端服務(wù)在返回的時(shí)候,帶上 API 請求在后端服務(wù)執行完畢所消耗的時(shí)間 server-timing,放在響應頭里;我們取到數據后,將 ttfb 耗時(shí) 減去返回的 server-timing 再除以 2;就是單程傳輸的耗時(shí);那這樣我們上文的計算中差的 單程傳輸耗時(shí)的誤差 就可以補上了;
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成 錯誤mid 的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br />export?const?getErrorUid?=?(input:?string)?=>?{<br />??return?window.btoa(unescape(encodeURIComponent(input)));<br />};<br />
  
  所以說(shuō)我們傳入的參數,是 錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的 錯誤mid 都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br />??const?submitParams?=?{<br />????...data,<br />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br />??}?as?ExceptionMetrics;<br />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br />??if?(hasSubmitStatus)?return;<br />??this.submitErrorUids.push(submitParams.errorUid);<br />??//?記錄后清除?breadcrumbs<br />??this.engineInstance.userInstance.breadcrumbs.clear();<br />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br />??this.engineInstance.transportInstance.kernelTransportHandler(<br />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br />??);<br />};<br />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先 Beacon API[6] 是一個(gè)較新的 API
  然后 Ajax 請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是 Ajax;
  最后來(lái)說(shuō)一下 Image 上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用 sendBeacon + xmlHttpRequest 降級上報的方式,當瀏覽器不支持 sendBeacon 或者 傳輸的數據量超過(guò)了 sendBeacon 的限制,我們就降級采用 xmlHttpRequest 進(jìn)行上報數據;
  優(yōu)先選用 Beacon API 的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載 的傳輸方式。
  而降級使用 XMLHttpRequest 的原因是, Beacon API 現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且 sendbeacon 不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到 Ajax 來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用 Image 呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br />??//?PV訪(fǎng)問(wèn)數據<br />??PV?=?'pv',<br />??//?性能數據<br />??PERF?=?'perf',<br />??//?api?請求數據<br />??API?=?'api',<br />??//?報錯數據<br />??ERROR?=?'error',<br />??//?自定義行為<br />??CUS?=?'custom',<br />}<br /><br />export?interface?DimensionStructure?{<br />??//?用戶(hù)id,存儲于cookie<br />??uid:?string;<br />??//?會(huì )話(huà)id,存儲于cookiestorage<br />??sid:?string;<br />??//?應用id,使用方傳入<br />??pid:?string;<br />??//?應用版本號<br />??release:?string;<br />??//?應用環(huán)境<br />??environment:?string;<br />}<br /><br />export?interface?TransportStructure?{<br />??//?上報類(lèi)別<br />??category:?transportCategory;<br />??//?上報的維度信息<br />??dimension:?DimensionStructure;<br />??//?上報對象(正文)<br />??context?:?Object;<br />??//?上報對象數組<br />??contexts?:?Array;<br />??//?捕獲的sdk版本信息,版本號等...<br />??sdk:?Object;<br />}<br /><br />export?default?class?TransportInstance?{<br />??private?engineInstance:?EngineInstance;<br /><br />??public?kernelTransportHandler:?Function;<br /><br />??private?options:?TransportParams;<br /><br />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br />????this.engineInstance?=?engineInstance;<br />????this.options?=?options;<br />????this.kernelTransportHandler?=?this.initTransportHandler();<br />??}<br /><br />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br />????const?transportStructure?=?{<br />??????category,<br />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br />??????sdk:?getSdkVersion(),<br />????}?as?TransportStructure;<br />????if?(data?instanceof?Array)?{<br />??????transportStructure.contexts?=?data;<br />????}?else?{<br />??????transportStructure.context?=?data;<br />????}<br />????return?transportStructure;<br />??};<br /><br />??//?初始化上報方法<br />??initTransportHandler?=?()?=>?{<br />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br />??};<br /><br />??//?beacon?形式上報<br />??beaconTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br />??????if?(!status)?this.xmlTransport().apply(this,?data);<br />????};<br />????return?handler;<br />??};<br /><br />??//?XMLHttpRequest?形式上報<br />??xmlTransport?=?():?Function?=>?{<br />????const?handler?=?(data:?TransportStructure)?=>?{<br />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br />??????xhr.open('POST',?this.options.transportUrl,?true);<br />??????xhr.send(JSON.stringify(data));<br />????};<br />????return?handler;<br />??};<br />}<br />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是 Math.random() ,我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random()

騰訊三面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 104 次瀏覽 ? 2022-06-27 21:05 ? 來(lái)自相關(guān)話(huà)題

  騰訊三面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在騰訊三輪面試的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的整體架構和可以做的亮點(diǎn)優(yōu)化;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀;傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò)削峰限流和數據加工后,將原始日志存儲于ES中,再經(jīng)過(guò)數據清洗、數據聚合后,將issue(聚合的數據)持久化存儲于MySQL,最后提供RESTful API提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是內核+插件的插件式設計;每個(gè)SDK首先繼承于平臺無(wú)關(guān)的Core層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè)web環(huán)境下的數據,包括Nodejs、微信小程序、Electron等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè)SDK可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改build的腳本,對packages文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到npm平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加Monitor插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />class?WebSdk?extends?Core?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?performanceInstance:?WebVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?userInstance:?UserVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?errorInstance:?ErrorVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報實(shí)例,這里面封裝上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?transportInstance:?TransportInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?數據格式化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?builderInstance:?BuilderInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?維度實(shí)例,用以初始化?uid、sid等信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?dimensionInstance:?DimensionInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?參數初始化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?configInstance:?ConfigInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?initOptions;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(options:?initOptions)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????super();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.configInstance?=?new?ConfigInstance(this,?options);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//?各種初始化......<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?WebSdk;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將this傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的內核+插件設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有單元測試的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè)SDK都用try catch包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的catch里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò)JS調用new Date()獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō)API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭上有一個(gè)字段Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的Date值后計算Diff差值存在本地;
  這樣子就可以提供一個(gè)公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間比較趨近于服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?diffTime?=?(date:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?serverDate?=?new?Date(date);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(diff?===?0?||?diff?>?inDiff)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????diff?=?inDiff;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getTime?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?new?Date(Date.now()?-?diff);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成錯誤mid的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getErrorUid?=?(input:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?window.btoa(unescape(encodeURIComponent(input)));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  所以說(shuō)我們傳入的參數,是錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的錯誤mid都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?submitParams?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????...data,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}?as?ExceptionMetrics;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(hasSubmitStatus)?return;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.submitErrorUids.push(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?記錄后清除?breadcrumbs<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.userInstance.breadcrumbs.clear();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.transportInstance.kernelTransportHandler(<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先Beacon API[6]是一個(gè)較新的 API
  然后Ajax請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是Ajax;
  最后來(lái)說(shuō)一下Image上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用sendBeacon+xmlHttpRequest降級上報的方式,當瀏覽器不支持sendBeacon或者傳輸的數據量超過(guò)了sendBeacon的限制,我們就降級采用xmlHttpRequest進(jìn)行上報數據;
  優(yōu)先選用Beacon API的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載的傳輸方式。
  而降級使用XMLHttpRequest的原因是,Beacon API現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且sendbeacon不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到Ajax來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用Image呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?PV訪(fǎng)問(wèn)數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PV?=?'pv',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PERF?=?'perf',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?api?請求數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??API?=?'api',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?報錯數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??ERROR?=?'error',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?自定義行為<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??CUS?=?'custom',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?DimensionStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?用戶(hù)id,存儲于cookie<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??uid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?會(huì )話(huà)id,存儲于cookiestorage<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用id,使用方傳入<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??pid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用版本號<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??release:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用環(huán)境<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??environment:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?TransportStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報類(lèi)別<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??category:?transportCategory;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報的維度信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??dimension:?DimensionStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象(正文)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??context?:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象數組<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??contexts?:?Array;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?捕獲的sdk版本信息,版本號等...<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sdk:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?class?TransportInstance?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?engineInstance:?EngineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?kernelTransportHandler:?Function;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?TransportParams;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance?=?engineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.options?=?options;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.kernelTransportHandler?=?this.initTransportHandler();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?transportStructure?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????category,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????sdk:?getSdkVersion(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?as?TransportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????if?(data?instanceof?Array)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.contexts?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?else?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.context?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?transportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?初始化上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??initTransportHandler?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?beacon?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??beaconTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????if?(!status)?this.xmlTransport().apply(this,?data);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?XMLHttpRequest?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??xmlTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.open('POST',?this.options.transportUrl,?true);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.send(JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是Math.random(),我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random() 查看全部

  騰訊三面:說(shuō)說(shuō)前端監控平臺/監控SDK的架構設計和難點(diǎn)亮點(diǎn)?
  前言
  事情是這樣的,上周,我的一位兩年前端經(jīng)驗的發(fā)小,在騰訊三輪面試的時(shí)候被問(wèn)了一個(gè)問(wèn)題:說(shuō)說(shuō)你們公司前端監控項目的架構設計和亮點(diǎn)設計;
  而說(shuō)回我這位發(fā)小,因為做過(guò)他們公司監控項目的可視化報表界面,所以簡(jiǎn)歷上有寫(xiě)著(zhù)前端監控項目的項目經(jīng)驗;但是不幸的是,他雖然前端基礎相當不錯,但并沒(méi)有實(shí)際參與監控SDK的設計開(kāi)發(fā)(只負責寫(xiě)監控的可視化分析界面),所以被問(wèn)到這個(gè)問(wèn)題,直接就一個(gè)懵了;結果也很正常,面試沒(méi)過(guò);
  那么這篇文章,我就來(lái)介紹一下對于前端監控項目的整體架構和可以做的亮點(diǎn)優(yōu)化;前文幾篇文章有介紹具體的前端監控實(shí)現,感興趣的小伙伴可以點(diǎn)擊鏈接跳轉過(guò)去閱讀;傳送門(mén)就在下面。
  傳送門(mén)
  這篇文章的標題原擬定是:一文摸清前端監控實(shí)踐要點(diǎn)(四)架構設計;但是我的發(fā)小面試剛好碰上了這么一個(gè)問(wèn)題,于是我便將標題改為了這個(gè)。
  一文摸清前端監控實(shí)踐要點(diǎn)(一)性能監控[1]
  一文摸清前端監控實(shí)踐要點(diǎn)(二)行為監控[2]
  一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控[3]
  騰訊三面:說(shuō)說(shuō)前端監控告警分析平臺的架構設計和難點(diǎn)亮點(diǎn)?[4]
  整體 架構設計
  image.png
  直接上圖,我們在應用層SDK上報的數據,在接入層經(jīng)過(guò)削峰限流和數據加工后,將原始日志存儲于ES中,再經(jīng)過(guò)數據清洗、數據聚合后,將issue(聚合的數據)持久化存儲于MySQL,最后提供RESTful API提供給監控平臺調用;
  SDK 架構設計
  為支持多平臺、可拓展、可插拔的特點(diǎn),整體SDK的架構設計是內核+插件的插件式設計;每個(gè)SDK首先繼承于平臺無(wú)關(guān)的Core層代碼。然后在自身SDK中,初始化內核實(shí)例和插件;
  image.png
  image.png值得一談的點(diǎn)
  下面將主要談?wù)勥@些內容:前端監控項目除了正常的數據采集、數據報表分析以外;會(huì )碰上哪些難點(diǎn)可以去突破,或者說(shuō)可以做出哪些亮點(diǎn)的內容?
  SDK 如何設計成多平臺支持?
  首先我們先來(lái)了解一下,在前端監控的領(lǐng)域里,我們可能不僅僅只是監控一個(gè)web環(huán)境下的數據,包括Nodejs、微信小程序、Electron等各種其余的環(huán)境都是有監控的業(yè)務(wù)需求在的;
  那么我們就要思考一個(gè)點(diǎn),我們的一個(gè) SDK 項目,既然功能全,又要支持多平臺,那么怎么設計這個(gè)SDK可以讓它既支持多平臺,但是在啟用某個(gè)平臺的時(shí)候不會(huì )引入無(wú)用的代碼呢?
  最簡(jiǎn)單的辦法:將每個(gè)平臺單獨放一個(gè)倉庫,單獨維護;但是這種辦法的問(wèn)題也很?chē)乐兀喝肆Y源浪費嚴重;會(huì )導致一些重復的代碼很多;維護非常困難;
  而較好一點(diǎn)的解決方案:我們可以通過(guò)插件化對代碼進(jìn)行組織:見(jiàn)下圖
  image.png
  這樣子進(jìn)行 SDK 的設計有很多好處:
  最后打包上線(xiàn)時(shí),我們通過(guò)修改build的腳本,對packages文件夾下的每個(gè)平臺都單獨打一個(gè)包,并且分開(kāi)上傳到npm平臺;
  SDK 如何方便的進(jìn)行業(yè)務(wù)拓展和定制?
  業(yè)務(wù)功能總是會(huì )不斷迭代的,SDK也一樣,所以說(shuō)我們在設計SDK的時(shí)候就要考慮它的一個(gè)拓展性;我們來(lái)看下圖:
  image.png
  上圖是 SDK 內部的一個(gè)架構設計 :內核+插件的設計;
  而看了上圖已經(jīng)上文的解釋?zhuān)赏卣惯@個(gè)問(wèn)題的答案已經(jīng)很清晰了,我們需要拓展業(yè)務(wù),只需要在內核的基礎上,不斷的往上疊加Monitor插件的數量就可以了;
  至于說(shuō)定制化,插件里的功能,都是使用與否不影響整個(gè)SDK運行的,所以我們可以自由的讓用戶(hù)對插件里的功能進(jìn)行定制化,決定哪個(gè)監控功能啟用、哪個(gè)監控功能不啟用等等....
  我這邊舉個(gè)代碼例子,大家可以參考著(zhù)看看就行:
  //?服務(wù)于?Web?的SDK,繼承了?Core?上的與平臺無(wú)關(guān)方法;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />class?WebSdk?extends?Core?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)性能監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?performanceInstance:?WebVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?行為監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)行為監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?userInstance:?UserVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?錯誤監控實(shí)例,實(shí)例里每個(gè)插件實(shí)現一個(gè)錯誤監控功能;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?errorInstance:?ErrorVitals;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報實(shí)例,這里面封裝上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?transportInstance:?TransportInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?數據格式化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?builderInstance:?BuilderInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?維度實(shí)例,用以初始化?uid、sid等信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?dimensionInstance:?DimensionInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?參數初始化實(shí)例<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?configInstance:?ConfigInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?initOptions;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(options:?initOptions)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????super();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.configInstance?=?new?ConfigInstance(this,?options);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????//?各種初始化......<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?WebSdk;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  看上面的代碼,我在初始化每個(gè)插件的時(shí)候,都將this傳入進(jìn)去,那么每個(gè)插件里面都可以訪(fǎng)問(wèn)內核里的方法;
  SDK 在拓展新業(yè)務(wù)的時(shí)候,如何保證原有業(yè)務(wù)的正確性?
  在上述的內核+插件設計下,我們開(kāi)發(fā)新業(yè)務(wù)對原功能的影響基本上可以忽略不計,但是難免有意外,所以在 SDK 項目的層面上,需要有單元測試的來(lái)保證業(yè)務(wù)的穩定性;
  我們可以引入單元測試,并對每一個(gè)插件,每一個(gè)內核方法,都單獨編寫(xiě)測試用例,在覆蓋率達標的情況下,只要每次代碼上傳都測試通過(guò),就可以保證原有業(yè)務(wù)的一個(gè)穩定性;
  SDK 如何實(shí)現異常隔離以及上報?
  首先,我們引入監控系統的原因之一就是為了避免頁(yè)面產(chǎn)生錯誤,而如果因為監控SDK報錯,導致整個(gè)應用主業(yè)務(wù)流程被中斷,這是我們不能夠接收的;
  實(shí)際上,我們無(wú)法保證我們的 SDK 不出現錯誤,那么假如萬(wàn)一SDK本身報錯了,我們就需要它不會(huì )去影響主業(yè)務(wù)流程的運行;最簡(jiǎn)單粗暴的方法就是把整個(gè)SDK都用try catch包裹起來(lái),那么這樣子即使出現了錯誤,也會(huì )被攔截在我們的catch里面;
  但是我們回過(guò)頭來(lái)想一想,這樣簡(jiǎn)單粗暴的包裹,會(huì )帶來(lái)哪些問(wèn)題:
  那么,我們就需要一個(gè)相對優(yōu)雅的一個(gè)異常隔離+上報機制,回想我們上文的架構:內核+插件的形式;我們對每一個(gè)插件模塊,都單獨的用trycatch包裹起來(lái),然后當拋出錯誤的時(shí)候,進(jìn)行數據的封裝、上報;
  這樣子,就完成了一個(gè)異常隔離機制:
  SDK 如何實(shí)現服務(wù)端時(shí)間的校對?
  看到這里,可能有的同學(xué)并不明白,進(jìn)行服務(wù)端時(shí)間的校對是什么意思;我們首先要明白,我們通過(guò)JS調用new Date()獲取的時(shí)間,是我們的機器時(shí)間;也就是說(shuō):這個(gè)時(shí)間是一個(gè)隨時(shí)都有可能不準確的時(shí)間;
  那么既然時(shí)間是不準確的,假如有一個(gè)對時(shí)間精準度要求比較敏感的功能:比如說(shuō)API全鏈路監控;最后整體繪制出來(lái)的全鏈路圖直接客戶(hù)端的訪(fǎng)問(wèn)時(shí)間點(diǎn)變成了未來(lái)的時(shí)間點(diǎn),直接時(shí)間穿梭那可不行;
  image.png
  如上圖,我們先要了解的是,http響應頭上有一個(gè)字段Date;它的值是服務(wù)端發(fā)送資源時(shí)的服務(wù)器時(shí)間,我們可以在初始化SDK的時(shí)候,發(fā)送一個(gè)簡(jiǎn)單的請求給上報服務(wù)器,獲取返回的Date值后計算Diff差值存在本地;
  這樣子就可以提供一個(gè)公共API,來(lái)提供一個(gè)時(shí)間校對的服務(wù),讓本地的時(shí)間比較趨近于服務(wù)端的真實(shí)時(shí)間;(只是比較趨近的原因是:還會(huì )有一個(gè)單程傳輸耗時(shí)的誤差)
  let?diff?=?0;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?diffTime?=?(date:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?serverDate?=?new?Date(date);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?inDiff?=?Date.now()?-?serverDate.getTime();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(diff?===?0?||?diff?>?inDiff)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????diff?=?inDiff;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getTime?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?new?Date(Date.now()?-?diff);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 如何實(shí)現會(huì )話(huà)級別的錯誤上報去重?
  首先,我們需要理清一個(gè)概念,我們可以認為:
  為什么有上面的結論呢?理由很簡(jiǎn)單:
  所以說(shuō)我們在第三篇文章《一文摸清前端監控實(shí)踐要點(diǎn)(三)錯誤監控》[5]中有一個(gè)生成錯誤mid的操作,這是一個(gè)唯一id,但是它的唯一規則是針對于不同錯誤的唯一;
  //?對每一個(gè)錯誤詳情,生成一串編碼<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?const?getErrorUid?=?(input:?string)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??return?window.btoa(unescape(encodeURIComponent(input)));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  所以說(shuō)我們傳入的參數,是錯誤信息、錯誤行號、錯誤列號、錯誤文件等可能的關(guān)鍵信息的一個(gè)集合,這樣保證了產(chǎn)生在同一個(gè)地方的錯誤,生成的錯誤mid都是相等的;這樣子,我們才能在錯誤上報的入口函數里,做上報去重;
  //?封裝錯誤的上報入口,上報前,判斷錯誤是否已經(jīng)發(fā)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />errorSendHandler?=?(data:?ExceptionMetrics)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?統一加上?用戶(hù)行為追蹤?和?頁(yè)面基本信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?submitParams?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????...data,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????breadcrumbs:?this.engineInstance.userInstance.breadcrumbs.get(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????pageInformation:?this.engineInstance.userInstance.metrics.get('page-information'),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}?as?ExceptionMetrics;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?判斷同一個(gè)錯誤在本次頁(yè)面訪(fǎng)問(wèn)中是否已經(jīng)發(fā)生過(guò);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??const?hasSubmitStatus?=?this.submitErrorUids.includes(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?檢查一下錯誤在本次頁(yè)面訪(fǎng)問(wèn)中,是否已經(jīng)產(chǎn)生過(guò)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??if?(hasSubmitStatus)?return;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.submitErrorUids.push(submitParams.errorUid);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?記錄后清除?breadcrumbs<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.userInstance.breadcrumbs.clear();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?一般來(lái)說(shuō),有報錯就立刻上報;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??this.engineInstance.transportInstance.kernelTransportHandler(<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance.transportInstance.formatTransportData(transportCategory.ERROR,?submitParams),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  SDK 采用什么樣的上報策略?
  對于上報方面來(lái)說(shuō),SDK的數據上報可不是隨隨便便就上報上去了,里面有涉及到數據上報的方式取舍以及上報時(shí)機的選擇等等,還有一些可以讓數據上報更加優(yōu)雅的優(yōu)化點(diǎn);
  首先,日志上報并不是應用的主要功能邏輯,日志上報行為不應該影響業(yè)務(wù)邏輯,不應該占用業(yè)務(wù)計算資源;那么在往下閱讀之前,我們先來(lái)了解一下目前通用的幾個(gè)上報方式:
  我們來(lái)簡(jiǎn)單講一下上述的幾個(gè)上報方式
  首先Beacon API[6]是一個(gè)較新的 API
  然后Ajax請求方式就不用我多說(shuō)了,大家應該平常用的最多的異步請求就是Ajax;
  最后來(lái)說(shuō)一下Image上報方式:我們可以以向服務(wù)端請求圖片資源的形式,像服務(wù)端傳輸少量數據,這種方式不會(huì )造成跨域;
  上報方式
  看了上面的三種上報方式,我們最終采用sendBeacon+xmlHttpRequest降級上報的方式,當瀏覽器不支持sendBeacon或者傳輸的數據量超過(guò)了sendBeacon的限制,我們就降級采用xmlHttpRequest進(jìn)行上報數據;
  優(yōu)先選用Beacon API的理由上文已經(jīng)有提到:它可以保證頁(yè)面卸載之前啟動(dòng)信標請求,是一種數據可靠,傳輸異步并且不會(huì )影響下一頁(yè)面的加載的傳輸方式。
  而降級使用XMLHttpRequest的原因是,Beacon API現在并不是所有的瀏覽器都完全支持,我們需要一個(gè)保險方案兜底,并且sendbeacon不能傳輸大數據量的信息,這個(gè)時(shí)候還是得回到Ajax來(lái);
  看到了這里,有的同學(xué)可能會(huì )問(wèn):為什么不用Image呀?那跨域怎么辦呀?原因也很簡(jiǎn)單:
  我們將其簡(jiǎn)單封裝一下:
  export?enum?transportCategory?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?PV訪(fǎng)問(wèn)數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PV?=?'pv',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?性能數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??PERF?=?'perf',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?api?請求數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??API?=?'api',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?報錯數據<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??ERROR?=?'error',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?自定義行為<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??CUS?=?'custom',<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?DimensionStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?用戶(hù)id,存儲于cookie<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??uid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?會(huì )話(huà)id,存儲于cookiestorage<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用id,使用方傳入<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??pid:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用版本號<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??release:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?應用環(huán)境<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??environment:?string;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?interface?TransportStructure?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報類(lèi)別<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??category:?transportCategory;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報的維度信息<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??dimension:?DimensionStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象(正文)<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??context?:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?上報對象數組<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??contexts?:?Array;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?捕獲的sdk版本信息,版本號等...<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??sdk:?Object;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />export?default?class?TransportInstance?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?engineInstance:?EngineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??public?kernelTransportHandler:?Function;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??private?options:?TransportParams;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??constructor(engineInstance:?EngineInstance,?options:?TransportParams)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.engineInstance?=?engineInstance;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.options?=?options;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????this.kernelTransportHandler?=?this.initTransportHandler();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?格式化數據,傳入部分為?category?和?context?\?contexts<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??formatTransportData?=?(category:?transportCategory,?data:?Object?|?Array):?TransportStructure?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?transportStructure?=?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????category,<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????dimension:?this.engineInstance.dimensionInstance.getDimension(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????sdk:?getSdkVersion(),<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?as?TransportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????if?(data?instanceof?Array)?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.contexts?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}?else?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????transportStructure.context?=?data;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?transportStructure;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?初始化上報方法<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??initTransportHandler?=?()?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?typeof?navigator.sendBeacon?===?'function'???this.beaconTransport()?:?this.xmlTransport();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?beacon?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??beaconTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?status?=?window.navigator.sendBeacon(this.options.transportUrl,?JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????//?如果數據量過(guò)大,則本次大數據量用?XMLHttpRequest?上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????if?(!status)?this.xmlTransport().apply(this,?data);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??//?XMLHttpRequest?形式上報<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??xmlTransport?=?():?Function?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????const?handler?=?(data:?TransportStructure)?=>?{<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????const?xhr?=?new?(window?as?any).oXMLHttpRequest();<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.open('POST',?this.options.transportUrl,?true);<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??????xhr.send(JSON.stringify(data));<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />????return?handler;<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />??};<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />}<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  上報時(shí)機
  上報時(shí)機這里,一般來(lái)說(shuō):
  上報優(yōu)化
  或許,我們想把我們的數據上報做的再優(yōu)雅一點(diǎn),那么我們還有什么可以?xún)?yōu)化的點(diǎn)呢?還是有的:
  平臺數據如何進(jìn)行 削峰限流?
  假設說(shuō),有某一個(gè)時(shí)間點(diǎn),突然間流量爆炸,無(wú)數的數據向服務(wù)器訪(fǎng)問(wèn)過(guò)來(lái),這時(shí)如果沒(méi)有一個(gè)削峰限流的策略,很可能會(huì )導致機器Down掉,
  所以說(shuō)我們有必要去做一個(gè)削峰限流,從概率學(xué)的角度上講,在大數據量的基礎上我們對于整體數據做一個(gè)百分比的截斷,并不會(huì )影響整體的一個(gè)數據比例。
  簡(jiǎn)單方案-隨機丟棄策略進(jìn)行限流
  前端做削峰限流最簡(jiǎn)單的方法是什么?沒(méi)錯,就是Math.random(),我們讓用戶(hù)傳入一個(gè)采樣率,
<p>if(Math.random()

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 154 次瀏覽 ? 2022-06-26 01:10 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。 查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。

信息收集組合拳之從廢棄接口中尋找漏洞

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 67 次瀏覽 ? 2022-06-21 06:53 ? 來(lái)自相關(guān)話(huà)題

  信息收集組合拳之從廢棄接口中尋找漏洞
  使用OneForAll工具通過(guò)域名收集網(wǎng)址和ip
  工具地址:GitHub - shmilylty/OneForAll: OneForAll是一款功能強大的子域收集工具
  常用命令:python oneforall.py --targets targets.txt run
  targets.txt中放入需要掃描的域名,運行后會(huì )在results文件夾下生成掃描結果
  在運行結果中我們可以看到有url、子域名、ip
  其中運行結果中ip是一行多個(gè)、還有重復的,我們需要提取ip轉換成每行一個(gè)ip且沒(méi)有重復的txt文本中,方便其他工具掃描
  腳本:刪除重復ip
  #!/usr/bin/env python# conding:utf-8<br />##把同一行的ip換行,然后寫(xiě)進(jìn)result.txt的文件里with open('ip.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): with open('result.txt','a',encoding='utf-8') as writelist: b = dirs.replace(",", '\n') writelist.write(b)<br />#去除重復ip,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://blog.csdn.net/qq_22764 ... s%3D1
  提取成這樣單行一個(gè)ip且不重復的文本,我們就可以放到goby、fscan、小米范等工具中掃描
  
  fscan工具掃描ip
  工具地址:GitHub - shadow1ng/fscan: 一款內網(wǎng)綜合掃描工具,方便一鍵自動(dòng)化、全方位漏掃掃描。
  這款工具主要是用于內網(wǎng)掃描,發(fā)現資產(chǎn)以及進(jìn)行漏洞掃描與弱口令爆破,運行速度很快,用于外網(wǎng)探測發(fā)現一些web資產(chǎn)也是不錯的選擇
  常用命令:全端口掃描 fscan64.exe -hf ip.txt -p 1-65535 -o result.txt
  ip.txt中放入需要掃描的ip地址,result.txt為運行結果
  
  小米范
  工具地址:我的小工具 - 標簽 - 范世強 - 博客園
 ?。ㄕ也坏竭@個(gè)版本的地址了,就貼個(gè)作者的博客地址吧)
  JSFinder掃描js及url
  工具地址:GitHub - Threezh1/JSFinder: JSFinder is a tool for quickly extracting URLs and subdomains from JS files on a website.
  常用命令:python JSFinder.py -f targets.txt -d -ou JSurl.txt -os JSdomain.txt
  targets.txt中放入需要掃描的url,運行結束后生會(huì )成兩個(gè)txt文本, JSurl.txt為URL,JSdomain.txt為子域名
  上面這些工具的掃描結果中含有很多的url,我們需要效率高一些的話(huà)我們可以?xún)?yōu)先從參數下手,于是需要篩選含有參數的url
  腳本:提取含有參數的url
  #!/usr/bin/env python# conding:utf-8<br />#字符串中有“?”且不在字符串的結尾的就寫(xiě)入result.txt中with open('JSurl.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): # re_result=re.search(r"'?'",dirs) # re_result=str(re_result) if "?" in dirs :#判斷字符中是否有“?”,如果有則返回該字符串的位置,是從坐標0開(kāi)始算的 re = dirs.find("?") # a=len(dirs)-2是為了判斷“?”是不是在最后一個(gè)字符,len()與find()不同是從一開(kāi)始算字符串的長(cháng)度的,在加上每行字符中\n換行符也占了一個(gè)字符,所以要減2 a=len(dirs)-2#判斷字符串中“?”是不是在字符的最后 if re < a : with open('result.txt','a',encoding='utf-8') as writelist: writelist.write(dirs)<br />#去除result.txt中的重復字符串,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://www.cnblogs.com/luguankun/p/11846401.html(判斷一個(gè)字符是否在一個(gè)字符串中)
  <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  運行完腳本生成的含有參數的url如下所示
  從廢棄接口中尋找漏洞
  有些網(wǎng)站經(jīng)過(guò)了多輪滲透,正常業(yè)務(wù)都測爛的,連邏輯漏洞都找不到,經(jīng)歷了上面一番信息收集后,一般能收集到網(wǎng)站的歷史業(yè)務(wù)中的url。
  然后我們將url使用腳本處理篩選之后的結果可以批量的放到sqlmap中跑,還有一些敏感接口可以嘗試尋找越權、信息泄露等。
  sqlmap批量掃描
  常用命令:python sqlmap.py -m urls.txt --batch
  在urls.txt文件內放入我們使用“提取含有參數的url”這個(gè)腳本篩選后的url
  除了參數以外也可以用同樣的思路把腳本修改一下去找敏感接口與url跳轉參數等
  常見(jiàn)敏感接口
  常見(jiàn)跳轉參數
  toUrl=
  login_url=
  register_url
  redirect_url=
  load_url=
  proxy_url=
  file_url=
  jump_url=
  某次項目中客戶(hù)都疑惑我怎么找到的接口
  
  轉載于:
  推薦↓↓↓ 查看全部

  信息收集組合拳之從廢棄接口中尋找漏洞
  使用OneForAll工具通過(guò)域名收集網(wǎng)址和ip
  工具地址:GitHub - shmilylty/OneForAll: OneForAll是一款功能強大的子域收集工具
  常用命令:python oneforall.py --targets targets.txt run
  targets.txt中放入需要掃描的域名,運行后會(huì )在results文件夾下生成掃描結果
  在運行結果中我們可以看到有url、子域名、ip
  其中運行結果中ip是一行多個(gè)、還有重復的,我們需要提取ip轉換成每行一個(gè)ip且沒(méi)有重復的txt文本中,方便其他工具掃描
  腳本:刪除重復ip
  #!/usr/bin/env python# conding:utf-8<br />##把同一行的ip換行,然后寫(xiě)進(jìn)result.txt的文件里with open('ip.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): with open('result.txt','a',encoding='utf-8') as writelist: b = dirs.replace(",", '\n') writelist.write(b)<br />#去除重復ip,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://blog.csdn.net/qq_22764 ... s%3D1
  提取成這樣單行一個(gè)ip且不重復的文本,我們就可以放到goby、fscan、小米范等工具中掃描
  
  fscan工具掃描ip
  工具地址:GitHub - shadow1ng/fscan: 一款內網(wǎng)綜合掃描工具,方便一鍵自動(dòng)化、全方位漏掃掃描。
  這款工具主要是用于內網(wǎng)掃描,發(fā)現資產(chǎn)以及進(jìn)行漏洞掃描與弱口令爆破,運行速度很快,用于外網(wǎng)探測發(fā)現一些web資產(chǎn)也是不錯的選擇
  常用命令:全端口掃描 fscan64.exe -hf ip.txt -p 1-65535 -o result.txt
  ip.txt中放入需要掃描的ip地址,result.txt為運行結果
  
  小米范
  工具地址:我的小工具 - 標簽 - 范世強 - 博客園
 ?。ㄕ也坏竭@個(gè)版本的地址了,就貼個(gè)作者的博客地址吧)
  JSFinder掃描js及url
  工具地址:GitHub - Threezh1/JSFinder: JSFinder is a tool for quickly extracting URLs and subdomains from JS files on a website.
  常用命令:python JSFinder.py -f targets.txt -d -ou JSurl.txt -os JSdomain.txt
  targets.txt中放入需要掃描的url,運行結束后生會(huì )成兩個(gè)txt文本, JSurl.txt為URL,JSdomain.txt為子域名
  上面這些工具的掃描結果中含有很多的url,我們需要效率高一些的話(huà)我們可以?xún)?yōu)先從參數下手,于是需要篩選含有參數的url
  腳本:提取含有參數的url
  #!/usr/bin/env python# conding:utf-8<br />#字符串中有“?”且不在字符串的結尾的就寫(xiě)入result.txt中with open('JSurl.txt','r',encoding='utf-8') as readlist: for dirs in readlist.readlines(): # re_result=re.search(r"'?'",dirs) # re_result=str(re_result) if "?" in dirs :#判斷字符中是否有“?”,如果有則返回該字符串的位置,是從坐標0開(kāi)始算的 re = dirs.find("?") # a=len(dirs)-2是為了判斷“?”是不是在最后一個(gè)字符,len()與find()不同是從一開(kāi)始算字符串的長(cháng)度的,在加上每行字符中\n換行符也占了一個(gè)字符,所以要減2 a=len(dirs)-2#判斷字符串中“?”是不是在字符的最后 if re < a : with open('result.txt','a',encoding='utf-8') as writelist: writelist.write(dirs)<br />#去除result.txt中的重復字符串,然后把結果寫(xiě)進(jìn)only.txt文件里with open('result.txt','r',encoding='utf-8') as readlist: lines_seen = set() for line in readlist.readlines(): if line not in lines_seen: lines_seen.add(line) with open('only.txt','a',encoding='utf-8') as writelist: writelist.write(line)<br />#參考文章:https://www.cnblogs.com/luguankun/p/11846401.html(判斷一個(gè)字符是否在一個(gè)字符串中)
  <br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
  
  運行完腳本生成的含有參數的url如下所示
  從廢棄接口中尋找漏洞
  有些網(wǎng)站經(jīng)過(guò)了多輪滲透,正常業(yè)務(wù)都測爛的,連邏輯漏洞都找不到,經(jīng)歷了上面一番信息收集后,一般能收集到網(wǎng)站的歷史業(yè)務(wù)中的url。
  然后我們將url使用腳本處理篩選之后的結果可以批量的放到sqlmap中跑,還有一些敏感接口可以嘗試尋找越權、信息泄露等。
  sqlmap批量掃描
  常用命令:python sqlmap.py -m urls.txt --batch
  在urls.txt文件內放入我們使用“提取含有參數的url”這個(gè)腳本篩選后的url
  除了參數以外也可以用同樣的思路把腳本修改一下去找敏感接口與url跳轉參數等
  常見(jiàn)敏感接口
  常見(jiàn)跳轉參數
  toUrl=
  login_url=
  register_url
  redirect_url=
  load_url=
  proxy_url=
  file_url=
  jump_url=
  某次項目中客戶(hù)都疑惑我怎么找到的接口
  
  轉載于:
  推薦↓↓↓

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 119 次瀏覽 ? 2022-06-19 21:31 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。 查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。

圖數據庫無(wú)縫集成Tushare接口

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 74 次瀏覽 ? 2022-06-11 03:29 ? 來(lái)自相關(guān)話(huà)題

  圖數據庫無(wú)縫集成Tushare接口
  
  @TOC[1]Here's the table of contents:
  圖數據庫無(wú)縫集成Tushare接口
  使用第三方API,有助于我們快速集成數據,構建業(yè)務(wù)分析需要的知識圖譜數據。這篇文章主要介紹如何將Tushare HTTP接口集成到圖數據庫,并使用Cypher構建知識圖譜。
  在開(kāi)始集成前,請確保你的圖數據庫安裝了APOC組件,并保證apoc.load.jsonParams過(guò)程可以正常使用。APOC同時(shí)還支持JSON Path,即以特定模式搜索JSON文檔中的數據項并返回其內容,其概念類(lèi)似應用于XML的XPath和應用于HTML的jQuery。更多使用方式可以查看Neo4j社區技術(shù)專(zhuān)家俞博士的文章Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據[2]。
  一、Tushare介紹
  Tushare[3]是一個(gè)免費、開(kāi)源的python財經(jīng)數據接口包。主要實(shí)現對股票等金融數據從數據采集、清洗加工到數據存儲的過(guò)程,能夠為金融分析人員提供快速、整潔、和多樣的便于分析的數據,為他們在數據獲取方面極大地減輕工作量,使他們更加專(zhuān)注于策略和模型的研究與實(shí)現上。
  
  Tushare官網(wǎng)二、集成Tushare接口
  在開(kāi)始集成之前請確保你有一個(gè)Tushare的訪(fǎng)問(wèn)賬號。
  在圖數據庫的安裝目錄下找到conf文件夾,并在neo4j.conf文件中為T(mén)ushare HTTP API的URL定義別名,新增一行配置即可。修改配置后,重啟數據庫服務(wù)即可。
  apoc.json.tushare.url=http://api.tushare.pro
  三、使用接口數據
  現在我們可以編寫(xiě)Cypher代碼很方便地從Tushare獲取數據了。下面我將演示一個(gè)申萬(wàn)成分股圖譜構建的案例。請注意在使用Cypher腳本時(shí)請設置私有token。
  下面的代碼通過(guò)分批循環(huán)調用首先從stock_basic接口獲取到股票代碼,然后再使用股票代碼獲取申萬(wàn)成分股時(shí)間序列數據。每個(gè)股票代碼調用index_member接口之前,設置了執行四百萬(wàn)次加法運算表示進(jìn)行延時(shí)1~2秒,這個(gè)操作的目的是為了保證HTTP接口調用時(shí)不要超過(guò)接口頻率限制。
  //循環(huán)獲取所有股票代碼<br />WITH?RANGE(1,10)?AS?list,1000?AS?limit<br />UNWIND?list?AS?num<br />WITH?num*limit?AS?offset,limit<br />//分批獲取股票代碼<br />WITH?<br />????'{"api_name":"stock_basic","token":"xxxxxxxxxxx","params":{"limit":"'+limit+'","offset":"'+offset+'"},"fields":"ts_code"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.has_more?AS?has_more,value.data.items?AS?stocks<br />WHERE?has_more<br />//獲取申萬(wàn)成分數據<br />WITH?has_more,stocks<br />UNWIND?stocks?AS?stock<br />WITH?stock<br />//延時(shí)執行【HTTP?API對調用頻率有限制,讓函數執行四百萬(wàn)次加法,耗時(shí)大概1~2秒】<br />WITH?RANGE(1,2000)?AS?l,stock?UNWIND?l?AS?a?UNWIND?l?AS?b?WITH?SUM(a+b)?AS?delay,stock<br />WITH?<br />????'{"api_name":"index_member","token":"xxxxxxxxxxx","params":{"ts_code":"'+stock[0]+'"},"fields":"index_code,index_name,con_code,con_name,in_date,out_date,is_new"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.items?AS?items<br />//構建申萬(wàn)行業(yè)成分股圖譜<br />UNWIND?items?AS?item<br />WITH?item<br />MERGE?(stk:股票?{code:item[2]})?SET?stk+={name:item[3]+'('+item[2]+')'}<br />MERGE?(hy:申萬(wàn)行業(yè)?{code:item[0]})?SET?hy+={name:item[1]+'('+item[0]+')'}<br />CREATE?(stk)-[r:屬于]->(hy)?SET?r+={in_date:item[4],out_date:item[5],is_new:item[6]}
  
  申萬(wàn)行業(yè)成分股時(shí)序圖譜引用鏈接
  [1]TOC:圖數據庫無(wú)縫集成Tushare接口
  [2]Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據:
  [3]Tushare:
   查看全部

  圖數據庫無(wú)縫集成Tushare接口
  
  @TOC[1]Here's the table of contents:
  圖數據庫無(wú)縫集成Tushare接口
  使用第三方API,有助于我們快速集成數據,構建業(yè)務(wù)分析需要的知識圖譜數據。這篇文章主要介紹如何將Tushare HTTP接口集成到圖數據庫,并使用Cypher構建知識圖譜。
  在開(kāi)始集成前,請確保你的圖數據庫安裝了APOC組件,并保證apoc.load.jsonParams過(guò)程可以正常使用。APOC同時(shí)還支持JSON Path,即以特定模式搜索JSON文檔中的數據項并返回其內容,其概念類(lèi)似應用于XML的XPath和應用于HTML的jQuery。更多使用方式可以查看Neo4j社區技術(shù)專(zhuān)家俞博士的文章Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據[2]。
  一、Tushare介紹
  Tushare[3]是一個(gè)免費、開(kāi)源的python財經(jīng)數據接口包。主要實(shí)現對股票等金融數據從數據采集、清洗加工到數據存儲的過(guò)程,能夠為金融分析人員提供快速、整潔、和多樣的便于分析的數據,為他們在數據獲取方面極大地減輕工作量,使他們更加專(zhuān)注于策略和模型的研究與實(shí)現上。
  
  Tushare官網(wǎng)二、集成Tushare接口
  在開(kāi)始集成之前請確保你有一個(gè)Tushare的訪(fǎng)問(wèn)賬號。
  在圖數據庫的安裝目錄下找到conf文件夾,并在neo4j.conf文件中為T(mén)ushare HTTP API的URL定義別名,新增一行配置即可。修改配置后,重啟數據庫服務(wù)即可。
  apoc.json.tushare.url=http://api.tushare.pro
  三、使用接口數據
  現在我們可以編寫(xiě)Cypher代碼很方便地從Tushare獲取數據了。下面我將演示一個(gè)申萬(wàn)成分股圖譜構建的案例。請注意在使用Cypher腳本時(shí)請設置私有token。
  下面的代碼通過(guò)分批循環(huán)調用首先從stock_basic接口獲取到股票代碼,然后再使用股票代碼獲取申萬(wàn)成分股時(shí)間序列數據。每個(gè)股票代碼調用index_member接口之前,設置了執行四百萬(wàn)次加法運算表示進(jìn)行延時(shí)1~2秒,這個(gè)操作的目的是為了保證HTTP接口調用時(shí)不要超過(guò)接口頻率限制。
  //循環(huán)獲取所有股票代碼<br />WITH?RANGE(1,10)?AS?list,1000?AS?limit<br />UNWIND?list?AS?num<br />WITH?num*limit?AS?offset,limit<br />//分批獲取股票代碼<br />WITH?<br />????'{"api_name":"stock_basic","token":"xxxxxxxxxxx","params":{"limit":"'+limit+'","offset":"'+offset+'"},"fields":"ts_code"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.has_more?AS?has_more,value.data.items?AS?stocks<br />WHERE?has_more<br />//獲取申萬(wàn)成分數據<br />WITH?has_more,stocks<br />UNWIND?stocks?AS?stock<br />WITH?stock<br />//延時(shí)執行【HTTP?API對調用頻率有限制,讓函數執行四百萬(wàn)次加法,耗時(shí)大概1~2秒】<br />WITH?RANGE(1,2000)?AS?l,stock?UNWIND?l?AS?a?UNWIND?l?AS?b?WITH?SUM(a+b)?AS?delay,stock<br />WITH?<br />????'{"api_name":"index_member","token":"xxxxxxxxxxx","params":{"ts_code":"'+stock[0]+'"},"fields":"index_code,index_name,con_code,con_name,in_date,out_date,is_new"}'?AS?payload<br />CALL?apoc.load.jsonParams(<br />????'tushare',<br />????NULL,<br />????payload,<br />????NULL,<br />????{})?yield?value<br />WITH?value.data.items?AS?items<br />//構建申萬(wàn)行業(yè)成分股圖譜<br />UNWIND?items?AS?item<br />WITH?item<br />MERGE?(stk:股票?{code:item[2]})?SET?stk+={name:item[3]+'('+item[2]+')'}<br />MERGE?(hy:申萬(wàn)行業(yè)?{code:item[0]})?SET?hy+={name:item[1]+'('+item[0]+')'}<br />CREATE?(stk)-[r:屬于]->(hy)?SET?r+={in_date:item[4],out_date:item[5],is_new:item[6]}
  
  申萬(wàn)行業(yè)成分股時(shí)序圖譜引用鏈接
  [1]TOC:圖數據庫無(wú)縫集成Tushare接口
  [2]Neo4j圖數據庫高級應用系列 / 服務(wù)器擴展指南 APOC(5.5) - 導入JSON數據:
  [3]Tushare:
  

【干貨】一篇文章講透數據挖掘

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 134 次瀏覽 ? 2022-06-10 03:56 ? 來(lái)自相關(guān)話(huà)題

  【干貨】一篇文章講透數據挖掘
  (1)數據挖掘流程
  數據挖掘一般是指從大量的數據中通過(guò)算法搜索隱藏于其中信息的過(guò)程。它通常與計算機科學(xué)有關(guān),并通過(guò)統計、在線(xiàn)分析處理、情報檢索、機器學(xué)習、專(zhuān)家系統(依靠過(guò)去的經(jīng)驗法則)和模式識別等諸多方法來(lái)實(shí)現上述目標。它的分析方法包括:分類(lèi)、估計、預測、相關(guān)性分組或關(guān)聯(lián)規則、聚類(lèi)和復雜數據類(lèi)型挖掘。
  
  1)數據的采集
  首先得有數據,數據的收集有兩個(gè)方式,第一個(gè)方式是拿,專(zhuān)業(yè)點(diǎn)的說(shuō)法叫抓取或者爬取,例如搜索引擎就是這么做的,它把網(wǎng)上的所有的信息都下載到它的數據中心,然后你一搜才能搜出來(lái)。
  2)數據的傳輸
  一般會(huì )通過(guò)隊列方式進(jìn)行,因為數據量實(shí)在是太大了,數據必須經(jīng)過(guò)處理才會(huì )有用,可是系統處理不過(guò)來(lái),只好排好隊,慢慢的處理。
  3)數據的存儲
  現在數據就是金錢(qián),掌握了數據就相當于掌握了錢(qián)。要不然網(wǎng)站怎么知道你想買(mǎi)什么呢?就是因為它有你歷史的交易的數據,這個(gè)信息可不能給別人,十分寶貴,所以需要存儲下來(lái)。
  4)數據的清洗和分析
  上面存儲的數據是原始數據,原始數據多是雜亂無(wú)章的,有很多垃圾數據在里面,因而需要清洗和過(guò)濾,得到一些高質(zhì)量的數據。對于高質(zhì)量的數據,就可以進(jìn)行分析,從而對數據進(jìn)行分類(lèi),或者發(fā)現數據之間的相互關(guān)系,得到知識。
  注:第三與第四個(gè)步驟,現存后清洗和先清洗再存,在真是的業(yè)務(wù)場(chǎng)景中可以適當互換。
  5)數據的檢索和挖掘
  檢索就是搜索,所謂外事問(wèn)google,內事問(wèn)百度。挖掘,僅僅搜索出來(lái)已經(jīng)不能滿(mǎn)足人們的要求了,還需要從信息中挖掘出相互的關(guān)系。
  6)數據的加載與應用
  怎么友好的展示與傳遞給用戶(hù)為數據挖掘工作做好閉環(huán)。
  (2)數據挖掘工具類(lèi)1)數據采集工具
  1、針對日志文件類(lèi)
  工具
  定義
  Logstash
  Logstash是一個(gè)開(kāi)源數據收集引擎,具有實(shí)時(shí)管道功能。Logstash可以動(dòng)態(tài)地將來(lái)自不同數據源的數據統一起來(lái),并將數據標準化到所選擇的目的地。
  Filebeat
  Filebeat 作為一個(gè)輕量級的日志傳輸工具可以將日志推送到中心 Logstash。
  Fluentd
  Fluentd 創(chuàng )建的初衷主要是盡可能的使用 JSON 作為日志輸出,所以傳輸工具及其下游的傳輸線(xiàn)不需要猜測子字符串里面各個(gè)字段的類(lèi)型。這樣,它為幾乎所有的語(yǔ)言都提供庫,即可以將它插入到自定義的程序中。
  Logagent
  Logagent 是 Sematext 提供的傳輸工具,它用來(lái)將日志傳輸到 Logsene(一個(gè)基于 SaaS 平臺的 Elasticsearch API)。
  Rsylog
  絕大多數 Linux 發(fā)布版本默認的守護進(jìn)程,rsyslog 讀取并寫(xiě)入 /var/log/messages 。它可以提取文件、解析、緩沖(磁盤(pán)和內存)以及將它們傳輸到多個(gè)目的地,包括 Elasticsearch ??梢詮拇颂幷业饺绾翁幚?Apache 以及系統日志。
  Logtail
  阿里云日志服務(wù)的生產(chǎn)者,目前在阿里集團內部機器上運行,經(jīng)過(guò)3年多時(shí)間的考驗,目前為阿里公有云用戶(hù)提供日志收集服務(wù)。
  2、針對爬蟲(chóng)類(lèi)
  頁(yè)面下載 --> 頁(yè)面解析 --> 數據存儲
 ?。?)頁(yè)面下載器
  對于下載器而言,python的庫requests能滿(mǎn)足大部分測試+抓取需求,進(jìn)階工程化scrapy,動(dòng)態(tài)網(wǎng)頁(yè)優(yōu)先找API接口,如果有簡(jiǎn)單加密就破解,實(shí)在困難就使用splash渲染。
 ?。?)頁(yè)面解析器
 ?、貰eautifulSoup(入門(mén)級):Python爬蟲(chóng)入門(mén)BeautifulSoup模塊
 ?、趐yquery(類(lèi)似jQuery):Python爬蟲(chóng):pyquery模塊解析網(wǎng)頁(yè)
 ?、踠xml:Python爬蟲(chóng):使用lxml解析網(wǎng)頁(yè)內容
 ?、躳arsel:Extract text using CSS or XPath selectors
 ?、輘crapy的Selector (強烈推薦, 比較高級的封裝,基于parsel)
 ?、捱x擇器(Selectors):python爬蟲(chóng):scrapy框架xpath和css選擇器語(yǔ)法
  ---------------------
  總結:
  解析器直接使用scrapy的Selector 就行,簡(jiǎn)單、直接、高效。
 ?。?)數據存儲
 ?、賢xt文本:Python全棧之路:文件file常用操作
 ?、赾sv文件:python讀取寫(xiě)入csv文件
 ?、踫qlite3 (python自帶):Python編程:使用數據庫sqlite3
 ?、躆ySQL:SQL:pymysql模塊讀寫(xiě)mysql數據
 ?、軲ongoDB:Python編程:mongodb的基本增刪改查操作
  ---------------------
  總結:
  數據存儲沒(méi)有什么可深究的,按照業(yè)務(wù)需求來(lái)就行,一般快速測試使用MongoDB,業(yè)務(wù)使用MySQL
 ?。?)其他工具
 ?、賓xecjs :執行js
  Python爬蟲(chóng):execjs在python中運行javascript代碼
 ?、趐yv8: 執行js
  mac安裝pyv8模塊-JavaScript翻譯成python
 ?、踙tml5lib
  Python爬蟲(chóng):scrapy利用html5lib解析不規范的html文本
  2)數據清洗工具
  1、DataWrangler
  基于網(wǎng)絡(luò )的服務(wù)是斯坦福大學(xué)的可視化組設計來(lái)清洗和重排數據的.文本編輯非常簡(jiǎn)單。例如,當我選擇大標題為“Reported crime in Alabama”的樣本數據的某行的“Alabama”,然后選擇另一組數據的“Alaska”,它會(huì )建議提取每州的名字。把鼠標停留在建議上,就可以看到用紅色突出顯示的行。
  2、Google Refine
  它可以導入導出多種格式的數據,如標簽或逗號分隔的文本文件、Excel、XML和JSON文件。Refine設有內置算法,可以發(fā)現一些拼寫(xiě)不一樣但實(shí)際上應分為一組的文本。導入你的數據后,選擇編輯單元格->聚類(lèi),編輯,然后選擇要用的算法。數據選項,提供快速簡(jiǎn)單的數據分布概貌。這個(gè)功能可以揭示那些可能由于輸入錯誤導致的異?!?,工資記錄不是80,000美元而竟然是800,000美元;或指出不一致的地方——例如薪酬數據記錄之間的差異,有的是計時(shí)工資,有的是每周支付,有的是年薪。除了數據管家功能,Google Refine還提供了一些有用的分析工具,例如排序和篩選。
  3、Logstash
  Logstash 是一款強大的數據處理工具,它可以實(shí)現數據傳輸,格式處理,格式化輸出,還有強大的插件功能,常用于日志處理。
  3)數據存儲工具
  數據存儲主要分為結構化數據的存儲和非結構化數據的存儲。
  1、結構化數據
 ?。?)定義
  一般指存儲在數據庫中,具有一定邏輯結構和物理結構的數據,最為常見(jiàn)的是存儲在關(guān)系數據庫中的數據;非結構化數據:一般指結構化數據以外的數據,這些數據不存儲在數據庫中,而是以各種類(lèi)型的文本形式存放,其中Web上的一些數據(內嵌于HTML或XML標記中)又具有一定的邏輯結構和物理結構,被稱(chēng)為半結構數據。
 ?。?)存儲系統
  目前比較成熟的結構化存儲系統有Oracle、MySQL、Hadoop等。
  2、非結構化數據
 ?。?)定義
  非結構化數據是數據結構不規則或不完整,沒(méi)有預定義的數據模型,不方便用數據庫二維邏輯表來(lái)表現的數據。包括所有格式的辦公文檔、文本、圖片、XML, HTML、各類(lèi)報表、圖像和音頻/視頻信息等等。
 ?。?)存儲方式
  1)使用文件系統存儲文件,而在數據庫中存儲訪(fǎng)問(wèn)路徑。這種方式的優(yōu)點(diǎn)是實(shí)現簡(jiǎn)單,不需要DBMS的高級功能,但是這種方式無(wú)法實(shí)現文件的事務(wù)性訪(fǎng)問(wèn),不便于數據備份和恢復,不便于數據遷移等;
  2)使用阿里云OSS的文件存儲功能。
  4)數據計算工具
  數據計算分為實(shí)時(shí)計算、在線(xiàn)計算、離線(xiàn)計算。
  1、數據實(shí)時(shí)計算
  ApacheStorm
  2、數據在線(xiàn)計算
  Elasticsearch
  MySQL
  3、數據離線(xiàn)計算
  HaDoop Hive
  5)數據分析工具
  1、對數據矩陣科學(xué)計算:Python的numpy庫
  2、對數據切片等常規處理:強大的pandas庫
  3、對數據建模處理:sklearn庫
  6)數據加載工具
  1、數據的可視化處理:Python中的matplotlib和seaborn庫
  2、常用的BI可視化工具:Tableu和帆軟
  3、ECharts
  ——————————————
  閱讀推薦 查看全部

  【干貨】一篇文章講透數據挖掘
  (1)數據挖掘流程
  數據挖掘一般是指從大量的數據中通過(guò)算法搜索隱藏于其中信息的過(guò)程。它通常與計算機科學(xué)有關(guān),并通過(guò)統計、在線(xiàn)分析處理、情報檢索、機器學(xué)習、專(zhuān)家系統(依靠過(guò)去的經(jīng)驗法則)和模式識別等諸多方法來(lái)實(shí)現上述目標。它的分析方法包括:分類(lèi)、估計、預測、相關(guān)性分組或關(guān)聯(lián)規則、聚類(lèi)和復雜數據類(lèi)型挖掘。
  
  1)數據的采集
  首先得有數據,數據的收集有兩個(gè)方式,第一個(gè)方式是拿,專(zhuān)業(yè)點(diǎn)的說(shuō)法叫抓取或者爬取,例如搜索引擎就是這么做的,它把網(wǎng)上的所有的信息都下載到它的數據中心,然后你一搜才能搜出來(lái)。
  2)數據的傳輸
  一般會(huì )通過(guò)隊列方式進(jìn)行,因為數據量實(shí)在是太大了,數據必須經(jīng)過(guò)處理才會(huì )有用,可是系統處理不過(guò)來(lái),只好排好隊,慢慢的處理。
  3)數據的存儲
  現在數據就是金錢(qián),掌握了數據就相當于掌握了錢(qián)。要不然網(wǎng)站怎么知道你想買(mǎi)什么呢?就是因為它有你歷史的交易的數據,這個(gè)信息可不能給別人,十分寶貴,所以需要存儲下來(lái)。
  4)數據的清洗和分析
  上面存儲的數據是原始數據,原始數據多是雜亂無(wú)章的,有很多垃圾數據在里面,因而需要清洗和過(guò)濾,得到一些高質(zhì)量的數據。對于高質(zhì)量的數據,就可以進(jìn)行分析,從而對數據進(jìn)行分類(lèi),或者發(fā)現數據之間的相互關(guān)系,得到知識。
  注:第三與第四個(gè)步驟,現存后清洗和先清洗再存,在真是的業(yè)務(wù)場(chǎng)景中可以適當互換。
  5)數據的檢索和挖掘
  檢索就是搜索,所謂外事問(wèn)google,內事問(wèn)百度。挖掘,僅僅搜索出來(lái)已經(jīng)不能滿(mǎn)足人們的要求了,還需要從信息中挖掘出相互的關(guān)系。
  6)數據的加載與應用
  怎么友好的展示與傳遞給用戶(hù)為數據挖掘工作做好閉環(huán)。
  (2)數據挖掘工具類(lèi)1)數據采集工具
  1、針對日志文件類(lèi)
  工具
  定義
  Logstash
  Logstash是一個(gè)開(kāi)源數據收集引擎,具有實(shí)時(shí)管道功能。Logstash可以動(dòng)態(tài)地將來(lái)自不同數據源的數據統一起來(lái),并將數據標準化到所選擇的目的地。
  Filebeat
  Filebeat 作為一個(gè)輕量級的日志傳輸工具可以將日志推送到中心 Logstash。
  Fluentd
  Fluentd 創(chuàng )建的初衷主要是盡可能的使用 JSON 作為日志輸出,所以傳輸工具及其下游的傳輸線(xiàn)不需要猜測子字符串里面各個(gè)字段的類(lèi)型。這樣,它為幾乎所有的語(yǔ)言都提供庫,即可以將它插入到自定義的程序中。
  Logagent
  Logagent 是 Sematext 提供的傳輸工具,它用來(lái)將日志傳輸到 Logsene(一個(gè)基于 SaaS 平臺的 Elasticsearch API)。
  Rsylog
  絕大多數 Linux 發(fā)布版本默認的守護進(jìn)程,rsyslog 讀取并寫(xiě)入 /var/log/messages 。它可以提取文件、解析、緩沖(磁盤(pán)和內存)以及將它們傳輸到多個(gè)目的地,包括 Elasticsearch ??梢詮拇颂幷业饺绾翁幚?Apache 以及系統日志。
  Logtail
  阿里云日志服務(wù)的生產(chǎn)者,目前在阿里集團內部機器上運行,經(jīng)過(guò)3年多時(shí)間的考驗,目前為阿里公有云用戶(hù)提供日志收集服務(wù)。
  2、針對爬蟲(chóng)類(lèi)
  頁(yè)面下載 --> 頁(yè)面解析 --> 數據存儲
 ?。?)頁(yè)面下載器
  對于下載器而言,python的庫requests能滿(mǎn)足大部分測試+抓取需求,進(jìn)階工程化scrapy,動(dòng)態(tài)網(wǎng)頁(yè)優(yōu)先找API接口,如果有簡(jiǎn)單加密就破解,實(shí)在困難就使用splash渲染。
 ?。?)頁(yè)面解析器
 ?、貰eautifulSoup(入門(mén)級):Python爬蟲(chóng)入門(mén)BeautifulSoup模塊
 ?、趐yquery(類(lèi)似jQuery):Python爬蟲(chóng):pyquery模塊解析網(wǎng)頁(yè)
 ?、踠xml:Python爬蟲(chóng):使用lxml解析網(wǎng)頁(yè)內容
 ?、躳arsel:Extract text using CSS or XPath selectors
 ?、輘crapy的Selector (強烈推薦, 比較高級的封裝,基于parsel)
 ?、捱x擇器(Selectors):python爬蟲(chóng):scrapy框架xpath和css選擇器語(yǔ)法
  ---------------------
  總結:
  解析器直接使用scrapy的Selector 就行,簡(jiǎn)單、直接、高效。
 ?。?)數據存儲
 ?、賢xt文本:Python全棧之路:文件file常用操作
 ?、赾sv文件:python讀取寫(xiě)入csv文件
 ?、踫qlite3 (python自帶):Python編程:使用數據庫sqlite3
 ?、躆ySQL:SQL:pymysql模塊讀寫(xiě)mysql數據
 ?、軲ongoDB:Python編程:mongodb的基本增刪改查操作
  ---------------------
  總結:
  數據存儲沒(méi)有什么可深究的,按照業(yè)務(wù)需求來(lái)就行,一般快速測試使用MongoDB,業(yè)務(wù)使用MySQL
 ?。?)其他工具
 ?、賓xecjs :執行js
  Python爬蟲(chóng):execjs在python中運行javascript代碼
 ?、趐yv8: 執行js
  mac安裝pyv8模塊-JavaScript翻譯成python
 ?、踙tml5lib
  Python爬蟲(chóng):scrapy利用html5lib解析不規范的html文本
  2)數據清洗工具
  1、DataWrangler
  基于網(wǎng)絡(luò )的服務(wù)是斯坦福大學(xué)的可視化組設計來(lái)清洗和重排數據的.文本編輯非常簡(jiǎn)單。例如,當我選擇大標題為“Reported crime in Alabama”的樣本數據的某行的“Alabama”,然后選擇另一組數據的“Alaska”,它會(huì )建議提取每州的名字。把鼠標停留在建議上,就可以看到用紅色突出顯示的行。
  2、Google Refine
  它可以導入導出多種格式的數據,如標簽或逗號分隔的文本文件、Excel、XML和JSON文件。Refine設有內置算法,可以發(fā)現一些拼寫(xiě)不一樣但實(shí)際上應分為一組的文本。導入你的數據后,選擇編輯單元格->聚類(lèi),編輯,然后選擇要用的算法。數據選項,提供快速簡(jiǎn)單的數據分布概貌。這個(gè)功能可以揭示那些可能由于輸入錯誤導致的異?!?,工資記錄不是80,000美元而竟然是800,000美元;或指出不一致的地方——例如薪酬數據記錄之間的差異,有的是計時(shí)工資,有的是每周支付,有的是年薪。除了數據管家功能,Google Refine還提供了一些有用的分析工具,例如排序和篩選。
  3、Logstash
  Logstash 是一款強大的數據處理工具,它可以實(shí)現數據傳輸,格式處理,格式化輸出,還有強大的插件功能,常用于日志處理。
  3)數據存儲工具
  數據存儲主要分為結構化數據的存儲和非結構化數據的存儲。
  1、結構化數據
 ?。?)定義
  一般指存儲在數據庫中,具有一定邏輯結構和物理結構的數據,最為常見(jiàn)的是存儲在關(guān)系數據庫中的數據;非結構化數據:一般指結構化數據以外的數據,這些數據不存儲在數據庫中,而是以各種類(lèi)型的文本形式存放,其中Web上的一些數據(內嵌于HTML或XML標記中)又具有一定的邏輯結構和物理結構,被稱(chēng)為半結構數據。
 ?。?)存儲系統
  目前比較成熟的結構化存儲系統有Oracle、MySQL、Hadoop等。
  2、非結構化數據
 ?。?)定義
  非結構化數據是數據結構不規則或不完整,沒(méi)有預定義的數據模型,不方便用數據庫二維邏輯表來(lái)表現的數據。包括所有格式的辦公文檔、文本、圖片、XML, HTML、各類(lèi)報表、圖像和音頻/視頻信息等等。
 ?。?)存儲方式
  1)使用文件系統存儲文件,而在數據庫中存儲訪(fǎng)問(wèn)路徑。這種方式的優(yōu)點(diǎn)是實(shí)現簡(jiǎn)單,不需要DBMS的高級功能,但是這種方式無(wú)法實(shí)現文件的事務(wù)性訪(fǎng)問(wèn),不便于數據備份和恢復,不便于數據遷移等;
  2)使用阿里云OSS的文件存儲功能。
  4)數據計算工具
  數據計算分為實(shí)時(shí)計算、在線(xiàn)計算、離線(xiàn)計算。
  1、數據實(shí)時(shí)計算
  ApacheStorm
  2、數據在線(xiàn)計算
  Elasticsearch
  MySQL
  3、數據離線(xiàn)計算
  HaDoop Hive
  5)數據分析工具
  1、對數據矩陣科學(xué)計算:Python的numpy庫
  2、對數據切片等常規處理:強大的pandas庫
  3、對數據建模處理:sklearn庫
  6)數據加載工具
  1、數據的可視化處理:Python中的matplotlib和seaborn庫
  2、常用的BI可視化工具:Tableu和帆軟
  3、ECharts
  ——————————————
  閱讀推薦

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 239 次瀏覽 ? 2022-06-07 07:15 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 148 次瀏覽 ? 2022-06-06 08:26 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

深入淺出云原生環(huán)境信息收集技術(shù)(一)

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 62 次瀏覽 ? 2022-06-05 10:27 ? 來(lái)自相關(guān)話(huà)題

  深入淺出云原生環(huán)境信息收集技術(shù)(一)
  前言
  信息收集在攻擊和防御兩端都是非常重要的一環(huán)。從宏觀(guān)的角度來(lái)說(shuō),大多數信息相關(guān)的工作都可以看作信息收集和信息處理交替進(jìn)行的循環(huán)。優(yōu)質(zhì)的信息收集成果是后續工作順利展開(kāi)的首要條件?!秾O子兵法》有云:故善戰人之勢,如轉圓石于千仞之山者,勢也。在掌握了充足信息后,攻防工作將“如轉圓石于千仞之山”。
  然而,信息的瑣碎性和云原生本身的復雜組成為云原生環(huán)境下的信息收集工作帶來(lái)了一定挑戰。有些朋友也許會(huì )說(shuō),這有何難?比如,執行uname -a命令,就能收集到內核信息了。沒(méi)錯,信息收集確實(shí)是一步步進(jìn)行、一項項完成的。但是,如果只是想當然地進(jìn)行,收集到的信息難免陷于凌亂瑣碎,也很可能不全面。
  對此,筆者結合在攻、防兩端積累的經(jīng)驗,希望與大家探討四個(gè)問(wèn)題:
  1. 站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  2. 站在攻擊者視角,云原生環(huán)境下的信息分類(lèi)維度有哪些?
  3. 站在攻擊者視角,收集到的云原生環(huán)境信息有什么價(jià)值?
  4. 站在攻擊者視角,有沒(méi)有可能阻礙或影響防守者收集信息?
  就“信息收集”這個(gè)話(huà)題而言,毫無(wú)疑問(wèn),防守者是占盡天時(shí)地利的,無(wú)論是能夠收集到的信息種類(lèi)、規模,還是信息收集開(kāi)始的時(shí)間、收集信息所需的權限,都遠遠在攻擊者之上。防守者更需要關(guān)注的是如何使用、分析收集到的信息。因此,我們從攻擊者的角度出發(fā)進(jìn)行探討。這并不意味著(zhù)防守的同學(xué)不需要關(guān)注。相反,只有對攻擊者的技術(shù)了然于胸,才能更好地識別攻擊行為、判定攻擊意圖。
  作為本系列的第一篇文章,本文將展開(kāi)討論第一個(gè)問(wèn)題:站在攻擊者視角,云原生環(huán)境下的收集信息方式有哪些?
  注:文中案例相關(guān)操作均在實(shí)驗環(huán)境中進(jìn)行,相關(guān)技術(shù)僅供研究交流,請勿應用于未授權的滲透測試。
  站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  思路重在“有章可循”。先有一個(gè)點(diǎn),再進(jìn)行發(fā)散。信息收集方式通常與攻擊場(chǎng)景緊密相關(guān)。在云原生環(huán)境下,攻擊場(chǎng)景通常有三種:
  1.攻擊從遠程發(fā)起。遠程發(fā)起的攻擊十分常見(jiàn),例如,通過(guò)存在未授權訪(fǎng)問(wèn)漏洞的KubernetesAPI Server或DockerDaemon執行命令。
  2.攻擊從容器內發(fā)起。容器內發(fā)起的攻擊通常屬于一次滲透測試的后滲透階段——它的前提是獲得了容器內某種權限的Shell,或者是Containeras a Service(后文簡(jiǎn)稱(chēng)CaaS)的場(chǎng)景——攻擊者本身就是容器服務(wù)的“客戶(hù)”。
  3.依托于鏡像的軟件供應鏈攻擊。包括“鏡像漏洞利用”和“鏡像投毒”等,《云原生安全:攻防實(shí)踐與體系構建》第三章對此進(jìn)行了詳細介紹。
  相應地,信息收集方式主要也有這三種,與攻擊場(chǎng)景相伴而生。讓我們來(lái)一起看一下。
  1通過(guò)遠程交互收集信息
  綜合來(lái)看,云原生環(huán)境中開(kāi)放的遠程服務(wù)主要有兩類(lèi):云原生控制面服務(wù)和容器化業(yè)務(wù)服務(wù)。遠程交互,顧名思義,網(wǎng)絡(luò )可達就行,別無(wú)限制。收集到的信息數量和價(jià)值主要取決于目標的訪(fǎng)問(wèn)控制機制。
 ?。?)從云原生控制面服務(wù)收集信息
  如前所述,如果遇到存在未授權訪(fǎng)問(wèn)漏洞的Kubernetes API Server,不費吹灰之力即可控制整個(gè)云原生集群;如果目標設置了合理的訪(fǎng)問(wèn)控制機制,則獲取到的有價(jià)值信息將大大減少,但也并非毫無(wú)所得。例如,許多Kubernetes API Server允許匿名用戶(hù)訪(fǎng)問(wèn)部分API endpoints。在下面的示例中,攻擊者通過(guò)訪(fǎng)問(wèn)/version,獲得了目標Kubernetes的版本號:
  rambo@t-matrix:~$ curl -k https://1.1.1.1:6443/version{ "major": "1", "minor": "16", "gitVersion": "v1.16.2", "gitCommit": "c97fe5036ef3df2967d086711e6c0c405941e14b", "gitTreeState": "clean", "buildDate": "2019-10-15T19:09:08Z", "goVersion": "go1.12.10", "compiler": "gc", "platform": "linux/amd64"}
  通過(guò)版本匹配,攻擊者能夠判斷目標Kubernetes可能存在哪些漏洞,從而進(jìn)一步利用,這便是版本信息的價(jià)值。
  即使目標設置了非常嚴格的訪(fǎng)問(wèn)控制,攻擊者通常也能夠獲得一些信息。例如,即使訪(fǎng)問(wèn)/version失敗,根據失敗信息我們能夠得知目標是一個(gè)Kubernetes集群,從而利用Kubernetes的背景知識,去探測其他Kubernetes控制面服務(wù)端口(如kubelet、etcd等)。
 ?。?)從容器化業(yè)務(wù)服務(wù)收集信息
  大多數情況下,就信息收集而言,容器化與非容器化業(yè)務(wù)服務(wù)沒(méi)有顯著(zhù)不同之處,收集到的信息均與業(yè)務(wù)服務(wù)(如Web服務(wù)、數據庫服務(wù)等)本身強相關(guān)。關(guān)于這些信息的收集方法,安全社區已經(jīng)有很多總結,本文不再展開(kāi)講述。
  然而,許多業(yè)務(wù)在云原生化的過(guò)程中,其自身架構或部署形態(tài)也會(huì )發(fā)生變化,引入微服務(wù)治理(如服務(wù)網(wǎng)格)、API治理(如API網(wǎng)關(guān))的特征。這些特征有時(shí)是有價(jià)值的信息,提供了承載業(yè)務(wù)的云原生環(huán)境的一些線(xiàn)索,同樣值得收集。例如,如果我們發(fā)現與服務(wù)交互的HTTP返回頭中包含了x-envoy-開(kāi)頭的項,可以推測該服務(wù)處于一個(gè)由Istio/Envoy進(jìn)行服務(wù)網(wǎng)格管理的云原生環(huán)境中。其中,x-envoy-peer-metadata-id更是包含了服務(wù)所在的Pod、Pod的內部IP和Kubernetes命名空間等重要信息[1]:
  rambo@t-matrix:~$ curl -k https://1.1.1.1 -vv 2>&1 | grepx-envoy-peer-metadata-id< x-envoy-peer-metadata-id:sidecar~2.2.2.2~PodName.Namespace~Namespace.svc.cluster.local
  事實(shí)上,網(wǎng)絡(luò )空間測繪的關(guān)鍵步驟之一就是通過(guò)遠程交互收集信息。我們通過(guò)測繪發(fā)現,Istio、Envoy和Kong等云原生治理程序都會(huì )給被治理的業(yè)務(wù)服務(wù)添加一個(gè)或多個(gè)特征,這些特征對于探索業(yè)務(wù)網(wǎng)絡(luò )拓撲具有積極意義。
  2容器內收集信息
  多見(jiàn)于針對云原生環(huán)境滲透測試的后滲透階段,例如,攻擊者事先通過(guò)Web服務(wù)文件上傳漏洞上傳了一個(gè)Webshell。除此之外,云服務(wù)商提供的CaaS也允許攻擊者作為用戶(hù)直接創(chuàng )建并訪(fǎng)問(wèn)容器。
 ?。?)容器內通過(guò)本地操作收集信息
  雖然起點(diǎn)不同,但這兩個(gè)場(chǎng)景中攻擊者的目的是類(lèi)似的:突破容器到宿主機或其他容器。不過(guò),兩個(gè)場(chǎng)景下攻擊者擁有的初始權限可能不同。隨著(zhù)人們安全意識的增強,許多容器化業(yè)務(wù)已經(jīng)采用Rootless Container部署,業(yè)務(wù)進(jìn)程本身以低權限用戶(hù)(如www-data)運行,這通常也是攻擊者獲得的Webshell的權限;然而,作為CaaS的用戶(hù),攻擊者通??梢該碛腥萜鲀萺oot權限。與前文介紹的訪(fǎng)問(wèn)控制機制類(lèi)似,容器內權限大小對于容器內信息收集也有影響。但是,本文并不單獨討論權限問(wèn)題給信息收集工作帶來(lái)的影響,而是倡導一種“因地制宜”的隨機應變能力。
  “在容器內收集信息”或許是不少朋友看到本文標題后想到的第一個(gè)場(chǎng)景。沒(méi)錯,從容器內部能夠收集到當前環(huán)境的大量信息?!度萜魈右菁夹g(shù)概覽》[2]中曾介紹過(guò)的通過(guò)判定/.dockerenv文件是否存在來(lái)推斷是否處于Docker創(chuàng )建的容器環(huán)境等手法,就是典型的容器內信息收集。
 ?。?)容器內通過(guò)網(wǎng)絡(luò )交互收集信息
  與前文介紹的遠程交互方式相比,容器內的網(wǎng)絡(luò )交互對于攻擊者來(lái)說(shuō)具有獨特優(yōu)勢。因此,我們將這部分內容放在這里,強調“容器內”,而不是在前面一起介紹。這種優(yōu)勢主要有兩個(gè)方面:
  1.訪(fǎng)問(wèn)內部網(wǎng)絡(luò )。容器擁有云原生集群的內部IP,默認配置下還會(huì )有CAP_NET_RAW權限,攻擊者可以通過(guò)網(wǎng)絡(luò )掃描等方式摸清集群內部網(wǎng)絡(luò )拓撲,發(fā)現有價(jià)值的服務(wù),某些場(chǎng)景下甚至能夠訪(fǎng)問(wèn)到節點(diǎn)主機的元數據服務(wù)。這種網(wǎng)絡(luò )可達的優(yōu)勢是值得重視的,外部攻擊者通常需要借助SSRF等手段才能達到相同的目的。
  2.獲得一定權限。云原生集群的容器內可能會(huì )有某種形式的訪(fǎng)問(wèn)憑證,如Pod攜帶的ServiceAccount token等。利用此token可以向Kubernetes API Server發(fā)起訪(fǎng)問(wèn),縱使權限很小,至少不再是“匿名訪(fǎng)問(wèn)”,能夠訪(fǎng)問(wèn)/version獲得版本信息。
  3基于鏡像收集信息
  近年來(lái),軟件供應鏈安全事件頻發(fā),人們的重視程度也日漸提高。容器從鏡像創(chuàng )建而來(lái),就像進(jìn)程從程序創(chuàng )建而來(lái)一樣。因此,依托于鏡像,攻擊者能夠收集到許多有價(jià)值的信息,方式主要有兩種:
  1.利用鏡像和鏡像倉庫收集信息。有時(shí),攻擊者在容器中的權限是有限的,無(wú)法讀寫(xiě)關(guān)鍵文件及其元數據。如果能夠獲取到目標環(huán)境使用的鏡像甚至找到其公開(kāi)的鏡像倉庫,就能夠分析其鏡像組件的脆弱性,找到突破口。
  2.利用特殊鏡像收集運行時(shí)環(huán)境信息。由于runC等容器運行時(shí)的設計問(wèn)題,攻擊者能夠通過(guò)在目標環(huán)境部署特殊鏡像來(lái)獲取環(huán)境中的容器運行時(shí)二進(jìn)制程序文件,進(jìn)而獲得版本信息,發(fā)現潛在脆弱性?!度萜鬟\行時(shí)信息收集技術(shù)介紹》[3]一文對該技術(shù)進(jìn)行了詳細介紹。
  總結
  本文是“深入淺出云原生環(huán)境信息收集技術(shù)”系列的開(kāi)篇,幫助大家梳理了云原生環(huán)境下常見(jiàn)的信息收集方式。有了這些知識作為基礎,我們就能夠逐漸展開(kāi)討論如何在云原生環(huán)境下體系化地收集瑣碎復雜的信息。以攻促防,知攻知防。一起來(lái)守護云原生安全。
  后續文章更加精彩,敬請期待!
  參考文獻
  1. %20CIS%20-%20Attack%20in%20a%20Service%20Mesh%20-%20Public.pptx.pdf
  2.
  3.
  關(guān)于星云實(shí)驗室
  星云實(shí)驗室專(zhuān)注于云計算安全、解決方案研究與虛擬化網(wǎng)絡(luò )安全問(wèn)題研究?;贗aaS環(huán)境的安全防護,利用SDN/NFV等新技術(shù)和新理念,提出了軟件定義安全的云安全防護體系。承擔并完成多個(gè)國家、省、市以及行業(yè)重點(diǎn)單位創(chuàng )新研究課題,已成功孵化落地綠盟科技云安全解決方案。 查看全部

  深入淺出云原生環(huán)境信息收集技術(shù)(一)
  前言
  信息收集在攻擊和防御兩端都是非常重要的一環(huán)。從宏觀(guān)的角度來(lái)說(shuō),大多數信息相關(guān)的工作都可以看作信息收集和信息處理交替進(jìn)行的循環(huán)。優(yōu)質(zhì)的信息收集成果是后續工作順利展開(kāi)的首要條件?!秾O子兵法》有云:故善戰人之勢,如轉圓石于千仞之山者,勢也。在掌握了充足信息后,攻防工作將“如轉圓石于千仞之山”。
  然而,信息的瑣碎性和云原生本身的復雜組成為云原生環(huán)境下的信息收集工作帶來(lái)了一定挑戰。有些朋友也許會(huì )說(shuō),這有何難?比如,執行uname -a命令,就能收集到內核信息了。沒(méi)錯,信息收集確實(shí)是一步步進(jìn)行、一項項完成的。但是,如果只是想當然地進(jìn)行,收集到的信息難免陷于凌亂瑣碎,也很可能不全面。
  對此,筆者結合在攻、防兩端積累的經(jīng)驗,希望與大家探討四個(gè)問(wèn)題:
  1. 站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  2. 站在攻擊者視角,云原生環(huán)境下的信息分類(lèi)維度有哪些?
  3. 站在攻擊者視角,收集到的云原生環(huán)境信息有什么價(jià)值?
  4. 站在攻擊者視角,有沒(méi)有可能阻礙或影響防守者收集信息?
  就“信息收集”這個(gè)話(huà)題而言,毫無(wú)疑問(wèn),防守者是占盡天時(shí)地利的,無(wú)論是能夠收集到的信息種類(lèi)、規模,還是信息收集開(kāi)始的時(shí)間、收集信息所需的權限,都遠遠在攻擊者之上。防守者更需要關(guān)注的是如何使用、分析收集到的信息。因此,我們從攻擊者的角度出發(fā)進(jìn)行探討。這并不意味著(zhù)防守的同學(xué)不需要關(guān)注。相反,只有對攻擊者的技術(shù)了然于胸,才能更好地識別攻擊行為、判定攻擊意圖。
  作為本系列的第一篇文章,本文將展開(kāi)討論第一個(gè)問(wèn)題:站在攻擊者視角,云原生環(huán)境下的收集信息方式有哪些?
  注:文中案例相關(guān)操作均在實(shí)驗環(huán)境中進(jìn)行,相關(guān)技術(shù)僅供研究交流,請勿應用于未授權的滲透測試。
  站在攻擊者視角,云原生環(huán)境下的信息收集方式有哪些?
  思路重在“有章可循”。先有一個(gè)點(diǎn),再進(jìn)行發(fā)散。信息收集方式通常與攻擊場(chǎng)景緊密相關(guān)。在云原生環(huán)境下,攻擊場(chǎng)景通常有三種:
  1.攻擊從遠程發(fā)起。遠程發(fā)起的攻擊十分常見(jiàn),例如,通過(guò)存在未授權訪(fǎng)問(wèn)漏洞的KubernetesAPI Server或DockerDaemon執行命令。
  2.攻擊從容器內發(fā)起。容器內發(fā)起的攻擊通常屬于一次滲透測試的后滲透階段——它的前提是獲得了容器內某種權限的Shell,或者是Containeras a Service(后文簡(jiǎn)稱(chēng)CaaS)的場(chǎng)景——攻擊者本身就是容器服務(wù)的“客戶(hù)”。
  3.依托于鏡像的軟件供應鏈攻擊。包括“鏡像漏洞利用”和“鏡像投毒”等,《云原生安全:攻防實(shí)踐與體系構建》第三章對此進(jìn)行了詳細介紹。
  相應地,信息收集方式主要也有這三種,與攻擊場(chǎng)景相伴而生。讓我們來(lái)一起看一下。
  1通過(guò)遠程交互收集信息
  綜合來(lái)看,云原生環(huán)境中開(kāi)放的遠程服務(wù)主要有兩類(lèi):云原生控制面服務(wù)和容器化業(yè)務(wù)服務(wù)。遠程交互,顧名思義,網(wǎng)絡(luò )可達就行,別無(wú)限制。收集到的信息數量和價(jià)值主要取決于目標的訪(fǎng)問(wèn)控制機制。
 ?。?)從云原生控制面服務(wù)收集信息
  如前所述,如果遇到存在未授權訪(fǎng)問(wèn)漏洞的Kubernetes API Server,不費吹灰之力即可控制整個(gè)云原生集群;如果目標設置了合理的訪(fǎng)問(wèn)控制機制,則獲取到的有價(jià)值信息將大大減少,但也并非毫無(wú)所得。例如,許多Kubernetes API Server允許匿名用戶(hù)訪(fǎng)問(wèn)部分API endpoints。在下面的示例中,攻擊者通過(guò)訪(fǎng)問(wèn)/version,獲得了目標Kubernetes的版本號:
  rambo@t-matrix:~$ curl -k https://1.1.1.1:6443/version{ "major": "1", "minor": "16", "gitVersion": "v1.16.2", "gitCommit": "c97fe5036ef3df2967d086711e6c0c405941e14b", "gitTreeState": "clean", "buildDate": "2019-10-15T19:09:08Z", "goVersion": "go1.12.10", "compiler": "gc", "platform": "linux/amd64"}
  通過(guò)版本匹配,攻擊者能夠判斷目標Kubernetes可能存在哪些漏洞,從而進(jìn)一步利用,這便是版本信息的價(jià)值。
  即使目標設置了非常嚴格的訪(fǎng)問(wèn)控制,攻擊者通常也能夠獲得一些信息。例如,即使訪(fǎng)問(wèn)/version失敗,根據失敗信息我們能夠得知目標是一個(gè)Kubernetes集群,從而利用Kubernetes的背景知識,去探測其他Kubernetes控制面服務(wù)端口(如kubelet、etcd等)。
 ?。?)從容器化業(yè)務(wù)服務(wù)收集信息
  大多數情況下,就信息收集而言,容器化與非容器化業(yè)務(wù)服務(wù)沒(méi)有顯著(zhù)不同之處,收集到的信息均與業(yè)務(wù)服務(wù)(如Web服務(wù)、數據庫服務(wù)等)本身強相關(guān)。關(guān)于這些信息的收集方法,安全社區已經(jīng)有很多總結,本文不再展開(kāi)講述。
  然而,許多業(yè)務(wù)在云原生化的過(guò)程中,其自身架構或部署形態(tài)也會(huì )發(fā)生變化,引入微服務(wù)治理(如服務(wù)網(wǎng)格)、API治理(如API網(wǎng)關(guān))的特征。這些特征有時(shí)是有價(jià)值的信息,提供了承載業(yè)務(wù)的云原生環(huán)境的一些線(xiàn)索,同樣值得收集。例如,如果我們發(fā)現與服務(wù)交互的HTTP返回頭中包含了x-envoy-開(kāi)頭的項,可以推測該服務(wù)處于一個(gè)由Istio/Envoy進(jìn)行服務(wù)網(wǎng)格管理的云原生環(huán)境中。其中,x-envoy-peer-metadata-id更是包含了服務(wù)所在的Pod、Pod的內部IP和Kubernetes命名空間等重要信息[1]:
  rambo@t-matrix:~$ curl -k https://1.1.1.1 -vv 2>&1 | grepx-envoy-peer-metadata-id< x-envoy-peer-metadata-id:sidecar~2.2.2.2~PodName.Namespace~Namespace.svc.cluster.local
  事實(shí)上,網(wǎng)絡(luò )空間測繪的關(guān)鍵步驟之一就是通過(guò)遠程交互收集信息。我們通過(guò)測繪發(fā)現,Istio、Envoy和Kong等云原生治理程序都會(huì )給被治理的業(yè)務(wù)服務(wù)添加一個(gè)或多個(gè)特征,這些特征對于探索業(yè)務(wù)網(wǎng)絡(luò )拓撲具有積極意義。
  2容器內收集信息
  多見(jiàn)于針對云原生環(huán)境滲透測試的后滲透階段,例如,攻擊者事先通過(guò)Web服務(wù)文件上傳漏洞上傳了一個(gè)Webshell。除此之外,云服務(wù)商提供的CaaS也允許攻擊者作為用戶(hù)直接創(chuàng )建并訪(fǎng)問(wèn)容器。
 ?。?)容器內通過(guò)本地操作收集信息
  雖然起點(diǎn)不同,但這兩個(gè)場(chǎng)景中攻擊者的目的是類(lèi)似的:突破容器到宿主機或其他容器。不過(guò),兩個(gè)場(chǎng)景下攻擊者擁有的初始權限可能不同。隨著(zhù)人們安全意識的增強,許多容器化業(yè)務(wù)已經(jīng)采用Rootless Container部署,業(yè)務(wù)進(jìn)程本身以低權限用戶(hù)(如www-data)運行,這通常也是攻擊者獲得的Webshell的權限;然而,作為CaaS的用戶(hù),攻擊者通??梢該碛腥萜鲀萺oot權限。與前文介紹的訪(fǎng)問(wèn)控制機制類(lèi)似,容器內權限大小對于容器內信息收集也有影響。但是,本文并不單獨討論權限問(wèn)題給信息收集工作帶來(lái)的影響,而是倡導一種“因地制宜”的隨機應變能力。
  “在容器內收集信息”或許是不少朋友看到本文標題后想到的第一個(gè)場(chǎng)景。沒(méi)錯,從容器內部能夠收集到當前環(huán)境的大量信息?!度萜魈右菁夹g(shù)概覽》[2]中曾介紹過(guò)的通過(guò)判定/.dockerenv文件是否存在來(lái)推斷是否處于Docker創(chuàng )建的容器環(huán)境等手法,就是典型的容器內信息收集。
 ?。?)容器內通過(guò)網(wǎng)絡(luò )交互收集信息
  與前文介紹的遠程交互方式相比,容器內的網(wǎng)絡(luò )交互對于攻擊者來(lái)說(shuō)具有獨特優(yōu)勢。因此,我們將這部分內容放在這里,強調“容器內”,而不是在前面一起介紹。這種優(yōu)勢主要有兩個(gè)方面:
  1.訪(fǎng)問(wèn)內部網(wǎng)絡(luò )。容器擁有云原生集群的內部IP,默認配置下還會(huì )有CAP_NET_RAW權限,攻擊者可以通過(guò)網(wǎng)絡(luò )掃描等方式摸清集群內部網(wǎng)絡(luò )拓撲,發(fā)現有價(jià)值的服務(wù),某些場(chǎng)景下甚至能夠訪(fǎng)問(wèn)到節點(diǎn)主機的元數據服務(wù)。這種網(wǎng)絡(luò )可達的優(yōu)勢是值得重視的,外部攻擊者通常需要借助SSRF等手段才能達到相同的目的。
  2.獲得一定權限。云原生集群的容器內可能會(huì )有某種形式的訪(fǎng)問(wèn)憑證,如Pod攜帶的ServiceAccount token等。利用此token可以向Kubernetes API Server發(fā)起訪(fǎng)問(wèn),縱使權限很小,至少不再是“匿名訪(fǎng)問(wèn)”,能夠訪(fǎng)問(wèn)/version獲得版本信息。
  3基于鏡像收集信息
  近年來(lái),軟件供應鏈安全事件頻發(fā),人們的重視程度也日漸提高。容器從鏡像創(chuàng )建而來(lái),就像進(jìn)程從程序創(chuàng )建而來(lái)一樣。因此,依托于鏡像,攻擊者能夠收集到許多有價(jià)值的信息,方式主要有兩種:
  1.利用鏡像和鏡像倉庫收集信息。有時(shí),攻擊者在容器中的權限是有限的,無(wú)法讀寫(xiě)關(guān)鍵文件及其元數據。如果能夠獲取到目標環(huán)境使用的鏡像甚至找到其公開(kāi)的鏡像倉庫,就能夠分析其鏡像組件的脆弱性,找到突破口。
  2.利用特殊鏡像收集運行時(shí)環(huán)境信息。由于runC等容器運行時(shí)的設計問(wèn)題,攻擊者能夠通過(guò)在目標環(huán)境部署特殊鏡像來(lái)獲取環(huán)境中的容器運行時(shí)二進(jìn)制程序文件,進(jìn)而獲得版本信息,發(fā)現潛在脆弱性?!度萜鬟\行時(shí)信息收集技術(shù)介紹》[3]一文對該技術(shù)進(jìn)行了詳細介紹。
  總結
  本文是“深入淺出云原生環(huán)境信息收集技術(shù)”系列的開(kāi)篇,幫助大家梳理了云原生環(huán)境下常見(jiàn)的信息收集方式。有了這些知識作為基礎,我們就能夠逐漸展開(kāi)討論如何在云原生環(huán)境下體系化地收集瑣碎復雜的信息。以攻促防,知攻知防。一起來(lái)守護云原生安全。
  后續文章更加精彩,敬請期待!
  參考文獻
  1. %20CIS%20-%20Attack%20in%20a%20Service%20Mesh%20-%20Public.pptx.pdf
  2.
  3.
  關(guān)于星云實(shí)驗室
  星云實(shí)驗室專(zhuān)注于云計算安全、解決方案研究與虛擬化網(wǎng)絡(luò )安全問(wèn)題研究?;贗aaS環(huán)境的安全防護,利用SDN/NFV等新技術(shù)和新理念,提出了軟件定義安全的云安全防護體系。承擔并完成多個(gè)國家、省、市以及行業(yè)重點(diǎn)單位創(chuàng )新研究課題,已成功孵化落地綠盟科技云安全解決方案。

聊聊Spring Boot服務(wù)監控,健康檢查,線(xiàn)程信息,JVM堆信息,指標收集

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 97 次瀏覽 ? 2022-06-02 11:23 ? 來(lái)自相關(guān)話(huà)題

  聊聊Spring Boot服務(wù)監控,健康檢查,線(xiàn)程信息,JVM堆信息,指標收集
  點(diǎn)擊上方“芋道源碼”,選擇“”
  管她前浪,還是后浪?
  能浪的浪,才是好浪!
  每天 10:33更新文章,每天掉億點(diǎn)點(diǎn)頭發(fā)...
  源碼精品專(zhuān)欄
  去年我們項目做了微服務(wù)1.0的架構轉型,但是服務(wù)監控這塊卻沒(méi)有跟上。這不,最近我就被分配了要將我們核心的微服務(wù)應用全部監控起來(lái)的任務(wù)。我們的微服務(wù)應用都是SpringBoot 應用,因此就自然而然的想到了借助Spring Boot 的Actuator 模塊。(沒(méi)吃過(guò)豬肉總聽(tīng)過(guò)豬叫見(jiàn)過(guò)豬跑吧)。
  本篇是我在完成這個(gè)工單之后,對Spring Boot Actuator模塊 學(xué)習應用的總結。在本篇文章中,你可以學(xué)習到:
  之后我還會(huì )介紹:
  推薦下自己做的 Spring Boot 的實(shí)戰項目:
  Spring Boot Actuator 模塊提供了生產(chǎn)級別的功能,比如健康檢查,審計,指標收集,HTTP 跟蹤等,幫助我們監控和管理Spring Boot 應用。這個(gè)模塊是一個(gè)采集應用內部信息暴露給外部的模塊,上述的功能都可以通過(guò)HTTP 和 JMX 訪(fǎng)問(wèn)。
  因為暴露內部信息的特性,Actuator 也可以和一些外部的應用監控系統整合(Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等)。這些監控系統提供了出色的儀表板,圖形,分析和警報,可幫助你通過(guò)一個(gè)統一友好的界面,監視和管理你的應用程序。
  Actuator使用Micrometer與這些外部應用程序監視系統集成。這樣一來(lái),只需很少的配置即可輕松集成外部的監控系統。
  Micrometer 為 Java 平臺上的性能數據收集提供了一個(gè)通用的 API,應用程序只需要使用 Micrometer 的通用 API 來(lái)收集性能指標即可。Micrometer 會(huì )負責完成與不同監控系統的適配工作。這就使得切換監控系統變得很容易。
  對比 Slf4j 之于 Java Logger 中的定位。
  推薦下自己做的 Spring Cloud 的實(shí)戰項目:
  我們先創(chuàng )建一個(gè)demo應用。
  spring?init?-d=web,actuator?-n=actuator-demo?actuator-demo<br /><br />
  image.png
  <br />????...<br />?<br />??org.springframework.boot<br />??spring-boot-starter-actuator<br />?<br />????...<br /><br /><br /><br />
  dependencies?{<br />?compile("org.springframework.boot:spring-boot-starter-actuator")<br />}<br /><br />
  Spring Boot 提供了所謂的 endpoints (下文翻譯為端點(diǎn))給外部來(lái)與應用程序進(jìn)行訪(fǎng)問(wèn)和交互。
  打比方來(lái)說(shuō),/health 端點(diǎn) 提供了關(guān)于應用健康情況的一些基礎信息。metrics 端點(diǎn)提供了一些有用的應用程序指標(JVM 內存使用、系統CPU使用等)。
  這些 Actuator 模塊本來(lái)就有的端點(diǎn)我們稱(chēng)之為原生端點(diǎn)。根據端點(diǎn)的作用的話(huà),我們大概可以分為三大類(lèi):
  詳細的原生端點(diǎn)介紹,請以官網(wǎng)為準,這里就不贅述徒增篇幅。
  需要注意的就是:
  我們可以通過(guò)以下配置,來(lái)配置通過(guò)JMX 和 HTTP 暴露的端點(diǎn)。
  PropertyDefault
  management.endpoints.jmx.exposure.exclude
  management.endpoints.jmx.exposure.include
  *
  management.endpoints.web.exposure.exclude
  management.endpoints.web.exposure.include
  info, healt
  可以打開(kāi)所有的監控點(diǎn)
  management.endpoints.web.exposure.include=*<br /><br />
  也可以選擇打開(kāi)部分,"*" 代表暴露所有的端點(diǎn),如果指定多個(gè)端點(diǎn),用","分開(kāi)
  management.endpoints.web.exposure.exclude=beans,trace<br /><br />
  Actuator 默認所有的監控點(diǎn)路徑都在/actuator/*,當然如果有需要這個(gè)路徑也支持定制。
  management.endpoints.web.base-path=/minitor<br /><br />
  設置完重啟后,再次訪(fǎng)問(wèn)地址就會(huì )變成/minitor/*。
  現在我們按照如下配置:
  #?"*"?代表暴露所有的端點(diǎn)?如果指定多個(gè)端點(diǎn),用","分開(kāi)<br />management.endpoints.web.exposure.include=*<br />#?賦值規則同上<br />management.endpoints.web.exposure.exclude=<br /><br />
  啟動(dòng)DEMO程序,訪(fǎng)問(wèn):8080/actuator,查看暴露出來(lái)的端點(diǎn):
  image.png
  上面這樣顯示是因為chrome 瀏覽器安裝了 JSON-handle 插件,實(shí)際上就是返回一大段json
  下面,我會(huì )著(zhù)重介紹幾個(gè)比較重要的端點(diǎn)。
  /health端點(diǎn)會(huì )聚合你程序的健康指標,來(lái)檢查程序的健康情況。端點(diǎn)公開(kāi)的應用健康信息取決于:
  management.endpoint.health.show-details=always<br /><br />
  該屬性可以使用以下值之一進(jìn)行配置:
  NameDescription
  never
  不展示詳細信息,up或者down的狀態(tài),默認配置
  when-authorized
  詳細信息將會(huì )展示給通過(guò)認證的用戶(hù)。授權的角色可以通過(guò)management.endpoint.health.roles配置
  always
  對所有用戶(hù)暴露詳細信息
  按照上述配置,配置成always之后,我們啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/health端口,可以看到這樣的信息:
  image.png
  是不是感覺(jué)好像健康信息有點(diǎn)少?先別急,那是因為我們創(chuàng )建的是一個(gè)最基礎的Demo項目,沒(méi)有依賴(lài)很多的組件。
  /health端點(diǎn)有很多自動(dòng)配置的健康指示器:如redis、rabbitmq、db等組件。當你的項目有依賴(lài)對應組件的時(shí)候,這些健康指示器就會(huì )被自動(dòng)裝配,繼而采集對應的信息。如上面的 diskSpace 節點(diǎn)信息就是DiskSpaceHealthIndicator 在起作用。
  
  image.png
  上述截圖取自官方文檔
  這是我另一個(gè)項目的/health端點(diǎn)信息。
  image.png
  當如上的組件有一個(gè)狀態(tài)異常,應用服務(wù)的整體狀態(tài)即為down。我們也可以通過(guò)配置禁用某個(gè)組件的健康監測。
  management.health.mongo.enabled:?false<br /><br />
  或者禁用所有自動(dòng)配置的健康指示器:
  management.health.defaults.enabled:?false<br /><br />
  當然你也可以自定義一個(gè)Health Indicator,只需要實(shí)現HealthIndicator 接口或者繼承AbstractHealthIndicator類(lèi)。
  /**<br />?*?@author?Richard_yyf<br />?*?@version?1.0?2020/1/16<br />?*/<br />@Component<br />public?class?CustomHealthIndicator?extends?AbstractHealthIndicator?{<br /><br />????@Override<br />????protected?void?doHealthCheck(Health.Builder?builder)?throws?Exception?{<br />????????//?使用?builder?來(lái)創(chuàng )建健康狀態(tài)信息<br />????????//?如果你throw?了一個(gè)?exception,那么status?就會(huì )被置為DOWN,異常信息會(huì )被記錄下來(lái)<br />????????builder.up()<br />????????????????.withDetail("app",?"這個(gè)項目很健康")<br />????????????????.withDetail("error",?"Nothing,?I'm?very?good");<br />????}<br />}<br /><br />
  最終效果:
  image.png
  /metrics端點(diǎn)用來(lái)返回當前應用的各類(lèi)重要度量指標,比如:內存信息、線(xiàn)程信息、垃圾回收信息、tomcat、數據庫連接池等。
  {<br />????"names":?[<br />????????"tomcat.threads.busy",<br />????????"jvm.threads.states",<br />????????"jdbc.connections.active",<br />????????"jvm.gc.memory.promoted",<br />????????"http.server.requests",<br />????????"hikaricp.connections.max",<br />????????"hikaricp.connections.min",<br />????????"jvm.memory.used",<br />????????"jvm.gc.max.data.size",<br />????????"jdbc.connections.max",<br />?????????....<br />????]<br />}<br /><br />
  不同于1.x,Actuator在這個(gè)界面看不到具體的指標信息,只是展示了一個(gè)指標列表。 為了獲取到某個(gè)指標的詳細信息,我們可以請求具體的指標信息,像這樣:
  http://localhost:8080/actuator/metrics/{MetricName}<br /><br />
  比如我訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max,返回信息如下:
  image.png
  你也可以用query param的方式查看單獨的一塊區域。比如你可以訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max?tag=id:Metaspace。結果就是:
  image.png
  /loggers 端點(diǎn)暴露了我們程序內部配置的所有logger的信息。我們訪(fǎng)問(wèn)/actuator/loggers可以看到,
  image.png
  你也可以通過(guò)下述方式訪(fǎng)問(wèn)單獨一個(gè)logger,
  http://localhost:8080/actuator/loggers/{name}<br /><br />
  比如我現在訪(fǎng)問(wèn) root logger,:8080/actuator/loggers/root
  {<br />????"configuredLevel":?"INFO",<br />????"effectiveLevel":?"INFO"<br />}<br /><br />
  /loggers端點(diǎn)我最想提的就是這個(gè)功能,能夠動(dòng)態(tài)修改你的日志等級。
  比如,我們可以通過(guò)下述方式來(lái)修改 root logger的日志等級。我們只需要發(fā)起一個(gè)URL 為:8080/actuator/loggers/root的POST請求,POST報文如下:
  {<br />???"configuredLevel":?"DEBUG"<br />}<br /><br />
  image.png
  仔細想想,這個(gè)功能是不是非常有用。如果在生產(chǎn)環(huán)境中,你想要你的應用輸出一些Debug信息以便于你診斷一些異常情況,你你只需要按照上述方式就可以修改,而不需要重啟應用。
  如果想重置成默認值,把value 改成 null
  /info端點(diǎn)可以用來(lái)展示你程序的信息。我理解過(guò)來(lái)就是一些程序的基礎信息。并且你可以按照自己的需求在配置文件application.properties中個(gè)性化配置(默認情況下,該端點(diǎn)只會(huì )返回一個(gè)空的json內容。):
  info.app.name=actuator-test-demo<br />info.app.encoding=UTF-8<br />info.app.java.source=1.8<br />info.app.java.target=1.8<br />#?在?maven?項目中你可以直接用下列方式引用?maven?properties的值<br />#?info.app.encoding=@project.build.sourceEncoding@<br />#?info.app.java.source=@java.version@<br />#?info.app.java.target=@java.version@<br /><br />
  啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/info:
  {<br />????"app":?{<br />????????"encoding":?"UTF-8",<br />????????"java":?{<br />????????????"source":?"1.8.0_131",<br />????????????"target":?"1.8.0_131"<br />????????},<br />????????"name":?"actuator-test-demo"<br />????}<br />}<br /><br />
  /beans端點(diǎn)會(huì )返回Spring 容器中所有bean的別名、類(lèi)型、是否單例、依賴(lài)等信息。
  訪(fǎng)問(wèn):8080/actuator/beans,返回如下:
  image.png
  訪(fǎng)問(wèn)::8080/actuator/heapdump會(huì )自動(dòng)生成一個(gè) Jvm 的堆文件 heapdump。我們可以使用 JDK 自帶的 Jvm 監控工具 VisualVM 打開(kāi)此文件查看內存快照。
  image.png
  這個(gè)端點(diǎn)我個(gè)人覺(jué)得特別有用,方便我們在日常定位問(wèn)題的時(shí)候查看線(xiàn)程的情況。主要展示了線(xiàn)程名、線(xiàn)程ID、線(xiàn)程的狀態(tài)、是否等待鎖資源、線(xiàn)程堆棧等信息。就是可能查看起來(lái)不太直觀(guān)。訪(fǎng)問(wèn):8080/actuator/threaddump返回如下:
  image.png
  這個(gè)端點(diǎn)屬于操作控制類(lèi)端點(diǎn),可以?xún)?yōu)雅關(guān)閉 Spring Boot 應用。要使用這個(gè)功能首先需要在配置文件中開(kāi)啟:
  management.endpoint.shutdown.enabled=true<br /><br />
  由于 shutdown 接口默認只支持 POST 請求 ,我們啟動(dòng)Demo項目,向:8080/actuator/shutdown發(fā)起POST請求。返回信息:
  {<br />????"message":?"Shutting?down,?bye..."<br />}<br /><br />
  然后應用程序被關(guān)閉。
  由于開(kāi)放關(guān)閉應用的操作本身是一件非常危險 的事,所以真正在線(xiàn)上使用的時(shí)候,我們需要對其加入一定的保護機制,比如:定制Actuator的端點(diǎn)路徑、整合Spring Security進(jìn)行安全校驗 等。(不是特別必要的話(huà),這個(gè)端點(diǎn)不用開(kāi))
  由于端點(diǎn)的信息和產(chǎn)生的交互都是非常敏感的,必須防止未經(jīng)授權的外部訪(fǎng)問(wèn)。如果您的應用程序中存在Spring Security 的依賴(lài),則默認情況下使用基于表單的HTTP身份驗證 來(lái)保護端點(diǎn)。
  如果沒(méi)有,只需要增加對應的依賴(lài)即可:
  <br />???org.springframework.boot<br />???spring-boot-starter-security<br /><br /><br />
  添加之后,我們需要定義安全校驗規則,來(lái)覆蓋Spring Security 的默認配置。
  這里我給出了兩個(gè)版本的模板配置:
  import?org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;<br />import?org.springframework.boot.actuate.context.ShutdownEndpoint;<br />import?org.springframework.boot.autoconfigure.security.servlet.PathRequest;<br />import?org.springframework.context.annotation.Configuration;<br />import?org.springframework.security.config.annotation.web.builders.HttpSecurity;<br />import?org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;<br /><br />/**<br />?*?@author?Richard_yyf<br />?*/<br />@Configuration<br />public?class?ActuatorSecurityConfig?extends?WebSecurityConfigurerAdapter?{<br /><br />????/*<br />?????*?version1:<br />?????*?1.?限制?'/shutdown'端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)其他的端點(diǎn)<br />?????*?3.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?4.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?5.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*?version2:<br />?????*?1.?限制所有端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?3.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?4.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*/<br /><br />????@Override<br />????protected?void?configure(HttpSecurity?http)?throws?Exception?{<br />????????//?version1<br />//????????http<br />//????????????????.authorizeRequests()<br />//????????????????????.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))<br />//????????????????????????.hasRole("ACTUATOR_ADMIN")<br />//????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />//????????????????????.permitAll()<br />//????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/")<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/**")<br />//????????????????????.authenticated()<br />//????????????????.and()<br />//????????????????.httpBasic();<br /><br />????????//?version2<br />????????http<br />????????????????.authorizeRequests()<br />????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />????????????????????.hasRole("ACTUATOR_ADMIN")<br />????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />????????????????????.permitAll()<br />????????????????.antMatchers("/")<br />????????????????????.permitAll()<br />????????????????.antMatchers("/**")<br />????????????????????.authenticated()<br />????????????????.and()<br />????????????????.httpBasic();<br />????}<br />}<br /><br />
  application.properties的相關(guān)配置如下:
  #?Spring?Security?Default?user?name?and?password<br />spring.security.user.name=actuator<br />spring.security.user.password=actuator<br />spring.security.user.roles=ACTUATOR_ADMIN<br />
  - END -
  歡迎加入我的知識星球,一起探討架構,交流源碼。加入方式,長(cháng)按下方二維碼噢:
  
  已在知識星球更新源碼解析如下:
  最近更新《芋道 SpringBoot 2.X 入門(mén)》系列,已經(jīng) 101 余篇,覆蓋了MyBatis、Redis、MongoDB、ES、分庫分表、讀寫(xiě)分離、SpringMVC、Webflux、權限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能測試等等內容。
  提供近 3W 行代碼的 SpringBoot 示例,以及超 4W 行代碼的電商微服務(wù)項目。 查看全部

  聊聊Spring Boot服務(wù)監控,健康檢查,線(xiàn)程信息,JVM堆信息,指標收集
  點(diǎn)擊上方“芋道源碼”,選擇“”
  管她前浪,還是后浪?
  能浪的浪,才是好浪!
  每天 10:33更新文章,每天掉億點(diǎn)點(diǎn)頭發(fā)...
  源碼精品專(zhuān)欄
  去年我們項目做了微服務(wù)1.0的架構轉型,但是服務(wù)監控這塊卻沒(méi)有跟上。這不,最近我就被分配了要將我們核心的微服務(wù)應用全部監控起來(lái)的任務(wù)。我們的微服務(wù)應用都是SpringBoot 應用,因此就自然而然的想到了借助Spring Boot 的Actuator 模塊。(沒(méi)吃過(guò)豬肉總聽(tīng)過(guò)豬叫見(jiàn)過(guò)豬跑吧)。
  本篇是我在完成這個(gè)工單之后,對Spring Boot Actuator模塊 學(xué)習應用的總結。在本篇文章中,你可以學(xué)習到:
  之后我還會(huì )介紹:
  推薦下自己做的 Spring Boot 的實(shí)戰項目:
  Spring Boot Actuator 模塊提供了生產(chǎn)級別的功能,比如健康檢查,審計,指標收集,HTTP 跟蹤等,幫助我們監控和管理Spring Boot 應用。這個(gè)模塊是一個(gè)采集應用內部信息暴露給外部的模塊,上述的功能都可以通過(guò)HTTP 和 JMX 訪(fǎng)問(wèn)。
  因為暴露內部信息的特性,Actuator 也可以和一些外部的應用監控系統整合(Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等)。這些監控系統提供了出色的儀表板,圖形,分析和警報,可幫助你通過(guò)一個(gè)統一友好的界面,監視和管理你的應用程序。
  Actuator使用Micrometer與這些外部應用程序監視系統集成。這樣一來(lái),只需很少的配置即可輕松集成外部的監控系統。
  Micrometer 為 Java 平臺上的性能數據收集提供了一個(gè)通用的 API,應用程序只需要使用 Micrometer 的通用 API 來(lái)收集性能指標即可。Micrometer 會(huì )負責完成與不同監控系統的適配工作。這就使得切換監控系統變得很容易。
  對比 Slf4j 之于 Java Logger 中的定位。
  推薦下自己做的 Spring Cloud 的實(shí)戰項目:
  我們先創(chuàng )建一個(gè)demo應用。
  spring?init?-d=web,actuator?-n=actuator-demo?actuator-demo<br /><br />
  image.png
  <br />????...<br />?<br />??org.springframework.boot<br />??spring-boot-starter-actuator<br />?<br />????...<br /><br /><br /><br />
  dependencies?{<br />?compile("org.springframework.boot:spring-boot-starter-actuator")<br />}<br /><br />
  Spring Boot 提供了所謂的 endpoints (下文翻譯為端點(diǎn))給外部來(lái)與應用程序進(jìn)行訪(fǎng)問(wèn)和交互。
  打比方來(lái)說(shuō),/health 端點(diǎn) 提供了關(guān)于應用健康情況的一些基礎信息。metrics 端點(diǎn)提供了一些有用的應用程序指標(JVM 內存使用、系統CPU使用等)。
  這些 Actuator 模塊本來(lái)就有的端點(diǎn)我們稱(chēng)之為原生端點(diǎn)。根據端點(diǎn)的作用的話(huà),我們大概可以分為三大類(lèi):
  詳細的原生端點(diǎn)介紹,請以官網(wǎng)為準,這里就不贅述徒增篇幅。
  需要注意的就是:
  我們可以通過(guò)以下配置,來(lái)配置通過(guò)JMX 和 HTTP 暴露的端點(diǎn)。
  PropertyDefault
  management.endpoints.jmx.exposure.exclude
  management.endpoints.jmx.exposure.include
  *
  management.endpoints.web.exposure.exclude
  management.endpoints.web.exposure.include
  info, healt
  可以打開(kāi)所有的監控點(diǎn)
  management.endpoints.web.exposure.include=*<br /><br />
  也可以選擇打開(kāi)部分,"*" 代表暴露所有的端點(diǎn),如果指定多個(gè)端點(diǎn),用","分開(kāi)
  management.endpoints.web.exposure.exclude=beans,trace<br /><br />
  Actuator 默認所有的監控點(diǎn)路徑都在/actuator/*,當然如果有需要這個(gè)路徑也支持定制。
  management.endpoints.web.base-path=/minitor<br /><br />
  設置完重啟后,再次訪(fǎng)問(wèn)地址就會(huì )變成/minitor/*。
  現在我們按照如下配置:
  #?"*"?代表暴露所有的端點(diǎn)?如果指定多個(gè)端點(diǎn),用","分開(kāi)<br />management.endpoints.web.exposure.include=*<br />#?賦值規則同上<br />management.endpoints.web.exposure.exclude=<br /><br />
  啟動(dòng)DEMO程序,訪(fǎng)問(wèn):8080/actuator,查看暴露出來(lái)的端點(diǎn):
  image.png
  上面這樣顯示是因為chrome 瀏覽器安裝了 JSON-handle 插件,實(shí)際上就是返回一大段json
  下面,我會(huì )著(zhù)重介紹幾個(gè)比較重要的端點(diǎn)。
  /health端點(diǎn)會(huì )聚合你程序的健康指標,來(lái)檢查程序的健康情況。端點(diǎn)公開(kāi)的應用健康信息取決于:
  management.endpoint.health.show-details=always<br /><br />
  該屬性可以使用以下值之一進(jìn)行配置:
  NameDescription
  never
  不展示詳細信息,up或者down的狀態(tài),默認配置
  when-authorized
  詳細信息將會(huì )展示給通過(guò)認證的用戶(hù)。授權的角色可以通過(guò)management.endpoint.health.roles配置
  always
  對所有用戶(hù)暴露詳細信息
  按照上述配置,配置成always之后,我們啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/health端口,可以看到這樣的信息:
  image.png
  是不是感覺(jué)好像健康信息有點(diǎn)少?先別急,那是因為我們創(chuàng )建的是一個(gè)最基礎的Demo項目,沒(méi)有依賴(lài)很多的組件。
  /health端點(diǎn)有很多自動(dòng)配置的健康指示器:如redis、rabbitmq、db等組件。當你的項目有依賴(lài)對應組件的時(shí)候,這些健康指示器就會(huì )被自動(dòng)裝配,繼而采集對應的信息。如上面的 diskSpace 節點(diǎn)信息就是DiskSpaceHealthIndicator 在起作用。
  
  image.png
  上述截圖取自官方文檔
  這是我另一個(gè)項目的/health端點(diǎn)信息。
  image.png
  當如上的組件有一個(gè)狀態(tài)異常,應用服務(wù)的整體狀態(tài)即為down。我們也可以通過(guò)配置禁用某個(gè)組件的健康監測。
  management.health.mongo.enabled:?false<br /><br />
  或者禁用所有自動(dòng)配置的健康指示器:
  management.health.defaults.enabled:?false<br /><br />
  當然你也可以自定義一個(gè)Health Indicator,只需要實(shí)現HealthIndicator 接口或者繼承AbstractHealthIndicator類(lèi)。
  /**<br />?*?@author?Richard_yyf<br />?*?@version?1.0?2020/1/16<br />?*/<br />@Component<br />public?class?CustomHealthIndicator?extends?AbstractHealthIndicator?{<br /><br />????@Override<br />????protected?void?doHealthCheck(Health.Builder?builder)?throws?Exception?{<br />????????//?使用?builder?來(lái)創(chuàng )建健康狀態(tài)信息<br />????????//?如果你throw?了一個(gè)?exception,那么status?就會(huì )被置為DOWN,異常信息會(huì )被記錄下來(lái)<br />????????builder.up()<br />????????????????.withDetail("app",?"這個(gè)項目很健康")<br />????????????????.withDetail("error",?"Nothing,?I'm?very?good");<br />????}<br />}<br /><br />
  最終效果:
  image.png
  /metrics端點(diǎn)用來(lái)返回當前應用的各類(lèi)重要度量指標,比如:內存信息、線(xiàn)程信息、垃圾回收信息、tomcat、數據庫連接池等。
  {<br />????"names":?[<br />????????"tomcat.threads.busy",<br />????????"jvm.threads.states",<br />????????"jdbc.connections.active",<br />????????"jvm.gc.memory.promoted",<br />????????"http.server.requests",<br />????????"hikaricp.connections.max",<br />????????"hikaricp.connections.min",<br />????????"jvm.memory.used",<br />????????"jvm.gc.max.data.size",<br />????????"jdbc.connections.max",<br />?????????....<br />????]<br />}<br /><br />
  不同于1.x,Actuator在這個(gè)界面看不到具體的指標信息,只是展示了一個(gè)指標列表。 為了獲取到某個(gè)指標的詳細信息,我們可以請求具體的指標信息,像這樣:
  http://localhost:8080/actuator/metrics/{MetricName}<br /><br />
  比如我訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max,返回信息如下:
  image.png
  你也可以用query param的方式查看單獨的一塊區域。比如你可以訪(fǎng)問(wèn)/actuator/metrics/jvm.memory.max?tag=id:Metaspace。結果就是:
  image.png
  /loggers 端點(diǎn)暴露了我們程序內部配置的所有logger的信息。我們訪(fǎng)問(wèn)/actuator/loggers可以看到,
  image.png
  你也可以通過(guò)下述方式訪(fǎng)問(wèn)單獨一個(gè)logger,
  http://localhost:8080/actuator/loggers/{name}<br /><br />
  比如我現在訪(fǎng)問(wèn) root logger,:8080/actuator/loggers/root
  {<br />????"configuredLevel":?"INFO",<br />????"effectiveLevel":?"INFO"<br />}<br /><br />
  /loggers端點(diǎn)我最想提的就是這個(gè)功能,能夠動(dòng)態(tài)修改你的日志等級。
  比如,我們可以通過(guò)下述方式來(lái)修改 root logger的日志等級。我們只需要發(fā)起一個(gè)URL 為:8080/actuator/loggers/root的POST請求,POST報文如下:
  {<br />???"configuredLevel":?"DEBUG"<br />}<br /><br />
  image.png
  仔細想想,這個(gè)功能是不是非常有用。如果在生產(chǎn)環(huán)境中,你想要你的應用輸出一些Debug信息以便于你診斷一些異常情況,你你只需要按照上述方式就可以修改,而不需要重啟應用。
  如果想重置成默認值,把value 改成 null
  /info端點(diǎn)可以用來(lái)展示你程序的信息。我理解過(guò)來(lái)就是一些程序的基礎信息。并且你可以按照自己的需求在配置文件application.properties中個(gè)性化配置(默認情況下,該端點(diǎn)只會(huì )返回一個(gè)空的json內容。):
  info.app.name=actuator-test-demo<br />info.app.encoding=UTF-8<br />info.app.java.source=1.8<br />info.app.java.target=1.8<br />#?在?maven?項目中你可以直接用下列方式引用?maven?properties的值<br />#?info.app.encoding=@project.build.sourceEncoding@<br />#?info.app.java.source=@java.version@<br />#?info.app.java.target=@java.version@<br /><br />
  啟動(dòng)項目,訪(fǎng)問(wèn):8080/actuator/info:
  {<br />????"app":?{<br />????????"encoding":?"UTF-8",<br />????????"java":?{<br />????????????"source":?"1.8.0_131",<br />????????????"target":?"1.8.0_131"<br />????????},<br />????????"name":?"actuator-test-demo"<br />????}<br />}<br /><br />
  /beans端點(diǎn)會(huì )返回Spring 容器中所有bean的別名、類(lèi)型、是否單例、依賴(lài)等信息。
  訪(fǎng)問(wèn):8080/actuator/beans,返回如下:
  image.png
  訪(fǎng)問(wèn)::8080/actuator/heapdump會(huì )自動(dòng)生成一個(gè) Jvm 的堆文件 heapdump。我們可以使用 JDK 自帶的 Jvm 監控工具 VisualVM 打開(kāi)此文件查看內存快照。
  image.png
  這個(gè)端點(diǎn)我個(gè)人覺(jué)得特別有用,方便我們在日常定位問(wèn)題的時(shí)候查看線(xiàn)程的情況。主要展示了線(xiàn)程名、線(xiàn)程ID、線(xiàn)程的狀態(tài)、是否等待鎖資源、線(xiàn)程堆棧等信息。就是可能查看起來(lái)不太直觀(guān)。訪(fǎng)問(wèn):8080/actuator/threaddump返回如下:
  image.png
  這個(gè)端點(diǎn)屬于操作控制類(lèi)端點(diǎn),可以?xún)?yōu)雅關(guān)閉 Spring Boot 應用。要使用這個(gè)功能首先需要在配置文件中開(kāi)啟:
  management.endpoint.shutdown.enabled=true<br /><br />
  由于 shutdown 接口默認只支持 POST 請求 ,我們啟動(dòng)Demo項目,向:8080/actuator/shutdown發(fā)起POST請求。返回信息:
  {<br />????"message":?"Shutting?down,?bye..."<br />}<br /><br />
  然后應用程序被關(guān)閉。
  由于開(kāi)放關(guān)閉應用的操作本身是一件非常危險 的事,所以真正在線(xiàn)上使用的時(shí)候,我們需要對其加入一定的保護機制,比如:定制Actuator的端點(diǎn)路徑、整合Spring Security進(jìn)行安全校驗 等。(不是特別必要的話(huà),這個(gè)端點(diǎn)不用開(kāi))
  由于端點(diǎn)的信息和產(chǎn)生的交互都是非常敏感的,必須防止未經(jīng)授權的外部訪(fǎng)問(wèn)。如果您的應用程序中存在Spring Security 的依賴(lài),則默認情況下使用基于表單的HTTP身份驗證 來(lái)保護端點(diǎn)。
  如果沒(méi)有,只需要增加對應的依賴(lài)即可:
  <br />???org.springframework.boot<br />???spring-boot-starter-security<br /><br /><br />
  添加之后,我們需要定義安全校驗規則,來(lái)覆蓋Spring Security 的默認配置。
  這里我給出了兩個(gè)版本的模板配置:
  import?org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;<br />import?org.springframework.boot.actuate.context.ShutdownEndpoint;<br />import?org.springframework.boot.autoconfigure.security.servlet.PathRequest;<br />import?org.springframework.context.annotation.Configuration;<br />import?org.springframework.security.config.annotation.web.builders.HttpSecurity;<br />import?org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;<br /><br />/**<br />?*?@author?Richard_yyf<br />?*/<br />@Configuration<br />public?class?ActuatorSecurityConfig?extends?WebSecurityConfigurerAdapter?{<br /><br />????/*<br />?????*?version1:<br />?????*?1.?限制?'/shutdown'端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)其他的端點(diǎn)<br />?????*?3.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?4.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?5.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*?version2:<br />?????*?1.?限制所有端點(diǎn)的訪(fǎng)問(wèn),只允許ACTUATOR_ADMIN訪(fǎng)問(wèn)<br />?????*?2.?允許外部訪(fǎng)問(wèn)靜態(tài)資源<br />?????*?3.?允許外部訪(fǎng)問(wèn)?'/'<br />?????*?4.?其他的訪(fǎng)問(wèn)需要被校驗<br />?????*/<br /><br />????@Override<br />????protected?void?configure(HttpSecurity?http)?throws?Exception?{<br />????????//?version1<br />//????????http<br />//????????????????.authorizeRequests()<br />//????????????????????.requestMatchers(EndpointRequest.to(ShutdownEndpoint.class))<br />//????????????????????????.hasRole("ACTUATOR_ADMIN")<br />//????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />//????????????????????.permitAll()<br />//????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/")<br />//????????????????????.permitAll()<br />//????????????????.antMatchers("/**")<br />//????????????????????.authenticated()<br />//????????????????.and()<br />//????????????????.httpBasic();<br /><br />????????//?version2<br />????????http<br />????????????????.authorizeRequests()<br />????????????????.requestMatchers(EndpointRequest.toAnyEndpoint())<br />????????????????????.hasRole("ACTUATOR_ADMIN")<br />????????????????.requestMatchers(PathRequest.toStaticResources().atCommonLocations())<br />????????????????????.permitAll()<br />????????????????.antMatchers("/")<br />????????????????????.permitAll()<br />????????????????.antMatchers("/**")<br />????????????????????.authenticated()<br />????????????????.and()<br />????????????????.httpBasic();<br />????}<br />}<br /><br />
  application.properties的相關(guān)配置如下:
  #?Spring?Security?Default?user?name?and?password<br />spring.security.user.name=actuator<br />spring.security.user.password=actuator<br />spring.security.user.roles=ACTUATOR_ADMIN<br />
  - END -
  歡迎加入我的知識星球,一起探討架構,交流源碼。加入方式,長(cháng)按下方二維碼噢:
  
  已在知識星球更新源碼解析如下:
  最近更新《芋道 SpringBoot 2.X 入門(mén)》系列,已經(jīng) 101 余篇,覆蓋了MyBatis、Redis、MongoDB、ES、分庫分表、讀寫(xiě)分離、SpringMVC、Webflux、權限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能測試等等內容。
  提供近 3W 行代碼的 SpringBoot 示例,以及超 4W 行代碼的電商微服務(wù)項目。

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 161 次瀏覽 ? 2022-05-29 05:57 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 91 次瀏覽 ? 2022-05-28 21:46 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

springcloud微服務(wù)實(shí)踐:天氣數據API微服務(wù)的實(shí)現

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 69 次瀏覽 ? 2022-05-27 20:42 ? 來(lái)自相關(guān)話(huà)題

  springcloud微服務(wù)實(shí)踐:天氣數據API微服務(wù)的實(shí)現
  天氣數據API 微服務(wù)的實(shí)現
  天氣數據API微服務(wù)包含了天氣數據查詢(xún)組件。天氣數據查詢(xún)組件提供了天氣數據查詢(xún)的接口。
  我們的數據已經(jīng)通過(guò)天氣數據采集微服務(wù)集成到了Redis 存儲中,天氣數據API微服務(wù)只需要從Redis獲取數據,而后從接口中暴露出去即可。
  在micro-weather-report應用的基礎上,我們將對其進(jìn)行逐步的拆分,形成一個(gè)新的微服務(wù)msa-weather-data-server應用。
  
  所需環(huán)境
  為了演示本例子,需要采用如下開(kāi)發(fā)環(huán)境。
  修改天氣數據服務(wù)接口及實(shí)現
  在com.waylau.spring.cloud. weather.service包下,我們之前已經(jīng)定義了該應用的天氣數據服務(wù)接口WeatherDataService。
  public interface WeatherDataService {<br />*★<br />*根據城市ID查詢(xún)天氣數據<br />@param cityId<br />@return <br />WeatherResponse getDataByCityId(String cityId);<br />/**<br />★根據城市名稱(chēng)查詢(xún)天氣數據<br />* @param cityId<br />* Creturn<br />*/<br />Wea therResponse getDataByCityName (String cityName) ;<br />}
  對于該微服務(wù)而言,我們并不需要同步天氣的業(yè)務(wù)需求,所以把之前定義的syncDataByCityId方法刪除了。
  WeatherDataServicelmpl是對WeatherDataService 接口的實(shí)現,也要做出相應的調整,將同步天氣的代碼邏輯都刪除,保留以下代碼。
  package com. way1au . spr ing.cloud.weather .service;<br />import java. io. IOException;<br />import org.slf4j . Logger;<br />import org.slf4j. LoggerFactory;<br />import org.springf ramework. beans. factory . annotation.Autowired;<br />import org. springfr amework. data. redis. core. StringRedisTemplate;<br />import org. springf ramework. data. redis.core. ValueOperations;<br />import org. springf ramework. stereotype. Service;<br />import com. fasterxml. jackson.databind. objectMapper;<br />import com. waylau. spring. cloud . weather . vo . Wea therResponse;<br />/**<br />k天氣數據服務(wù).<br />@since 1.0.0 2017年10月29日 <br />* @author Way Lau<br />@Service<br />public class WeatherDataServiceImpl implements WeatherDataService {<br />private final static Logger logger = LoggerFactory .getLogger (Weather<br />DataServiceImpl.class) ;<br />@Autowi red<br />private StringRedisTemplate stringRedisTemplate; <br />private final String WEATHER API = "http://wthrcdn. etouch . cn/weather_<br />mini";<br />@Override<br />public WeatherResponse getDa taByCityId(String cityId) {<br />String uri = WEATHER API + "?citykey=" + cityId;<br />return this. doGetWea therData (uri) ;<br />@Override<br />public WeatherResponse getDataByCityName (String cityName) {<br />String uri = WEATHER_ API + "?city=" + cityName ;<br />return this. doGe tWeatherData (uri) ;<br />private WeatherResponse doGetWea therData (String uri)<br />valueOperations ops = this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />/先查緩存,查不到拋出異常<br />if (!this. stringRedisTemplate . hasKey (key)) {<br />logger .error("不存在key "+ key) ;<br />throw new Runt imeException ("沒(méi)有相應的天氣信息") ;<br />} else {<br />logger.info("存在key"+ key + ", value=" + ops.get (key));<br />strBody = ops.get (key) ;<br />}<br />0bj ectMapper mapper = new ObjectMapper () ;<br />WeatherResponse weather = null;<br />try {<br />weather = mapper . readvalue (strBody, WeatherResponse.class) ;<br />} catch (IOException e) {<br />logger . error ("JSON反序列化異常! ",e);<br />throw new RuntimeException ("天氣信息解析失敗") ;<br />return weather;<br />}<br />}
  其中需要注意的是:
  ●原有的RestTemplate用作REST客戶(hù)端來(lái)進(jìn)行天氣數據的同步,這個(gè)類(lèi)相關(guān)的代碼都可以刪除了;
  ●服務(wù) 會(huì )先從緩存中進(jìn)行查詢(xún),查不到數據就拋出異常(有可能該城市的天氣數據未同步,或者是數據已經(jīng)過(guò)期) ;
  在執行反序列化JSON過(guò)程中也可能遭遇異常,同樣將異常信息拋出。
  除上述WeatherDataServicelmpl、WeatherDataService 外,其他服務(wù)層的代碼都可以刪除了。
  調整控制層的代碼
  除了WeatherController 外,其他控制層的代碼都不需要了。
  WeatherController仍然是原有的代碼保持不變。
  
  刪除配置類(lèi)、天氣數據同步任務(wù)和工具類(lèi)
  配置類(lèi)RestConfiguration、QuartzConfiguration 及任務(wù)類(lèi)WeatherDataSyncJob、 工具類(lèi)Xml-
  Builder的代碼都可以刪除了。
  清理值對象
  值對象我們需要保留解析天氣相關(guān)的類(lèi)即可,其他值對象(如City. CityList等)都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并 刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并 刪除。
  最后,要清理Thymeleaf在application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。 查看全部

  springcloud微服務(wù)實(shí)踐:天氣數據API微服務(wù)的實(shí)現
  天氣數據API 微服務(wù)的實(shí)現
  天氣數據API微服務(wù)包含了天氣數據查詢(xún)組件。天氣數據查詢(xún)組件提供了天氣數據查詢(xún)的接口。
  我們的數據已經(jīng)通過(guò)天氣數據采集微服務(wù)集成到了Redis 存儲中,天氣數據API微服務(wù)只需要從Redis獲取數據,而后從接口中暴露出去即可。
  在micro-weather-report應用的基礎上,我們將對其進(jìn)行逐步的拆分,形成一個(gè)新的微服務(wù)msa-weather-data-server應用。
  
  所需環(huán)境
  為了演示本例子,需要采用如下開(kāi)發(fā)環(huán)境。
  修改天氣數據服務(wù)接口及實(shí)現
  在com.waylau.spring.cloud. weather.service包下,我們之前已經(jīng)定義了該應用的天氣數據服務(wù)接口WeatherDataService。
  public interface WeatherDataService {<br />*★<br />*根據城市ID查詢(xún)天氣數據<br />@param cityId<br />@return <br />WeatherResponse getDataByCityId(String cityId);<br />/**<br />★根據城市名稱(chēng)查詢(xún)天氣數據<br />* @param cityId<br />* Creturn<br />*/<br />Wea therResponse getDataByCityName (String cityName) ;<br />}
  對于該微服務(wù)而言,我們并不需要同步天氣的業(yè)務(wù)需求,所以把之前定義的syncDataByCityId方法刪除了。
  WeatherDataServicelmpl是對WeatherDataService 接口的實(shí)現,也要做出相應的調整,將同步天氣的代碼邏輯都刪除,保留以下代碼。
  package com. way1au . spr ing.cloud.weather .service;<br />import java. io. IOException;<br />import org.slf4j . Logger;<br />import org.slf4j. LoggerFactory;<br />import org.springf ramework. beans. factory . annotation.Autowired;<br />import org. springfr amework. data. redis. core. StringRedisTemplate;<br />import org. springf ramework. data. redis.core. ValueOperations;<br />import org. springf ramework. stereotype. Service;<br />import com. fasterxml. jackson.databind. objectMapper;<br />import com. waylau. spring. cloud . weather . vo . Wea therResponse;<br />/**<br />k天氣數據服務(wù).<br />@since 1.0.0 2017年10月29日 <br />* @author Way Lau<br />@Service<br />public class WeatherDataServiceImpl implements WeatherDataService {<br />private final static Logger logger = LoggerFactory .getLogger (Weather<br />DataServiceImpl.class) ;<br />@Autowi red<br />private StringRedisTemplate stringRedisTemplate; <br />private final String WEATHER API = "http://wthrcdn. etouch . cn/weather_<br />mini";<br />@Override<br />public WeatherResponse getDa taByCityId(String cityId) {<br />String uri = WEATHER API + "?citykey=" + cityId;<br />return this. doGetWea therData (uri) ;<br />@Override<br />public WeatherResponse getDataByCityName (String cityName) {<br />String uri = WEATHER_ API + "?city=" + cityName ;<br />return this. doGe tWeatherData (uri) ;<br />private WeatherResponse doGetWea therData (String uri)<br />valueOperations ops = this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />/先查緩存,查不到拋出異常<br />if (!this. stringRedisTemplate . hasKey (key)) {<br />logger .error("不存在key "+ key) ;<br />throw new Runt imeException ("沒(méi)有相應的天氣信息") ;<br />} else {<br />logger.info("存在key"+ key + ", value=" + ops.get (key));<br />strBody = ops.get (key) ;<br />}<br />0bj ectMapper mapper = new ObjectMapper () ;<br />WeatherResponse weather = null;<br />try {<br />weather = mapper . readvalue (strBody, WeatherResponse.class) ;<br />} catch (IOException e) {<br />logger . error ("JSON反序列化異常! ",e);<br />throw new RuntimeException ("天氣信息解析失敗") ;<br />return weather;<br />}<br />}
  其中需要注意的是:
  ●原有的RestTemplate用作REST客戶(hù)端來(lái)進(jìn)行天氣數據的同步,這個(gè)類(lèi)相關(guān)的代碼都可以刪除了;
  ●服務(wù) 會(huì )先從緩存中進(jìn)行查詢(xún),查不到數據就拋出異常(有可能該城市的天氣數據未同步,或者是數據已經(jīng)過(guò)期) ;
  在執行反序列化JSON過(guò)程中也可能遭遇異常,同樣將異常信息拋出。
  除上述WeatherDataServicelmpl、WeatherDataService 外,其他服務(wù)層的代碼都可以刪除了。
  調整控制層的代碼
  除了WeatherController 外,其他控制層的代碼都不需要了。
  WeatherController仍然是原有的代碼保持不變。
  
  刪除配置類(lèi)、天氣數據同步任務(wù)和工具類(lèi)
  配置類(lèi)RestConfiguration、QuartzConfiguration 及任務(wù)類(lèi)WeatherDataSyncJob、 工具類(lèi)Xml-
  Builder的代碼都可以刪除了。
  清理值對象
  值對象我們需要保留解析天氣相關(guān)的類(lèi)即可,其他值對象(如City. CityList等)都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并 刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并 刪除。
  最后,要清理Thymeleaf在application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。

天氣數據采集微服務(wù)的實(shí)現:數據采集組件、數據存儲組件

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 130 次瀏覽 ? 2022-05-25 13:07 ? 來(lái)自相關(guān)話(huà)題

  天氣數據采集微服務(wù)的實(shí)現:數據采集組件、數據存儲組件
  . Spring Boot Data Redis Starter 2.0.0.M4。
  .Redis 3.2.100。
  . Spring Boot Quartz Starter 2.0.0.M4。
  . Quartz Scheduler 2.3.0。
  新增天氣數據采集服務(wù)接口及實(shí)現
  在
  com.waylau.spring.cloud.weather.service包下,我們定義了該應用的天氣數據采集服務(wù)接口WeatherDataCollectionService。
  public interface WeatherDataCollectionService {<br />/**<br />*根據城市工D同步天氣數據<br />*<br />*@param cityId<br />*@return<br />*/<br />void syncDataByCityId(String cityId);<br />}
  WeatherDataCollectionService只有一個(gè)同步天氣數據的方法。WeatherDataCollectionServicelmpl是對WeatherDataCollectionService接口的實(shí)現。
  package com.waylau.spring.cloud.weather.service;<br />import java.util.concurrent.TimeUnit;<br />import org.slf4j.Logger;<br />import org.slf4j-LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.data.redis.core.StringRedisTemplate;<br />import org.springframework.data.redis.core.ValueOperations;<br />import org.springframework.http.ResponseEntity;<br />import org.springframework.stereotype.Service;<br />import org.springframework.web.client.RestTemplate;<br />/*★<br />*天氣數據采集服務(wù).<br />*<br />*@since 1.o.0 2017年10月29日<br />* @author Way Lau<br />*/<br />@service<br />public class WeatherDataCollectionServicelmpl implements WeatherData<br />CollectionService {<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatacollectionServicelmpl.class);<br />@Autowired<br />private RestTemplate restTemplate;<br />@Autowired<br />private stringRedisTemplate stringRedisTemplate;<br />private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";<br />private final Long TIME_OUT = 1800L;//緩存超時(shí)時(shí)間<br />@override<br />public void syncDataByCityId(String cityId) {<br />logger.info ("Start同步天氣.cityId: "+cityId);<br />String uri = WEATHER_API +"?citykey=" +cityId;<br />this.saveweatherData (uri);<br />logger.info("End同步天氣");<br />private void saveWeatherData(String uri) {<br />ValueOperations ops= this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />ResponseEntity response = restTemplate.getForEntity(uri,<br />String.class);<br />if(response.getStatusCodeValue()=-200) f<br />strBody=response.getBody(;<br />ops.set(key,strBody,TIME_OUT,TimeUnit.SECONDS);<br />}<br />}
  WeatherDataCollectionServiceImpl的實(shí)現過(guò)程,我們在之前的章節中也已經(jīng)詳細介紹過(guò),大家也已經(jīng)非常熟悉了。無(wú)非就是通過(guò)REST客戶(hù)端去調用第三方的天氣數據接口,并將返回的數據直接放入Redis存儲中。
  同時(shí),我們需要設置Redis數據的過(guò)期時(shí)間。
  修改天氣數據同步任務(wù)
  對于天氣數據同步任務(wù)WeatherDataSyncJob,我們要做一些調整。把之前所依賴(lài)的CityData-Service、WeatherDataService改為
  WeatherDataCollectionService。
  import java.util.ArrayList;<br />import java.util.List;<br />import org.quartz.JobExecutionContext;<br />import org.quartz.JobExecutionException;<br />import org.slf4j-Logger;<br />import org.slf4j.LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.scheduling.quartz.QuartzJobBean;<br />import com.waylau.spring.cloud.weather.service.WeatherDataCollection<br />service;<br />import com.waylau.spring.cloud.weather.vo.City;<br />*★<br />天氣數據同步任務(wù).<br />*<br />*@since 1.0.0 2017年10月29日<br />* author <a href=span style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;color: rgb(0, 117, 59);""https://waylau.com"/span>Way Lau</a><br />*/<br />public class WeatherDataSyncJob extends QuartzJobBean<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatasyncJob.class);<br />@Autowired<br />private WeatherDataCollectionService weatherDataCollectionService;<br />@override<br />protected void executeInternal (JobExecutionContext context) throws<br />JobExecutionException{<br />logger.info("'Start天氣數據同步任務(wù)");<br />/TODO改為由城市數據API微服務(wù)來(lái)提供數據<br />工istcityList =null;<br />trY {<br />//TODO 調用城市數據APT<br />cityList = new ArrayEist();<br />City city = new City();<br />city.setCityId("101280601");<br />cityList.add(city);<br />}catch(Exception e){<br />logger.error("獲取城市信息異常!",e);<br />throw new RuntimeException("獲取城市信息異常!",e);<br />}<br />for(City city : cityList){<br />String cityld = city.getCityld(;<br />logger.info("天氣數據同步任務(wù)中,cityId:" +cityId);<br />//根據城市ID同步天氣數據<br />weatherDataCollectionService.syncDataByCityId(cityId);<br />logger.info("End 天氣數據同步任務(wù)");<br />}<br />}
  這里需要注意的是,定時(shí)器仍然對城市ID列表有依賴(lài),只不過(guò)這個(gè)依賴(lài)最終會(huì )由其他應用(城市數據API微服務(wù))來(lái)提供,所以這里暫時(shí)還沒(méi)有辦法完全寫(xiě)完,先用“TODO”來(lái)標識這個(gè)方法,后期還需要改進(jìn)。但為了能讓整個(gè)程序可以完整地走下去,我們在程序里面假設返回了一個(gè)城市ID為“101280601”的城市信息。
  配置類(lèi)
  配置類(lèi)仍然保留之前的RestConfiguration、QuartzConfiguration的代碼不變,如下所示。
  1.RestConfiguration
  RestConfiguration用于配置REST客戶(hù)端。
  import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.boot.web.client.RestTemplateBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import org.springframework.web.client.RestTemplate;<br />/**<br />*REST 配置類(lèi).<br />*<br />*@since 1.0.0 2017年10月18日<br />* @author Way Lau<br />*/<br />@configuration<br />public class RestConfiguration {<br />@Autowired<br />private RestTemplateBuilder builder;<br />CBean<br />public RestTemplate restTemplate(){<br />return builder.build();<br />}<br />}
  2.QuartzConfiguration
  QuartzConfiguration類(lèi)用于定時(shí)任務(wù)。
  import org.quartz.JobBuilder;<br />import org.quartz.JobDetail;<br />import org.quartz.SimpleScheduleBuilder;<br />import org.quartz.Trigger;<br />import org.quartz.TriggerBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import com.waylau.spring.cloud.weather.job.WeatherDataSyncJob;<br />/*★<br />*Quartz配置類(lèi).<br />*<br />*since 1.0.0 2017年10月23日<br />* author Way Lau<br />*/<br />@configuration<br />public class QuartzConfiguration <br />private final int TIME=1800;1/更新頻率<br />@Bean<br />public JobDetail weatherDataSyncJobJobDetail(){<br />return JobBuilder.newJob(WeatherDataSyncJob.class).withIdentity<br />("weatherDataSyncJob")<br />.storeDurably() .build(;<br />}<br />CBean<br />public Trigger sampleJobTrigger({<br />SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.<br />simpleschedule()<br />.withIntervalInSeconds (TIME).repeatForever();<br />return TriggerBuilder.newTrigger().forJob(weatherDataSyncJob-<br />JobDetail())<br />.withIdentity("weatherDataSyncTrigger").withSchedule<br />(scheduleBuilder).build();<br />}<br />}
  值對象
  值對象我們只需要保留City即可,其他值對象都可以刪除了。需要注意的是,由于天氣數據采集微服務(wù)并未涉及對XML數據的解析,所以之前在City上添加的相關(guān)的JABX注解,都是可以一并刪除的。
  以下是新的City類(lèi)。
  public class City {<br />private String cityId;<br />private string cityName;<br />private string cityCode;<br />private String province;<br />1/省略getter/setter方法}
  工具類(lèi)
  工具類(lèi)XmlBuilder的代碼都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并刪除。
  最后,要清理Thymeleaf在 application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。
  測試和運行
  首先,在進(jìn)行測試前,需要將Redis服務(wù)器啟動(dòng)起來(lái)。
  而后再啟動(dòng)應用。啟動(dòng)應用之后,定時(shí)器就自動(dòng)開(kāi)始執行。整個(gè)同步過(guò)程可以通過(guò)以下控制臺信息看到。
  2017-10-29 22:26:41.748 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.WeatherDatasyncJob<br />:Start天氣數據同步任務(wù)<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.weatherDataSyncJob:天氣數據同步任務(wù)中,cityId:101280601<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] s.c.w.s.Weather<br />DataCollectionServiceImpl: Start同步天氣.cityId:101280601<br />2017-10-29 22:26:41.836 INFO 13956 ---[<br />main]o.s.b.w.embedded.<br />tomcat.TomcatwebServer: Tomcat started on port(s):8080 (http)<br />2017-10-29 22:26:41.840 INFO 13956 ---[<br />main]c.w.spring.<br />cloud.weather.Application:Started Application in 4.447 seconds<br />(JVM running for 4.788)<br />2017-10-29 22:26:41.919 INFO 13956---[eduler_Worker-1] S.c.w.s.eather<br />DatacollectionServiceImpl :End同步天氣<br />2017-10-29 22:26:41.920 INFO 13956---[eduler Worker-1] C.W.s.c.weather.<br />job.WeatherDataSyncJob:End 天氣數據同步任務(wù)
  由于我們只是在代碼里面“硬編碼”了一個(gè)城市ID為“101280601”的城市信息,所以,只有一條同步記錄。
  當然,我們也能通過(guò)Redis Desktop Manager,來(lái)方便查看存儲到Redis里面的數據,如圖7-3所示。
  
  本篇內容給大家講解的是天氣數據采集微服務(wù)的實(shí)現
  下篇文章給大家講解天氣數據API微服務(wù)的實(shí)現;
  覺(jué)得文章不錯的朋友可以轉發(fā)此文關(guān)注小編;
  感謝大家的支持??!
  本文就是愿天堂沒(méi)有BUG給大家分享的內容,大家有收獲的話(huà)可以分享下,想學(xué)習更多的話(huà)可以到微信公眾號里找我,我等你哦。 查看全部

  天氣數據采集微服務(wù)的實(shí)現:數據采集組件、數據存儲組件
  . Spring Boot Data Redis Starter 2.0.0.M4。
  .Redis 3.2.100。
  . Spring Boot Quartz Starter 2.0.0.M4。
  . Quartz Scheduler 2.3.0。
  新增天氣數據采集服務(wù)接口及實(shí)現
  在
  com.waylau.spring.cloud.weather.service包下,我們定義了該應用的天氣數據采集服務(wù)接口WeatherDataCollectionService。
  public interface WeatherDataCollectionService {<br />/**<br />*根據城市工D同步天氣數據<br />*<br />*@param cityId<br />*@return<br />*/<br />void syncDataByCityId(String cityId);<br />}
  WeatherDataCollectionService只有一個(gè)同步天氣數據的方法。WeatherDataCollectionServicelmpl是對WeatherDataCollectionService接口的實(shí)現。
  package com.waylau.spring.cloud.weather.service;<br />import java.util.concurrent.TimeUnit;<br />import org.slf4j.Logger;<br />import org.slf4j-LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.data.redis.core.StringRedisTemplate;<br />import org.springframework.data.redis.core.ValueOperations;<br />import org.springframework.http.ResponseEntity;<br />import org.springframework.stereotype.Service;<br />import org.springframework.web.client.RestTemplate;<br />/*★<br />*天氣數據采集服務(wù).<br />*<br />*@since 1.o.0 2017年10月29日<br />* @author Way Lau<br />*/<br />@service<br />public class WeatherDataCollectionServicelmpl implements WeatherData<br />CollectionService {<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatacollectionServicelmpl.class);<br />@Autowired<br />private RestTemplate restTemplate;<br />@Autowired<br />private stringRedisTemplate stringRedisTemplate;<br />private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";<br />private final Long TIME_OUT = 1800L;//緩存超時(shí)時(shí)間<br />@override<br />public void syncDataByCityId(String cityId) {<br />logger.info ("Start同步天氣.cityId: "+cityId);<br />String uri = WEATHER_API +"?citykey=" +cityId;<br />this.saveweatherData (uri);<br />logger.info("End同步天氣");<br />private void saveWeatherData(String uri) {<br />ValueOperations ops= this.stringRedisTemplate.<br />opsForValue() ;<br />String key = uri;<br />String strBody = null;<br />ResponseEntity response = restTemplate.getForEntity(uri,<br />String.class);<br />if(response.getStatusCodeValue()=-200) f<br />strBody=response.getBody(;<br />ops.set(key,strBody,TIME_OUT,TimeUnit.SECONDS);<br />}<br />}
  WeatherDataCollectionServiceImpl的實(shí)現過(guò)程,我們在之前的章節中也已經(jīng)詳細介紹過(guò),大家也已經(jīng)非常熟悉了。無(wú)非就是通過(guò)REST客戶(hù)端去調用第三方的天氣數據接口,并將返回的數據直接放入Redis存儲中。
  同時(shí),我們需要設置Redis數據的過(guò)期時(shí)間。
  修改天氣數據同步任務(wù)
  對于天氣數據同步任務(wù)WeatherDataSyncJob,我們要做一些調整。把之前所依賴(lài)的CityData-Service、WeatherDataService改為
  WeatherDataCollectionService。
  import java.util.ArrayList;<br />import java.util.List;<br />import org.quartz.JobExecutionContext;<br />import org.quartz.JobExecutionException;<br />import org.slf4j-Logger;<br />import org.slf4j.LoggerFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.scheduling.quartz.QuartzJobBean;<br />import com.waylau.spring.cloud.weather.service.WeatherDataCollection<br />service;<br />import com.waylau.spring.cloud.weather.vo.City;<br />*★<br />天氣數據同步任務(wù).<br />*<br />*@since 1.0.0 2017年10月29日<br />* author <a href=span style="box-sizing: border-box;border-width: 0px;border-style: initial;border-color: initial;color: rgb(0, 117, 59);""https://waylau.com"/span>Way Lau</a><br />*/<br />public class WeatherDataSyncJob extends QuartzJobBean<br />private final static Logger logger = LoggerFactory.getLogger(Weather<br />DatasyncJob.class);<br />@Autowired<br />private WeatherDataCollectionService weatherDataCollectionService;<br />@override<br />protected void executeInternal (JobExecutionContext context) throws<br />JobExecutionException{<br />logger.info("'Start天氣數據同步任務(wù)");<br />/TODO改為由城市數據API微服務(wù)來(lái)提供數據<br />工istcityList =null;<br />trY {<br />//TODO 調用城市數據APT<br />cityList = new ArrayEist();<br />City city = new City();<br />city.setCityId("101280601");<br />cityList.add(city);<br />}catch(Exception e){<br />logger.error("獲取城市信息異常!",e);<br />throw new RuntimeException("獲取城市信息異常!",e);<br />}<br />for(City city : cityList){<br />String cityld = city.getCityld(;<br />logger.info("天氣數據同步任務(wù)中,cityId:" +cityId);<br />//根據城市ID同步天氣數據<br />weatherDataCollectionService.syncDataByCityId(cityId);<br />logger.info("End 天氣數據同步任務(wù)");<br />}<br />}
  這里需要注意的是,定時(shí)器仍然對城市ID列表有依賴(lài),只不過(guò)這個(gè)依賴(lài)最終會(huì )由其他應用(城市數據API微服務(wù))來(lái)提供,所以這里暫時(shí)還沒(méi)有辦法完全寫(xiě)完,先用“TODO”來(lái)標識這個(gè)方法,后期還需要改進(jìn)。但為了能讓整個(gè)程序可以完整地走下去,我們在程序里面假設返回了一個(gè)城市ID為“101280601”的城市信息。
  配置類(lèi)
  配置類(lèi)仍然保留之前的RestConfiguration、QuartzConfiguration的代碼不變,如下所示。
  1.RestConfiguration
  RestConfiguration用于配置REST客戶(hù)端。
  import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.boot.web.client.RestTemplateBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import org.springframework.web.client.RestTemplate;<br />/**<br />*REST 配置類(lèi).<br />*<br />*@since 1.0.0 2017年10月18日<br />* @author Way Lau<br />*/<br />@configuration<br />public class RestConfiguration {<br />@Autowired<br />private RestTemplateBuilder builder;<br />CBean<br />public RestTemplate restTemplate(){<br />return builder.build();<br />}<br />}
  2.QuartzConfiguration
  QuartzConfiguration類(lèi)用于定時(shí)任務(wù)。
  import org.quartz.JobBuilder;<br />import org.quartz.JobDetail;<br />import org.quartz.SimpleScheduleBuilder;<br />import org.quartz.Trigger;<br />import org.quartz.TriggerBuilder;<br />import org.springframework.context.annotation.Bean;<br />import org.springframework.context.annotation.Configuration;<br />import com.waylau.spring.cloud.weather.job.WeatherDataSyncJob;<br />/*★<br />*Quartz配置類(lèi).<br />*<br />*since 1.0.0 2017年10月23日<br />* author Way Lau<br />*/<br />@configuration<br />public class QuartzConfiguration <br />private final int TIME=1800;1/更新頻率<br />@Bean<br />public JobDetail weatherDataSyncJobJobDetail(){<br />return JobBuilder.newJob(WeatherDataSyncJob.class).withIdentity<br />("weatherDataSyncJob")<br />.storeDurably() .build(;<br />}<br />CBean<br />public Trigger sampleJobTrigger({<br />SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.<br />simpleschedule()<br />.withIntervalInSeconds (TIME).repeatForever();<br />return TriggerBuilder.newTrigger().forJob(weatherDataSyncJob-<br />JobDetail())<br />.withIdentity("weatherDataSyncTrigger").withSchedule<br />(scheduleBuilder).build();<br />}<br />}
  值對象
  值對象我們只需要保留City即可,其他值對象都可以刪除了。需要注意的是,由于天氣數據采集微服務(wù)并未涉及對XML數據的解析,所以之前在City上添加的相關(guān)的JABX注解,都是可以一并刪除的。
  以下是新的City類(lèi)。
  public class City {<br />private String cityId;<br />private string cityName;<br />private string cityCode;<br />private String province;<br />1/省略getter/setter方法}
  工具類(lèi)
  工具類(lèi)XmlBuilder的代碼都可以刪除了。
  清理前端代碼、配置及測試用例
  已經(jīng)刪除的服務(wù)接口的相關(guān)測試用例自然也是要一并刪除的。
  同時(shí),之前所編寫(xiě)的頁(yè)面HTML、JS文件也要一并刪除。
  最后,要清理Thymeleaf在 application.properties文件中的配置,以及build.gradle文件中的依賴(lài)。
  測試和運行
  首先,在進(jìn)行測試前,需要將Redis服務(wù)器啟動(dòng)起來(lái)。
  而后再啟動(dòng)應用。啟動(dòng)應用之后,定時(shí)器就自動(dòng)開(kāi)始執行。整個(gè)同步過(guò)程可以通過(guò)以下控制臺信息看到。
  2017-10-29 22:26:41.748 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.WeatherDatasyncJob<br />:Start天氣數據同步任務(wù)<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] c.w.s.c.weather.<br />job.weatherDataSyncJob:天氣數據同步任務(wù)中,cityId:101280601<br />2017-10-29 22:26:41.749 INFO 13956---[eduler_Worker-1] s.c.w.s.Weather<br />DataCollectionServiceImpl: Start同步天氣.cityId:101280601<br />2017-10-29 22:26:41.836 INFO 13956 ---[<br />main]o.s.b.w.embedded.<br />tomcat.TomcatwebServer: Tomcat started on port(s):8080 (http)<br />2017-10-29 22:26:41.840 INFO 13956 ---[<br />main]c.w.spring.<br />cloud.weather.Application:Started Application in 4.447 seconds<br />(JVM running for 4.788)<br />2017-10-29 22:26:41.919 INFO 13956---[eduler_Worker-1] S.c.w.s.eather<br />DatacollectionServiceImpl :End同步天氣<br />2017-10-29 22:26:41.920 INFO 13956---[eduler Worker-1] C.W.s.c.weather.<br />job.WeatherDataSyncJob:End 天氣數據同步任務(wù)
  由于我們只是在代碼里面“硬編碼”了一個(gè)城市ID為“101280601”的城市信息,所以,只有一條同步記錄。
  當然,我們也能通過(guò)Redis Desktop Manager,來(lái)方便查看存儲到Redis里面的數據,如圖7-3所示。
  
  本篇內容給大家講解的是天氣數據采集微服務(wù)的實(shí)現
  下篇文章給大家講解天氣數據API微服務(wù)的實(shí)現;
  覺(jué)得文章不錯的朋友可以轉發(fā)此文關(guān)注小編;
  感謝大家的支持??!
  本文就是愿天堂沒(méi)有BUG給大家分享的內容,大家有收獲的話(huà)可以分享下,想學(xué)習更多的話(huà)可以到微信公眾號里找我,我等你哦。

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 133 次瀏覽 ? 2022-05-21 18:02 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

再見(jiàn)笨重的ELK!這套輕量級日志收集方案要火!

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 78 次瀏覽 ? 2022-05-21 01:39 ? 來(lái)自相關(guān)話(huà)題

  再見(jiàn)笨重的ELK!這套輕量級日志收集方案要火!
  之前一直使用的日志收集方案是ELK,動(dòng)輒占用幾個(gè)G的內存,有些配置不好的服務(wù)器有點(diǎn)頂不??!最近發(fā)現一套輕量級日志收集方案:Loki+Promtail+Grafana(簡(jiǎn)稱(chēng)LPG), 幾百M內存就夠了,而且界面也挺不錯的,推薦給大家!
  簡(jiǎn)介
  LPG日志收集方案內存占用很少,經(jīng)濟且高效!它不像ELK日志系統那樣為日志建立索引,而是為每個(gè)日志流設置一組標簽。下面分別介紹下它的核心組件:
  
  日志收集流程圖安裝
  實(shí)現這套日志收集方案需要安裝Loki、Promtail、Grafana這些服務(wù),直接使用docker-compose來(lái)安裝非常方便。
  version:?"3"<br /><br />services:<br />??#?日志存儲和解析<br />??loki:<br />????image:?grafana/loki<br />????container_name:?lpg-loki<br />????volumes:<br />??????-?/mydata/loki/:/etc/loki/<br />????#?修改loki默認配置文件路徑<br />????command:?-config.file=/etc/loki/loki.yml<br />????ports:<br />??????-?3100:3100<br /><br />??#?日志收集器<br />??promtail:<br />????image:?grafana/promtail<br />????container_name:?lpg-promtail<br />????volumes:<br />??????#?將需要收集的日志所在目錄掛載到promtail容器中<br />??????-?/mydata/app/mall-tiny-loki/logs/:/var/log/<br />??????-?/mydata/promtail:/etc/promtail/<br />????#?修改promtail默認配置文件路徑<br />????command:?-config.file=/etc/promtail/promtail.yml<br /><br />??#?日志可視化<br />??grafana:<br />????image:?grafana/grafana<br />????container_name:?lpg-grafana<br />????ports:<br />??????-?3000:3000<br />
  auth_enabled:?false<br /><br />server:<br />??http_listen_port:?3100<br /><br />ingester:<br />??lifecycler:<br />????address:?127.0.0.1<br />????ring:<br />??????kvstore:<br />????????store:?inmemory<br />??????replication_factor:?1<br />????final_sleep:?0s<br />??chunk_idle_period:?1h???????#?Any?chunk?not?receiving?new?logs?in?this?time?will?be?flushed<br />??max_chunk_age:?1h???????????#?All?chunks?will?be?flushed?when?they?hit?this?age,?default?is?1h<br />??chunk_target_size:?1048576??#?Loki?will?attempt?to?build?chunks?up?to?1.5MB,?flushing?first?if?chunk_idle_period?or?max_chunk_age?is?reached?first<br />??chunk_retain_period:?30s????#?Must?be?greater?than?index?read?cache?TTL?if?using?an?index?cache?(Default?index?read?cache?TTL?is?5m)<br />??max_transfer_retries:?0?????#?Chunk?transfers?disabled<br /><br />schema_config:<br />??configs:<br />????-?from:?2020-10-24<br />??????store:?boltdb-shipper<br />??????object_store:?filesystem<br />??????schema:?v11<br />??????index:<br />????????prefix:?index_<br />????????period:?24h<br /><br />storage_config:<br />??boltdb_shipper:<br />????active_index_directory:?/loki/boltdb-shipper-active<br />????cache_location:?/loki/boltdb-shipper-cache<br />????cache_ttl:?24h?????????#?Can?be?increased?for?faster?performance?over?longer?query?periods,?uses?more?disk?space<br />????shared_store:?filesystem<br />??filesystem:<br />????directory:?/loki/chunks<br /><br />compactor:<br />??working_directory:?/loki/boltdb-shipper-compactor<br />??shared_store:?filesystem<br /><br />limits_config:<br />??reject_old_samples:?true<br />??reject_old_samples_max_age:?168h<br /><br />chunk_store_config:<br />??max_look_back_period:?0s<br /><br />table_manager:<br />??retention_deletes_enabled:?false<br />??retention_period:?0s<br /><br />ruler:<br />??storage:<br />????type:?local<br />????local:<br />??????directory:?/loki/rules<br />??rule_path:?/loki/rules-temp<br />??alertmanager_url:?http://localhost:9093<br />??ring:<br />????kvstore:<br />??????store:?inmemory<br />??enable_api:?true<br />
  server:<br />??http_listen_port:?9080<br />??grpc_listen_port:?0<br /><br />positions:<br />??filename:?/tmp/positions.yaml<br /><br />clients:<br />??-?url:?http://loki:3100/loki/api/v1/push<br /><br />scrape_configs:<br />-?job_name:?system<br />??static_configs:<br />??-?targets:<br />??????-?localhost<br />????labels:<br />??????job:?varlogs<br />??????__path__:?/var/log/*log<br />
  docker-compose?up?-d<br />
  [root@local-linux?lpg]#?docker?ps?|grep?lpg<br />64761b407423????????grafana/loki????????????????????????????"/usr/bin/loki?-conf…"???3?minutes?ago???????Up?3?minutes????????0.0.0.0:3100->3100/tcp???????????????????????????lpg-loki<br />67f0f0912971????????grafana/grafana?????????????????????????"/run.sh"????????????????3?minutes?ago???????Up?3?minutes????????0.0.0.0:3000->3000/tcp???????????????????????????lpg-grafana<br />f2d78eb188d1????????grafana/promtail????????????????????????"/usr/bin/promtail?-…"???3?minutes?ago???????Up?3?minutes?????????????????????????????????????????????????????????lpg-promtail<br />
  使用
  接下來(lái)我們將使用LPG日志收集系統來(lái)收集SpringBoot應用的日志,SpringBoot應用基本不用做特殊配置。
  spring:<br />??application:<br />????name:?mall-tiny-loki<br /><br />logging:<br />??path:?/var/logs<br />??level:<br />????com.macro.mall.tiny:?debug<br />
  docker?run?-p?8088:8088?--name?mall-tiny-loki?\<br />-v?/etc/localtime:/etc/localtime?\<br />-v?/mydata/app/mall-tiny-loki/logs:/var/logs?\<br />-e?TZ="Asia/Shanghai"?\<br />-d?mall-tiny/mall-tiny-loki:1.0-SNAPSHOT<br />
  
  
  
  
  總結
  本文主要介紹了LPG日志系統的搭建及使用它收集SpringBoot應用的日志,LPG日志收集方案確實(shí)非常輕量級,性能也不錯!不過(guò)如果你有對日志進(jìn)行全文搜索的需求的話(huà),還是得使用ELK系統。如果你對Grafana還不熟悉的話(huà),可以參考下這篇文章。
  參考資料項目源碼地址
  微信8.0將好友放開(kāi)到了一萬(wàn),小伙伴可以加我大號了,先到先得,再滿(mǎn)就真沒(méi)了
  掃描下方二維碼即可加我微信啦,2021,抱團取暖,一起牛逼。
   查看全部

  再見(jiàn)笨重的ELK!這套輕量級日志收集方案要火!
  之前一直使用的日志收集方案是ELK,動(dòng)輒占用幾個(gè)G的內存,有些配置不好的服務(wù)器有點(diǎn)頂不??!最近發(fā)現一套輕量級日志收集方案:Loki+Promtail+Grafana(簡(jiǎn)稱(chēng)LPG), 幾百M內存就夠了,而且界面也挺不錯的,推薦給大家!
  簡(jiǎn)介
  LPG日志收集方案內存占用很少,經(jīng)濟且高效!它不像ELK日志系統那樣為日志建立索引,而是為每個(gè)日志流設置一組標簽。下面分別介紹下它的核心組件:
  
  日志收集流程圖安裝
  實(shí)現這套日志收集方案需要安裝Loki、Promtail、Grafana這些服務(wù),直接使用docker-compose來(lái)安裝非常方便。
  version:?"3"<br /><br />services:<br />??#?日志存儲和解析<br />??loki:<br />????image:?grafana/loki<br />????container_name:?lpg-loki<br />????volumes:<br />??????-?/mydata/loki/:/etc/loki/<br />????#?修改loki默認配置文件路徑<br />????command:?-config.file=/etc/loki/loki.yml<br />????ports:<br />??????-?3100:3100<br /><br />??#?日志收集器<br />??promtail:<br />????image:?grafana/promtail<br />????container_name:?lpg-promtail<br />????volumes:<br />??????#?將需要收集的日志所在目錄掛載到promtail容器中<br />??????-?/mydata/app/mall-tiny-loki/logs/:/var/log/<br />??????-?/mydata/promtail:/etc/promtail/<br />????#?修改promtail默認配置文件路徑<br />????command:?-config.file=/etc/promtail/promtail.yml<br /><br />??#?日志可視化<br />??grafana:<br />????image:?grafana/grafana<br />????container_name:?lpg-grafana<br />????ports:<br />??????-?3000:3000<br />
  auth_enabled:?false<br /><br />server:<br />??http_listen_port:?3100<br /><br />ingester:<br />??lifecycler:<br />????address:?127.0.0.1<br />????ring:<br />??????kvstore:<br />????????store:?inmemory<br />??????replication_factor:?1<br />????final_sleep:?0s<br />??chunk_idle_period:?1h???????#?Any?chunk?not?receiving?new?logs?in?this?time?will?be?flushed<br />??max_chunk_age:?1h???????????#?All?chunks?will?be?flushed?when?they?hit?this?age,?default?is?1h<br />??chunk_target_size:?1048576??#?Loki?will?attempt?to?build?chunks?up?to?1.5MB,?flushing?first?if?chunk_idle_period?or?max_chunk_age?is?reached?first<br />??chunk_retain_period:?30s????#?Must?be?greater?than?index?read?cache?TTL?if?using?an?index?cache?(Default?index?read?cache?TTL?is?5m)<br />??max_transfer_retries:?0?????#?Chunk?transfers?disabled<br /><br />schema_config:<br />??configs:<br />????-?from:?2020-10-24<br />??????store:?boltdb-shipper<br />??????object_store:?filesystem<br />??????schema:?v11<br />??????index:<br />????????prefix:?index_<br />????????period:?24h<br /><br />storage_config:<br />??boltdb_shipper:<br />????active_index_directory:?/loki/boltdb-shipper-active<br />????cache_location:?/loki/boltdb-shipper-cache<br />????cache_ttl:?24h?????????#?Can?be?increased?for?faster?performance?over?longer?query?periods,?uses?more?disk?space<br />????shared_store:?filesystem<br />??filesystem:<br />????directory:?/loki/chunks<br /><br />compactor:<br />??working_directory:?/loki/boltdb-shipper-compactor<br />??shared_store:?filesystem<br /><br />limits_config:<br />??reject_old_samples:?true<br />??reject_old_samples_max_age:?168h<br /><br />chunk_store_config:<br />??max_look_back_period:?0s<br /><br />table_manager:<br />??retention_deletes_enabled:?false<br />??retention_period:?0s<br /><br />ruler:<br />??storage:<br />????type:?local<br />????local:<br />??????directory:?/loki/rules<br />??rule_path:?/loki/rules-temp<br />??alertmanager_url:?http://localhost:9093<br />??ring:<br />????kvstore:<br />??????store:?inmemory<br />??enable_api:?true<br />
  server:<br />??http_listen_port:?9080<br />??grpc_listen_port:?0<br /><br />positions:<br />??filename:?/tmp/positions.yaml<br /><br />clients:<br />??-?url:?http://loki:3100/loki/api/v1/push<br /><br />scrape_configs:<br />-?job_name:?system<br />??static_configs:<br />??-?targets:<br />??????-?localhost<br />????labels:<br />??????job:?varlogs<br />??????__path__:?/var/log/*log<br />
  docker-compose?up?-d<br />
  [root@local-linux?lpg]#?docker?ps?|grep?lpg<br />64761b407423????????grafana/loki????????????????????????????"/usr/bin/loki?-conf…"???3?minutes?ago???????Up?3?minutes????????0.0.0.0:3100->3100/tcp???????????????????????????lpg-loki<br />67f0f0912971????????grafana/grafana?????????????????????????"/run.sh"????????????????3?minutes?ago???????Up?3?minutes????????0.0.0.0:3000->3000/tcp???????????????????????????lpg-grafana<br />f2d78eb188d1????????grafana/promtail????????????????????????"/usr/bin/promtail?-…"???3?minutes?ago???????Up?3?minutes?????????????????????????????????????????????????????????lpg-promtail<br />
  使用
  接下來(lái)我們將使用LPG日志收集系統來(lái)收集SpringBoot應用的日志,SpringBoot應用基本不用做特殊配置。
  spring:<br />??application:<br />????name:?mall-tiny-loki<br /><br />logging:<br />??path:?/var/logs<br />??level:<br />????com.macro.mall.tiny:?debug<br />
  docker?run?-p?8088:8088?--name?mall-tiny-loki?\<br />-v?/etc/localtime:/etc/localtime?\<br />-v?/mydata/app/mall-tiny-loki/logs:/var/logs?\<br />-e?TZ="Asia/Shanghai"?\<br />-d?mall-tiny/mall-tiny-loki:1.0-SNAPSHOT<br />
  
  
  
  
  總結
  本文主要介紹了LPG日志系統的搭建及使用它收集SpringBoot應用的日志,LPG日志收集方案確實(shí)非常輕量級,性能也不錯!不過(guò)如果你有對日志進(jìn)行全文搜索的需求的話(huà),還是得使用ELK系統。如果你對Grafana還不熟悉的話(huà),可以參考下這篇文章。
  參考資料項目源碼地址
  微信8.0將好友放開(kāi)到了一萬(wàn),小伙伴可以加我大號了,先到先得,再滿(mǎn)就真沒(méi)了
  掃描下方二維碼即可加我微信啦,2021,抱團取暖,一起牛逼。
  

文章采集api Python 爬取人人視頻

采集交流 ? 優(yōu)采云 發(fā)表了文章 ? 0 個(gè)評論 ? 140 次瀏覽 ? 2022-05-21 01:37 ? 來(lái)自相關(guān)話(huà)題

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
   查看全部

  文章采集api Python 爬取人人視頻
  hello,小伙伴們,又見(jiàn)面了,距離上一次發(fā)布文章的時(shí)間,也算是久別重逢了。期間也發(fā)生了很多的事情,導致博文斷更,也是筆者不愿意的,但是確實(shí)是比較忙,不再過(guò)多贅述,希望大家能夠體諒。
  平時(shí)不斷的在后臺收到小伙伴的私信,問(wèn)是不是不更了,答案當然是否定的,有著(zhù)這么多人的支持,小編還是要繼續努力下去的,再次謝謝大家的支持。
  這次給大家帶來(lái)的文章是爬取人人視頻,之前多是分享一些爬取數據,圖片,音樂(lè ),還沒(méi)怎么分享過(guò)爬取過(guò)視頻的,那么想要爬取視頻的話(huà)該怎么爬取呢?
  其實(shí)不管是圖片,還是音樂(lè ),或者是其他的文檔,大部分都是一個(gè)文件讀寫(xiě)的過(guò)程,當然視頻也不例外,只是文件的格式不同而已。所以我們也可以試著(zhù)以常規的手段去嘗試下爬取視頻,沒(méi)錯還是熟悉的套路與配方,即python常用函數 open()和 write()。
  不過(guò)這次略微不同的是我們會(huì )使用到iter_content來(lái)獲取請求的原始響應數據,普通情況可以用r.raw,在初始請求中設置stream=True,來(lái)獲取服務(wù)器的原始套接字響應,在這里我們使用iter_content更加方便一些,因為requests.get(url) 默認是下載在內存中的,下載完成才存到硬盤(pán)上,但Response.iter_content可以邊下載邊存硬盤(pán)中,所以在這視頻下載方面更具有優(yōu)勢。
  當然說(shuō)到 iter_content 的話(huà),不得不提下chunk_size,因為流式請求就是像流水一樣,不是一次過(guò)來(lái)而是一點(diǎn)一點(diǎn)“流”過(guò)來(lái)。處理流式數據也是一點(diǎn)一點(diǎn)處理。
  而chunk_size會(huì )直接返回從當前位置往后讀取 chunk_size 大小的文件內容,且迭代響應數據。這避免了立即將內容讀入內存以獲得較大的響應。chunk_size是它應該讀入內存的字節數。chunk_size的類(lèi)型必須是int或None。None的值將根據流的值發(fā)揮不同的作用。
  做了引薦與講解后,那么就開(kāi)始上我們的主菜了,即目標網(wǎng)站:
  https://m.rr.tv/
  介于代碼偏基礎化,且主要知識點(diǎn)做過(guò)分析就直接上代碼了,具體請看代碼:
  單線(xiàn)程:
  import requestsimport jsonimport reimport osheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}ep_list =[]vod_list = []def get_vod(url): response = requests.get(url = url,headers = headers)#請求url ep= re.compile(r'sid:"(.*?)",')#提取ep鏈接 ep_list = re.findall(ep,response.text) vod= re.compile(r'data:\[{id:(.*?),title:"')#提取vod鏈接 vod_list = re.findall(vod,response.text) vod= re.compile(r',title:"(.*?)",desc:"')#提取視頻標題 vod_name = re.findall(vod,response.text) ep = 1 os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 for i in ep_list: print("開(kāi)始下載"+vod_name[0]+"第"+str(ep)+"集") url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers)#下載請求 r = requests.get(str(json.loads(response.text)['data']['url']), stream=True)#解析出下載鏈接并發(fā)起下載請求 f = open("./"+vod_name[0]+"/第"+str(ep)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk) ep = ep+1if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxxx?snum=1&episode=1'#目標視頻鏈接 get_vod(url)
  多線(xiàn)程:
  import requestsimport jsonimport reimport osfrom concurrent.futures import ThreadPoolExecutorheaders = {'Referer':'https://m.rr.tv/',#全局設置'User-Agent':'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36'}vod_list = []#存儲視頻鏈接vod_name = []#存儲視頻標題get_down_url =[]#存儲下載直鏈def get_vod(url): response = requests.get(url = url,headers = headers)#請求url vod_list.append(re.findall(re.compile(r'data:\[{id:(.*?),title:"'),response.text)[0])#找找視頻鏈接 vod_name.append(re.findall(re.compile(r',title:"(.*?)",desc:"'),response.text)[0])#找找視頻標題 for i in re.findall(re.compile(r'{sid:"(.*?)",key:'),response.text): url ="https://web-api.rr.tv/web/dram ... _list[0]+"&episodeId="+i+"&2-7-17xx"#拼接地址 response = requests.get(url = url,headers = headers) get_down_url.append(str(json.loads(response.text)['data']['url']))#拿下載直鏈進(jìn)listdef down_begin(url,i): print("開(kāi)始多線(xiàn)程下載"+vod_name[0]+"第"+str(i)+"集") r = requests.get(url = url,headers = headers)#下載請求 f = open("./"+vod_name[0]+"/第"+str(i)+"集.MP4", "wb")#保存視頻 for chunk in r.iter_content(chunk_size=512): if chunk: f.write(chunk)if __name__ == '__main__': url='https://m.rr.tv/detail/xxxxx?snum=1&episode=1'#進(jìn)入rr.tv自行獲取 get_vod(url) os.mkdir('./'+vod_name[0])#創(chuàng )建視頻保存目錄 with ThreadPoolExecutor(10) as f:#這里寫(xiě)多線(xiàn)程參數,適合幾十集的電視劇使用 for i,url in enumerate(get_down_url): i=int(i)+1 f.submit(down_begin,url = url,i=i)
  把案例里面的鏈接改成你想要下載的鏈接,然后右擊運行代碼,即可成功的下載你想要的視頻了。代碼獲取后臺回復:”人人視頻“。
  在文章的最后給大家來(lái)一波福利,因為前一段時(shí)間小編在爬取百度相關(guān)關(guān)鍵詞以及文章采集時(shí),經(jīng)常觸發(fā)百度的驗證機制,這種情況很明顯要使用到代理IP,后來(lái)群里一個(gè)小伙伴推薦了品贊代理IP,小編測試了下,完美解決了爬取中存在的問(wèn)題。
  
  如果大家后續有需要使用到代理方面的業(yè)務(wù)的話(huà),可以?huà)叽a添加下方的二維碼。國內外的IP都有,新用戶(hù)可以免費測試。
  

官方客服QQ群

微信人工客服

QQ人工客服


線(xiàn)

亚洲国产精品无码久久大片,亚洲AV无码乱码麻豆精品国产,亚洲品质自拍网站,少妇伦子伦精品无码STYLES,国产精久久久久久久