python布隆過濾器
① 布隆過濾器的基本原理和使用
工作中遇到一個需求,需要從詞庫中快速判斷某個關鍵字是否存在,詞庫大小不超過百萬,當時腦子第一反應是用hash表相關數據結構,和同事一交流,同事推薦用布隆過濾器,查詢效率不輸hashmap,而且非常節省存儲空間。經過研究發現布隆過濾器挺好用的,這篇文章來說說三點:
1.什麼是布隆過濾器。
2.布隆過濾器基本原理。
3.布隆過濾器的使用方式。
布隆過濾器(Bloom Filter)是1970年由[布隆]提出的。它實際上是一個很長的[二進制]向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都比一般的演算法要好的多,缺點是有一定的誤識別率和刪除困難。
a.下圖是一個初始化後的長度為11的布隆過濾器結構,可以看成一個數組,還未放入任何數據,所有位的值都是0。
b.假如有三個hash函數(hash1、hash2、hash3)此時我們添加一個關鍵字進去,假設我們添加一個字母"a",通過三個hash函數分別求出2、5、6,於是把下標為2、5、6的值都改為1。
c.此時我們根據字母"a"去布隆過濾器查找,判斷a是否存在的流程如下圖,由於對a進行三個hash函數取模得到的2、5、6下標的值都是1,說明這個a大概率已經存在了(為什麼是大概率呢?因為布隆過濾器是一種概率型數據結構,存在非常小的誤判幾率,不能判斷某個元素一定百分之百存在,所以只能用在允許有少量誤判的場景,不能用在需要100%精確判斷存在的場景)。
如果使用字母b進行查找,三個hash函數取模得到的是7、8、9或者3、5、6,發現這些下標對應的值都不全部是1,則判定為不存在。
相對於HashMap的優點:
布隆過濾器節省空間,無需存儲全部數據,只需要將多個hash函數取模得到的下標對應位置的值改為1即可,無需存儲全部數據,是一種極度節省存儲空間的數據結構。
相對於HashMap的缺點:
布隆過濾器無法做到100%精確判斷,而HashMap可以。布隆過濾器本質上是賭不同的字元串不太可能所有的哈希函數都發生哈希碰撞,雖然有極低的概率發生,但是基本可以把誤判率控制在1%以內。
② 布隆過濾器
如果使用哈希表,內存空間需要100億*64位元組=640G(10億位元組(byte)是1G),空間就爆掉了。此時就輪到布隆過濾器登場了。
布隆過濾器應用場景:黑名單 爬蟲去重
布隆過濾器優勢:節省內存(30G以內),查詢速度快
布隆過濾器劣勢:存在一定失誤率
但布隆過濾器的失誤率是可以容忍的,一是可以通過設計人為的把失誤率控制的很低;二是就算失誤了不會誤報已經在黑名單里的URL。布隆過濾器只會把正常的URL當成黑名單系統里的,但不會誤報已經在黑名單里的URL。形象點說就是「寧可錯殺三千不會放過一個」
在講解布隆過濾器原理之前先講點陣圖。
點陣圖是bit類型的數組。 int類型4位元組即32bit,所以長度100的int類型數組可以看出長度3200的bit數組。假如要找1782位比特,那麼在int數組里下標是1782/32,在下標里存的int數字里第幾個比特位?即1782%32的計算結果,從而把整型數組操作轉換成比特類型數組操作。
布隆過濾器即大點陣圖,假設是長度為m的bit數組,那麼佔用m/8位位元組(Byte),
URL加黑名單過程:開始時m位比特都是0(白)的狀態,該URL通過哈希函數f1得到一個哈希值,然後%m,得到0~m-1上某個位置,將該位置描黑(變成1),再用哈希函數f2得到一個哈希值,再描黑,再用哈希函數f3同樣處理(f1、f2、f3是獨立的哈希函數),假設一共用了k個哈希函數,那麼描黑了k個位置(某個位置重復了就重復了,但一旦描黑就沒有描白的時候)完成後可以說該URL加入了點陣圖里。對下一個URL同樣處理,用k個哈希函數描黑k個位置……一直做100億個,點陣圖中有相當多位置被描黑了。
如何查某個URL在不在黑名單里?把待查的URL用K個哈希函數算出對應的哈希值,再把該哈希值%m,把K個位置的狀態都拿出來,如果全黑,就說這個URL屬於黑名單系統,如果有一個是白,就不屬於黑名單系統。如果m很小,100億個URL之後所有點陣圖都是黑的,那麼不論查誰都在黑名單里;如果m取的大一些,失誤率就會降低。
但布隆過濾器需要准備多少個bit位和單樣本的大小無關。一個URL經過K個哈希函數得到K個哈希值,對m取模後去相應的大比特map中描黑,只要保證哈希函數能接收單樣本這種類型的參數就可以了,與樣本是64位元組還是128位元組無關。所以影響失誤率的重要參數就是樣本量N和點陣圖比特數組長度m。
布隆過濾器三公式: 出處
1.確定m:對於輸入的數據量n(這里是100億)和失誤率p(這里是萬分之一),布隆過濾器的大小m:m = - (n lnp) / (ln2 ln2)
2.確定K:K假如很少,例如只有一個哈希函數,相當於每個數據只採集了一個特徵點,只把一個位置描黑,查的時候也只用一個哈希函數,特徵點不夠,失誤率雖然不至於很高但有一定的量,如果K很大,例如用10萬個哈希函數去把10萬個位置描黑,m的空間會接近耗盡,查什麼URL都是黑的,失誤率非常高。需要的哈希函數的個數k:k = ln2 * m/n = 0.7 * m/n
3.因為前兩步中公式1公式2都會進行向上取整,所以公式3算出的實際的失誤率與比預期失誤率要低
布隆過濾器在Hadoop中的應用:Hadoop中的分布式文件系統,是由許多小文件組成的,如何查詢一個數據在哪個文件里?首先不可能記錄每個小文件的索引,這樣做佔用空間太大了。所以每個小文件里都搞一個布隆過濾器,當Hadoop想知道某個key在哪個文件里,就查每一個布隆過濾器,文件a的布隆過濾器會說明你這個key在我這個文件里,但可能會有誤報;文件c的布隆過濾器會說明你這個key在我這個文件里,但可能會有誤報……如果失誤率很低,哪怕有幾萬個分布式文件,最終可能算出來只有3個文件里可能含有這個key。那麼就只用把這3個小文件遍歷一遍,就知道key在哪個小文件里了。通俗點講,如果一個文件真的含有key,那麼它的布隆過濾器會說這個key屬於我;但因為有失誤率,可能會發生一個文件不含有這個key,它還是會說這個key屬於我;可是這也沒關系,多查一遍就可以,反正失誤率很低,需要遍歷的文件很少。
③ 精通python網路爬蟲之網路爬蟲學習路線
欲精通Python網路爬蟲,必先了解網路爬蟲學習路線,本篇經驗主要解決這個問題。部分內容參考自書籍《精通Python網路爬蟲》。
作者:韋瑋
轉載請註明出處
隨著大數據時代的到來,人們對數據資源的需求越來越多,而爬蟲是一種很好的自動採集數據的手段。
那麼,如何才能精通Python網路爬蟲呢?學習Python網路爬蟲的路線應該如何進行呢?在此為大傢具體進行介紹。
1、選擇一款合適的編程語言
事實上,Python、PHP、JAVA等常見的語言都可以用於編寫網路爬蟲,你首先需要選擇一款合適的編程語言,這些編程語言各有優勢,可以根據習慣進行選擇。在此筆者推薦使用Python進行爬蟲項目的編寫,其優點是:簡潔、掌握難度低。
2、掌握Python的一些基礎爬蟲模塊
當然,在進行這一步之前,你應當先掌握Python的一些簡單語法基礎,然後才可以使用Python語言進行爬蟲項目的開發。
在掌握了Python的語法基礎之後,你需要重點掌握一個Python的關於爬蟲開發的基礎模塊。這些模塊有很多可以供你選擇,比如urllib、requests等等,只需要精通一個基礎模塊即可,不必要都精通,因為都是大同小異的,在此推薦的是掌握urllib,當然你可以根據你的習慣進行選擇。
3、深入掌握一款合適的表達式
學會了如何爬取網頁內容之後,你還需要學會進行信息的提取。事實上,信息的提取你可以通過表達式進行實現,同樣,有很多表達式可以供你選擇使用,常見的有正則表達式、XPath表達式、BeautifulSoup等,這些表達式你沒有必要都精通,同樣,精通1-2個,其他的掌握即可,在此建議精通掌握正則表達式以及XPath表達式,其他的了解掌握即可。正則表達式可以處理的數據的范圍比較大,簡言之,就是能力比較強,XPath只能處理XML格式的數據,有些形式的數據不能處理,但XPath處理數據會比較快。
4、深入掌握抓包分析技術
事實上,很多網站都會做一些反爬措施,即不想讓你爬到他的數據。最常見的反爬手段就是對數據進行隱藏處理,這個時候,你就無法直接爬取相關的數據了。作為爬蟲方,如果需要在這種情況下獲取數據,那麼你需要對相應的數據進行抓包分析,然後再根據分析結果進行處理。一般推薦掌握的抓包分析工具是Fiddler,當然你也可以用其他的抓包分析工具,沒有特別的要求。
5、精通一款爬蟲框架
事實上,當你學習到這一步的時候,你已經入門了。
這個時候,你可能需要深入掌握一款爬蟲框架,因為採用框架開發爬蟲項目,效率會更加高,並且項目也會更加完善。
同樣,你可以有很多爬蟲框架進行選擇,比如Scrapy、pySpider等等,一樣的,你沒必要每一種框架都精通,只需要精通一種框架即可,其他框架都是大同小異的,當你深入精通一款框架的時候,其他的框架了解一下事實上你便能輕松使用,在此推薦掌握Scrapy框架,當然你可以根據習慣進行選擇。
6、掌握常見的反爬策略與反爬處理策略
反爬,是相對於網站方來說的,對方不想給你爬他站點的數據,所以進行了一些限制,這就是反爬。
反爬處理,是相對於爬蟲方來說的,在對方進行了反爬策略之後,你還想爬相應的數據,就需要有相應的攻克手段,這個時候,就需要進行反爬處理。
事實上,反爬以及反爬處理都有一些基本的套路,萬變不離其宗,這些後面作者會具體提到,感興趣的可以關注。
常見的反爬策略主要有:
IP限制
UA限制
Cookie限制
資源隨機化存儲
動態載入技術
……
對應的反爬處理手段主要有:
IP代理池技術
用戶代理池技術
Cookie保存與處理
自動觸發技術
抓包分析技術+自動觸發技術
……
這些大家在此先有一個基本的思路印象即可,後面都會具體通過實戰案例去介紹。
7、掌握PhantomJS、Selenium等工具的使用
有一些站點,通過常規的爬蟲很難去進行爬取,這個時候,你需要藉助一些工具模塊進行,比如PhantomJS、Selenium等,所以,你還需要掌握PhantomJS、Selenium等工具的常規使用方法。
8、掌握分布式爬蟲技術與數據去重技術
如果你已經學習或者研究到到了這里,那麼恭喜你,相信現在你爬任何網站都已經不是問題了,反爬對你來說也只是一道形同虛設的牆而已了。
但是,如果要爬取的資源非常非常多,靠一個單機爬蟲去跑,仍然無法達到你的目的,因為太慢了。
所以,這個時候,你還應當掌握一種技術,就是分布式爬蟲技術,分布式爬蟲的架構手段有很多,你可以依據真實的伺服器集群進行,也可以依據虛擬化的多台伺服器進行,你可以採用urllib+redis分布式架構手段,也可以採用Scrapy+redis架構手段,都沒關系,關鍵是,你可以將爬蟲任務部署到多台伺服器中就OK。
至於數據去重技術,簡單來說,目的就是要去除重復數據,如果數據量小,直接採用資料庫的數據約束進行實現,如果數據量很大,建議採用布隆過濾器實現數據去重即可,布隆過濾器的實現在Python中也是不難的。
以上是如果你想精通Python網路爬蟲的學習研究路線,按照這些步驟學習下去,可以讓你的爬蟲技術得到非常大的提升。
至於有些朋友問到,使用Windows系統還是Linux系統,其實,沒關系的,一般建議學習的時候使用Windows系統進行就行,比較考慮到大部分朋友對該系統比較數據,但是在實際運行爬蟲任務的時候,把爬蟲部署到Linux系統中運行,這樣效率比較高。由於Python的可移植性非常好,所以你在不同的平台中運行一個爬蟲,代碼基本上不用進行什麼修改,只需要學會部署到Linux中即可。所以,這也是為什麼說使用Windows系統還是Linux系統進行學習都沒多大影響的原因之一。
本篇文章主要是為那些想學習Python網路爬蟲,但是又不知道從何學起,怎麼學下去的朋友而寫的。希望通過本篇文章,可以讓你對Python網路爬蟲的研究路線有一個清晰的了解,這樣,本篇文章的目的就達到了,加油!
本文章由作者韋瑋原創,轉載請註明出處。
④ 布隆過濾器
[TOC]
通過解決方案:
Java中如將數據存儲在內存中,最簡單的演算法結構是HashMap。通過HashMap判斷key是否存在,來判斷數據是否存在。通過hash演算法查找元素,時間復雜度基本是 O(1) (可能存在hash沖突後轉換成鏈表或紅黑樹的情況,時間復雜度的影響可以忽略)。
使用HashMap速度很快,存儲簡單,絕大部分場景可以使用。但是HashMap 佔用的空間比較大 :
為什麼出現布隆過濾器:
舉例:
如1000萬個Integer存儲在內存中,佔用空間為:4x32x10000000位,即1220兆。如布隆過濾器通過4位元組存儲(布隆過濾器通過多次hash對數據計算後-->幾次hash根據數據量指定,得到多個數據, 佔用多個位 ),則佔用空間為610M。比原有空間少一半。
個人覺得,此比較在字元等的比較中尤為有效。
一個字元串多個字元,根據編碼方式,一個字元兩個或三個位元組,如10個字元,字元串存儲佔用20個位元組,還有相關字元串相關的類信息的內存佔用。
位存儲,根據數據量的大小,hash的位數,靈活計算。如4個位元組,則是原hashMap佔用空間的五分之一。
(1)定義位元組向量
先定義一個指定長度的位元組數組(位元組數組,數組內每個元素的值)。
如長度為8(一個位元組大小),默認所有元素值均為0,如下:
(2)計算哈希值
將要寫入過濾器的數據,根據一定數量的哈希函數,得到多個哈希值,再依次判斷每個哈希值對應的索引。
如使用3個哈希函數,計算得到3個哈希值,判定哈希值對應的位元組向量為為1,3,7。
(3)更新位元組向量
將計算出的位元組向量的索引, 對應的位元組向量中的元素值更高為1 (無論之前為0或者為1,均更改為1)。如下:
(1)計算哈希值
將要判斷過濾器中是否存在的數據,根據一定數量的哈希函數,得到多個哈希值,再依次判斷每個哈希值對應的索引。
如使用3個哈希函數,計算得到3個哈希值,判定哈希值對應的位元組向量為為1,3,7。
注意:哈希函數的判斷方式和計算索引的方式,需和寫入數據時完全一致。
(2)判斷是否存在
如原位元組數組中,對應1,3,7中存在的元素的值都為1。則判定為此元素 可能存在 ,但凡有一個元素的值不為1,則判定此元素 一定不存在 。
布隆過濾器,主要需實現的目標是, 在指定的數據個數范圍內,滿足誤判率在設定的范圍內 ,誤判率太高的話,無法起到過濾數據的情況,誤判率不能為0。
因此需要計算兩個數據來滿足 存儲數據的個數 和 誤判率 :
使用布隆過濾器的決定性因素之一,就是此演算法插入數據和查詢數據的速度必須非常快。因此在對數據進行哈希運算的時候, 需選擇計算快的哈希演算法 。
而且, 寫入數據以及查詢數據的哈希演算法,順序和演算法都需完全一致 。
待完善。。。。。
可以通過google的 guava ,在內存中輕松實現布隆過濾器。
無需手動計算滿足位元組數組的長度和哈希個數,只需要輸入 擬輸入數據的個數 和 期望誤判率 即可。
不輸入期望誤判率的情況下,誤判率為0.03,即100個非范圍內的數據進行校驗時,約三個數據會判定為存在。
多次執行,結果一致,根據結果判定:
內存的存儲存在局限性,可以使用redis中的bitMap來實現位元組數組的存儲。
使用redis實現布隆過濾器。需要根據公式,手動計算位元組數組的長度和哈希的個數。
實現過程,待完善。。。。。。
⑤ 布隆過濾器和替代演算法
布隆過濾器和替代演算法:但是布隆過濾器的缺點和優點一樣明顯。誤算率是其中之一。隨著存入的元素數量增加,誤算率隨之增加。但是如果元素數量太少,則使用散列表足矣。
但是包含查找的數據項的數據文件它一定是會返回的,key-value系統中bloom filter返回的數據文件還是需要查看裡面的內容才能知道是否存在所需的數據的,這就保證了執行結果的正確性和完整性。
只是多訪問一些數據文件而已。在數據量很大key-value系統中,建立統一的B+樹索引的代價是非常大的,維護成本也很高,因此綜合起來bloom filter的性能是最好的。
缺點:
但是布隆過濾器的缺點和優點一樣明顯。誤算率是其中之一。隨著存入的元素數量增加,誤算率隨之增加。常見的補救辦法是建立一個小的白名單,存儲那些可能被誤判的元素。但是如果元素數量太少,則使用散列表足矣。
另外,一般情況下不能從布隆過濾器中刪除元素。我們很容易想到把位列陣變成整數數組,每插入一個元素相應的計數器加1, 這樣刪除元素時將計數器減掉就可以了。
⑥ 布隆過濾器(Bloom Filter)與比特幣
布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用於檢索一個元素是否在一個集合。它的優點是空間效率和查詢時間都比一般的演算法要好得多,缺點是有一定的誤識別率和刪除困難。
如果想要判斷一個元素是否在一個集合里,一般想到的是將所有元素保存起來,然後通過比較確定。數組、鏈表、樹等數據結構都是這種思路,它們的時間復雜度為(O(n)、O(logn))。散列表是一個能夠提供更快查詢速度的數據結構(時間復雜度為O(1))。但是隨著集合中元素的增加,我們需要的存儲空間越來越大,特別是隨著大數據的發展,我們越來越不可能將所有的數據都先載入到內存中再進行查找。
這時,我們就可以藉助一種新的數據結構,也就是本文的主題:布隆過濾器(Bloom Filter)。
我們使用一段長度為m的二進制位數組,再使用k個哈希函數,將一個值進行k次哈希,得到k個索引,並將對應的位置設置為1。
布隆過濾器主要提供兩種方法:Add和Test。
Add:通過哈希函數計算,得到k個索引,並將其對應的二進制位設置為1。
Test:通過哈希函數計算,得到k個索引,判斷如果任意位置上的二進制都為0,則表示該值一定不在集合中;但是如果所有位置上的二進制都為1,卻並不能表示該值一定在集合中,這被稱為假陽性,或是判斷錯誤。
可以通過增大數組的長度m,以及增加哈希函數的數量k來降低假陽性的概率。
時間復雜度
由於需要計算k次的哈希,需要的時間復雜度為O(k),而計算出對應的索引後,可以進行直接地址訪問,需要的時間復雜度為O(1),所以總的時間復雜度為O(k)。
空間復雜度
由於需要長度為m的二進制數據,所以空間復雜度為O(m),但是由於數據的基本單位是位,假設為了處理100萬條數據,為了降低假陽性的概率,我們使用長度為1000萬的二進制數組,所需的內存空間為10,000,000/8/1024/1024=1.2M內存空間。
優點
缺點
⑦ BloomFilter詳解(布隆過濾器)
從上式中可以看出,當m增大或n減小時,都會使得誤判率減小,這也符合直覺。
現在計算對於給定的m和n,k為何值時可以使得誤判率最低。設誤判率為k的函數為:
這說明了若想保持某固定誤判率不變,布隆過濾器的bit數m與被add的元素數n應該是線性同步增加的。
三 如何設計bloomfilter
此概率為某bit位在插入n個元素後未被置位的概率。因此,想保持錯誤率低,布隆過濾器的空間使用率需為50%。
bloomfilter的各個參數的錯誤率
公式推完了,大家可以看看,裡面的數學公式基本用到了指數函數 對數函數 微積分求導法則 概率論的知識,大家可以補充看下課本。
個人介紹:杜寶坤,京東聯邦學習從0到1構建者,帶領團隊構建了京東的聯邦學習解決方案,實現了電商營銷領域支持超大規模的工業化聯邦學習解決方案,支持超大規模樣本PSI隱私對齊、安全的樹模型與神經網路模型等眾多模型支持,並且實現了廣告側等業務領域的落地,開創了新的業務增長點,產生了顯著的業務經濟效益。
個人喜歡研究技術。基於從全鏈路思考與決策技術規劃的考量,研究的領域比較多,從架構、數據到演算法與演算法框架均有涉及。歡迎喜歡技術的同學和我交流,郵箱: [email protected]
⑧ 如何用python寫布隆過濾器
下面的是網路上找到的python的布隆過濾器的實現.
#!/usr/local/bin/python2.7
#coding=gbk
'''
Createdon2012-11-7
@author:palydawn
'''
importcmath
fromBitVectorimportBitVector
classBloomFilter(object):
def__init__(self,error_rate,elementNum):
#計算所需要的bit數
self.bit_num=-1*elementNum*cmath.log(error_rate)/(cmath.log(2.0)*cmath.log(2.0))
#四位元組對齊
self.bit_num=self.align_4byte(self.bit_num.real)
#分配內存
self.bit_array=BitVector(size=self.bit_num)
#計算hash函數個數
self.hash_num=cmath.log(2)*self.bit_num/elementNum
self.hash_num=self.hash_num.real
#向上取整
self.hash_num=int(self.hash_num)+1
#產生hash函數種子
self.hash_seeds=self.generate_hashseeds(self.hash_num)
definsert_element(self,element):
forseedinself.hash_seeds:
hash_val=self.hash_element(element,seed)
#取絕對值
hash_val=abs(hash_val)
#取模,防越界
hash_val=hash_val%self.bit_num
#設置相應的比特位
self.bit_array[hash_val]=1
#檢查元素是否存在,存在返回true,否則返回false
defis_element_exist(self,element):
forseedinself.hash_seeds:
hash_val=self.hash_element(element,seed)
#取絕對值
hash_val=abs(hash_val)
#取模,防越界
hash_val=hash_val%self.bit_num
#查看值
ifself.bit_array[hash_val]==0:
returnFalse
returnTrue
#內存對齊
defalign_4byte(self,bit_num):
num=int(bit_num/32)
num=32*(num+1)
returnnum
#產生hash函數種子,hash_num個素數
defgenerate_hashseeds(self,hash_num):
count=0
#連續兩個種子的最小差值
gap=50
#初始化hash種子為0
hash_seeds=[]
forindexinxrange(hash_num):
hash_seeds.append(0)
forindexinxrange(10,10000):
max_num=int(cmath.sqrt(1.0*index).real)
flag=1
fornuminxrange(2,max_num):
ifindex%num==0:
flag=0
break
ifflag==1:
#連續兩個hash種子的差值要大才行
ifcount>0and(index-hash_seeds[count-1])<gap:
continue
hash_seeds[count]=index
count=count+1
ifcount==hash_num:
break
returnhash_seeds
defhash_element(self,element,seed):
hash_val=1
forchinstr(element):
chval=ord(ch)
hash_val=hash_val*seed+chval
returnhash_val
'''
#測試代碼
bf=BloomFilter(0.001,1000000)
element='palydawn'
bf.insert_element(element)
printbf.is_element_exist('palydawn')'''
#其中使用了BitVector庫,python本身的二進制操作看起來很麻煩,這個就簡單多了
如果解決了您的問題請採納!
如果未解決請繼續追問