日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

爬虫教程( 6 ) --- 爬虫 进阶、扩展

發(fā)布時間:2023/12/14 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 爬虫教程( 6 ) --- 爬虫 进阶、扩展 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. 前言

1. 先看一個最簡單的爬蟲。

import requestsurl = "http://www.cricode.com" r = requests.get(url) print(r.text)

2. 一個正常的爬蟲程序

上面那個最簡單的爬蟲,是一個不完整的殘疾的爬蟲。因為爬蟲程序通常需要做的事情如下:

  • 1)給定的種子 URLs,爬蟲程序?qū)⑺蟹N子 URL 頁面爬取下來
  • 2)爬蟲程序解析爬取到的 URL 頁面中的鏈接,將這些鏈接放入待爬取 URL 集合中
  • 3)重復 1、2 步,直到達到指定條件才結(jié)束爬取

因此,一個完整的爬蟲大概是這樣子的:

import requests # 用來爬取網(wǎng)頁 from bs4 import BeautifulSoup # 用來解析網(wǎng)頁# 我們的種子 seeds = ["http://www.hao123.com","http://www.csdn.net","http://www.cricode.com" ]# 設定終止條件為:爬取到 100000個頁面時就停止爬取 end_sum = 0def do_save_action(text=None):passwhile end_sum < 10000:if end_sum < len(seeds):r = requests.get(seeds[end_sum])end_sum = end_sum + 1do_save_action(r.text)soup = BeautifulSoup(r.content)urls = soup.find_all('a') # 解析網(wǎng)頁for url in urls:seeds.append(url)else:break

3. 現(xiàn)在來找茬。上面那個完整的爬蟲,缺點實在是太多。下面一一列舉它的N宗罪:

  • 1)我們的任務是爬取1萬個網(wǎng)頁,按上面這個程序,一個人在默默的爬取,假設爬起一個網(wǎng)頁3秒鐘,那么,爬一萬個網(wǎng)頁需要3萬秒鐘。MGD,我們應當考慮開啟多個線程(池)去一起爬取,或者用分布式架構(gòu)去并發(fā)的爬取網(wǎng)頁。
  • 2)種子URL后續(xù)解析到的URL 都放在一個列表里,應該設計一個更合理的數(shù)據(jù)結(jié)構(gòu)來存放這些待爬取的 URL ,比如:隊列或者優(yōu)先隊列。( scrapy-redis 是 種子URL redis的 list 里面后續(xù)解析到的URL 隊列里面?)
  • 3)對各個網(wǎng)站的 url,我們一視同仁,事實上,我們應當區(qū)別對待。大站好站優(yōu)先原則應當予以考慮。
  • 4)每次發(fā)起請求,都是根據(jù) url 發(fā)起請求,而這個過程中會牽涉到 DNS 解析,將 url 轉(zhuǎn)換成 ip 地址。一個網(wǎng)站通常由成千上萬的 URL,因此,可以考慮將這些網(wǎng)站域名的 IP 地址進行緩存,避免每次都發(fā)起 DNS 請求,費時費力。
  • 5)解析到網(wǎng)頁中的 urls 后,我們沒有做任何去重處理,全部放入待爬取的列表中。事實上,可能有很多鏈接是重復的,我們做了很多重復勞動。
  • 6)…..

4.找了這么多茬后,現(xiàn)在討論一下問題的解決方案。

  • 1)并行爬取問題。我們可以有多種方法去實現(xiàn)并行。多線程或者線程池方式,一個爬蟲程序內(nèi)部開啟多個線程。同一臺機器開啟多個爬蟲程序,如此,我們就有N多爬取線程在同時工作。能大大減少時間。此外,當我們要爬取的任務特別多時,一臺機器、一個網(wǎng)點肯定是不夠的,我們必須考慮分布式爬蟲。常見的分布式架構(gòu)有:主從(Master——Slave)架構(gòu)、點對點(Peer to Peer)架構(gòu),混合架構(gòu)等。說到分布式架構(gòu),那我們需要考慮的問題就有很多,我們需要分派任務,各個爬蟲之間需要通信合作,共同完成任務,不要重復爬取相同的網(wǎng)頁。分派任務我們要做到公平公正,就需要考慮如何進行負載均衡。負載均衡,我們第一個想到的就是Hash,比如根據(jù)網(wǎng)站域名進行hash。負載均衡分派完任務之后,千萬不要以為萬事大吉了,萬一哪臺機器掛了呢?原先指派給掛掉的哪臺機器的任務指派給誰?又或者哪天要增加幾臺機器,任務有該如何進行重新分配呢 ?一個比較好的解決方案是用一致性 Hash 算法。
  • 2)待爬取網(wǎng)頁隊列。如何對待待抓取隊列,跟操作系統(tǒng)如何調(diào)度進程是類似的場景。不同網(wǎng)站,重要程度不同,因此,可以設計一個優(yōu)先級隊列來存放待爬起的網(wǎng)頁鏈接。如此一來,每次抓取時,我們都優(yōu)先爬取重要的網(wǎng)頁。當然,你也可以效仿操作系統(tǒng)的進程調(diào)度策略之多級反饋隊列調(diào)度算法。
  • 3)DNS緩存。為了避免每次都發(fā)起DNS查詢,我們可以將DNS進行緩存。DNS緩存當然是設計一個hash表來存儲已有的域名及其IP。
  • 4)網(wǎng)頁去重。說到網(wǎng)頁去重,第一個想到的是垃圾郵件過濾。垃圾郵件過濾一個經(jīng)典的解決方案是 Bloom Filter(布隆過濾器)。布隆過濾器原理簡單來說就是:建立一個大的位數(shù)組,然后用多個 Hash 函數(shù)對同一個 url 進行 hash 得到多個數(shù)字,然后將位數(shù)組中這些數(shù)字對應的位置為1。下次再來一個url時,同樣是用多個Hash函數(shù)進行hash,得到多個數(shù)字,我們只需要判斷位數(shù)組中這些數(shù)字對應的為是全為1,如果全為1,那么說明這個url已經(jīng)出現(xiàn)過。如此,便完成了url去重的問題。當然,這種方法會有誤差,只要誤差在我們的容忍范圍之類,比如1萬個網(wǎng)頁,我只爬取到了9999個,也是可以忍受滴。。。
  • 5)數(shù)據(jù)存儲的問題。數(shù)據(jù)存儲同樣是個很有技術含量的問題。用關系數(shù)據(jù)庫存取還是用 NoSQL,或是自己設計特定的文件格式進行存儲,都大有文章可做。
  • 6)進程間通信。分布式爬蟲,就必然離不開進程間的通信。我們可以以規(guī)定的數(shù)據(jù)格式進行數(shù)據(jù)交互,完成進程間通信。
  • 7)……

如何實現(xiàn)上面這些東西 ???

實現(xiàn)的過程中,你會發(fā)現(xiàn),我們要考慮的問題遠遠不止上面這些。紙上得來終覺淺,覺知此事要躬行!

2. 如何 "跟蹤"?和 "過濾"

在很多情況下,我們并不是只抓取某個頁面,而需要 "順藤摸瓜",從幾個種子頁面,通過超級鏈接索,最終定位到我們想要的頁面。Scrapy 對這個功能進行了很好的抽象:

from abc import ABCfrom scrapy.spiders import CrawlSpider, Rule from scrapy.linkextractors import LinkExtractor from scrapy.selector import Selector from scrapy.item import Itemclass Coder4Spider(CrawlSpider, ABC):name = 'coder4'allowed_domains = ['xxx.com']start_urls = ['http://www.xxx.com']rules = (Rule(LinkExtractor(allow=('page/[0-9]+',))),Rule(LinkExtractor(allow=('archives/[0-9]+',)), callback='parse_item'),)def parse_item(self, response):self.log(f'request url : {response.url}')

在上面,我們用了 CrawlSpider 而不是 Spider。其中 name、 allowed_domains、start_urls 就不解釋了。

重點說下 Rule:

  • 第 1 條不帶 callback 的,表示只是 “跳板”,即只下載網(wǎng)頁并根據(jù) allow 中匹配的鏈接,去繼續(xù)遍歷下一步的頁面,實際上 Rule 還可以指定 deny=xxx 表示過濾掉哪些頁面。

  • 第 2 條帶 callback 的,是最終會回調(diào) parse_item 函數(shù)的網(wǎng)頁。

3. 如何 "過濾重復" 的頁面

Scrapy 支持通過 RFPDupeFilter 來完成頁面的去重(防止重復抓取)。

DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'

RFPDupeFilter 實際是根據(jù) request_fingerprint 實現(xiàn)過濾的。

源碼中實現(xiàn)如下:

def request_fingerprint(request, include_headers=None, keep_fragments=False):if include_headers:include_headers = tuple(to_bytes(h.lower()) for h in sorted(include_headers))cache = _fingerprint_cache.setdefault(request, {})cache_key = (include_headers, keep_fragments)if cache_key not in cache:fp = hashlib.sha1()fp.update(to_bytes(request.method))fp.update(to_bytes(canonicalize_url(request.url, keep_fragments=keep_fragments)))fp.update(request.body or b'')if include_headers:for hdr in include_headers:if hdr in request.headers:fp.update(hdr)for v in request.headers.getlist(hdr):fp.update(v)cache[cache_key] = fp.hexdigest()return cache[cache_key]

我們可以看到,去重指紋是 sha1(method + url + body + header),所以,實際能夠去掉重復的比例并不大。如果我們需要自己提取去重的 finger,需要自己實現(xiàn) Filter,并配置上它。下面這個 Filter 只根據(jù) url 去重:

from scrapy.dupefilters import RFPDupeFilterclass SeenURLFilter(RFPDupeFilter):"""A dupe filter that considers the URL"""def __init__(self, path=None):self.urls_seen = set()RFPDupeFilter.__init__(self, path)def request_seen(self, request):if request.url in self.urls_seen:return Trueelse:self.urls_seen.add(request.url)

不要忘記配置上:

DUPEFILTER_CLASS ='scraper.custom_filters.SeenURLFilter'

4. 海量數(shù)據(jù)處理算法 Bloom Filter

海量數(shù)據(jù)處理算法—Bloom Filter:https://www.cnblogs.com/zhxshseu/p/5289871.html

結(jié)合 Guava 源碼解讀布隆過濾器:http://cyhone.com/2017/02/07/Introduce-to-BloomFilter/

更多:https://www.baidu.com/s?wd=Bloomfilter%20%E7%AE%97%E6%B3%95

Bloom-Filter,即布隆過濾器,1970年由 Bloom 中提出。是一種多哈希函數(shù)映射的快速查找算法。通常應用在一些需要快速判斷某個元素是否屬于集合,但是并不嚴格要求100%正確的場合。Bloom Filter 有可能會出現(xiàn)錯誤判斷,但不會漏掉判斷。也就是Bloom Filter 如果判斷元素不在集合中,那肯定就是不在。如果判斷元素存在集合中,有一定的概率判斷錯誤。。。

因此,Bloom Filter 不適合那些 "零錯誤"?的應用場合。而在能容忍低錯誤率的應用場合下,Bloom Filter 比其他常見的算法(如hash,折半查找)極大節(jié)省了空間。

  • 優(yōu)點是空間效率和查詢時間都遠遠超過一般的算法,
  • 缺點是有一定的誤識別率和刪除困難。

一. 實例

為了說明 Bloom Filter 存在的重要意義,舉一個實例:假設要你寫一個網(wǎng)絡蜘蛛(web crawler)。由于網(wǎng)絡間的鏈接錯綜復雜,蜘蛛在網(wǎng)絡間爬行很可能會形成 “環(huán)”。為了避免形成“環(huán)”,就需要知道蜘蛛已經(jīng)訪問過那些 URL。給一個 URL,怎樣知道蜘蛛是否已經(jīng)訪問過呢?稍微想想,就會有如下幾種方案:

  • 1. 將訪問過的 URL 保存到數(shù)據(jù)庫。
  • 2. 用 HashSet 將訪問過的 URL 保存起來。那只需接近 O(1) 的代價就可以查到一個 URL 是否被訪問過了。
  • 3. URL 經(jīng)過 MD5 或 SHA-1 等單向哈希后再保存到 HashSet 或數(shù)據(jù)庫。
  • 4. Bit-Map 方法。建立一個 BitSet,將每個 URL 經(jīng)過一個哈希函數(shù)映射到某一位。

方法 1~3 都是將訪問過的 URL 完整保存,方法4 則只標記 URL 的一個映射位。以上方法在數(shù)據(jù)量較小的情況下都能完美解決問題,但是當數(shù)據(jù)量變得非常龐大時問題就來了。

  • 方法 1 的 缺點:數(shù)據(jù)量變得非常龐大后關系型數(shù)據(jù)庫查詢的效率會變得很低。而且每來一個URL就啟動一次數(shù)據(jù)庫查詢是不是太小題大做了?
  • 方法 2 的 缺點:太消耗內(nèi)存。隨著 URL 的增多,占用的內(nèi)存會越來越多。就算只有1億個 URL,每個 URL 只算 50 個字符,就需要 5GB 內(nèi)存。
  • 方法 3 :由于字符串經(jīng)過 MD5 處理后的信息摘要長度只有128Bit,SHA-1 處理后也只有 160Bit,因此 方法3 比 方法2 節(jié)省了好幾倍的內(nèi)存。
  • 方法 4 :消耗內(nèi)存是相對較少的,但缺點是單一哈希函數(shù)發(fā)生沖突的概率太高。還記得數(shù)據(jù)結(jié)構(gòu)課上學過的 Hash 表沖突的各種解決方法么?若要降低沖突發(fā)生的概率到1%,就要將 BitSet 的長度設置為 URL 個數(shù)的 100 倍。

實質(zhì)上,上面的算法都忽略了一個重要的隱含條件:允許小概率的出錯,不一定要100%準確!也就是說少量 url 實際上沒有沒網(wǎng)絡蜘蛛訪問,而將它們錯判為已訪問的代價是很小的——大不了少抓幾個網(wǎng)頁唄。

二. Bloom Filter 的算法

? ? ? ? 廢話說到這里,下面引入本篇的主角——Bloom Filter。其實上面方法4的思想已經(jīng)很接近 Bloom Filter 了。方法四的致命缺點是沖突概率高,為了降低沖突的概念,Bloom Filter 使用了多個哈希函數(shù),而不是一個。

? ? ? ? Bloom Filter 算法如下:創(chuàng)建一個 m位 BitSet,先將所有位初始化為0,然后選擇 k個 不同的哈希函數(shù)。第 i個 哈希函數(shù)對 字符串str 哈希的結(jié)果記為 h(i,str),且 h(i,str)的范圍是 0 到 m-1 。

  • (1) 加入字符串過程。下面是每個字符串處理的過程,首先是將字符串 str “記錄” 到 BitSet 中的過程:對于字符串 str,分別計算 h(1,str),h(2,str)…… h(k,str)。然后將 BitSet 的第 h(1,str)、h(2,str)…… h(k,str)位設為1。下圖是 Bloom Filter 加入字符串過程,很簡單吧?這樣就將字符串 str 映射到 BitSet 中的 k 個二進制位了。

  • (2) 檢查字符串是否存在的過程。下面是檢查字符串str是否被BitSet記錄過的過程:對于字符串 str,分別計算 h(1,str),h(2,str)…… h(k,str)。然后檢查 BitSet 的第 h(1,str)、h(2,str)…… h(k,str)位是否為1,若其中任何一位不為1則可以判定str一定沒有被記錄過。若全部位都是1,則 “認為” 字符串 str 存在。若一個字符串對應的 Bit 不全為1,則可以肯定該字符串一定沒有被 Bloom Filter 記錄過。(這是顯然的,因為字符串被記錄過,其對應的二進制位肯定全部被設為1了)。但是若一個字符串對應的Bit全為1,實際上是不能100%的肯定該字符串被 Bloom Filter 記錄過的。(因為有可能該字符串的所有位都剛好是被其他字符串所對應)這種將該字符串劃分錯的情況,稱為 false positive 。

三. Bloom Filter 參數(shù)選擇

  • (1) 哈希函數(shù)選擇。哈希函數(shù)的選擇對性能的影響應該是很大的,一個好的哈希函數(shù)要能近似等概率的將字符串映射到各個Bit。選擇k個不同的哈希函數(shù)比較麻煩,一種簡單的方法是選擇一個哈希函數(shù),然后送入k個不同的參數(shù)。
  • (2) m,n,k 值,我們?nèi)绾稳≈怠N覀兌x:

    可能把不屬于這個集合的元素誤認為屬于這個集合(False Positive)

    不會把屬于這個集合的元素誤認為不屬于這個集合(False Negative)。

    哈希函數(shù)的個數(shù) k、位數(shù)組大小 m、加入的字符串數(shù)量 n 的關系。哈希函數(shù)個數(shù)k取10,位數(shù)組大小m設為字符串個數(shù) n 的20倍時,false positive 發(fā)生的概率是0.0000889 ,即10萬次的判斷中,會存在 9 次誤判,對于一天1億次的查詢,誤判的次數(shù)為9000次。

    哈希函數(shù)個數(shù) k、位數(shù)組大小 m、加入的字符串數(shù)量 n 的關系可以參考參考文獻 (?http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html?)。

Table 5:?False positive rate under various? m/ n?and? k?combinations.
m/nkk=17k=18k=19k=20k=21k=22k=23k=24
2215.22.67e-05???????
2315.91.61e-05???????
2416.69.84e-061e-05??????
2517.36.08e-066.11e-066.27e-06?????
26183.81e-063.76e-063.8e-063.92e-06????
2718.72.41e-062.34e-062.33e-062.37e-06????
2819.41.54e-061.47e-061.44e-061.44e-061.48e-06???
2920.19.96e-079.35e-079.01e-078.89e-078.96e-079.21e-07??
3020.86.5e-076e-075.69e-075.54e-075.5e-075.58e-07??
3121.54.29e-073.89e-073.63e-073.48e-073.41e-073.41e-073.48e-07?
3222.22.85e-072.55e-072.34e-072.21e-072.13e-072.1e-072.12e-072.17e-07

該文獻證明了對于給定的 m、n,當 k = ln(2)* m/n 時出錯的概率是最小的。(log2 e ≈ 1.44倍),同時該文獻還給出特定的k,m,n的出錯概率。例如:根據(jù)參考文獻1,哈希函數(shù)個數(shù)k取10,位數(shù)組大小m設為字符串個數(shù)n的20倍時,false positive 發(fā)生的概率是 0.0000889 ,這個概率基本能滿足網(wǎng)絡爬蟲的需求了。

四. Python 實現(xiàn) Bloom filter

pybloomfiltermmap3? 和?pybloom 不同 的包。。。。。

Python3 安裝(?pybloomfiltermmap3 ):pip install pybloomfiltermmap3

  • pybloomfiltermmap3 github 地址:https://pypi.org/project/pybloomfiltermmap3/
  • pybloomfiltermmap3 官方文檔:https://pybloomfiltermmap3.readthedocs.io/en/latest/
  • pybloomfiltermmap 的 github:https://github.com/axiak/pybloomfiltermmap

pybloomfiltermmap3?is a Python 3 compatible fork of?pybloomfiltermmap?by?@axiak。pybloomfiltermmap3 的目標:在 python3 中為 bloom過濾器 提供一個快速、簡單、可伸縮、正確的庫。

Python 中文網(wǎng):https://www.cnpython.com/pypi/pybloomfiltermmap3

#################################################################

Windows 安裝報錯解決方法:Python - 安裝pybloomfilter遇到的問題及解決辦法:https://blog.csdn.net/tianbianEileen/article/details/75059132

Stack Overflow 上的回答如下:
this problem looks like one “sys/mman.h:No such file or directory” And is a Unix header and is not available on Windows.
I suggest you should ues pybloom instead on windows:

pip install pybloom

通過 pypi?搜索發(fā)現(xiàn),最新的??pybloom 是?pybloom3 0.0.3

pybloom 的 github 地址:https://github.com/Hexmagic/pybloom3

所以安裝命令是:pip install pybloom3

and you should use the package like this:

from pybloom import BloomFilter

#################################################################

pybloomfiltermmap3 快速示例:https://pybloomfiltermmap3.readthedocs.io/en/latest/

BloomFilter.copy_template(filename[, perm=0755]) → BloomFilter Creates a new BloomFilter object with the same parameters–same hash seeds, same size.. everything. Once this is performed, the two filters are comparable, so you can perform logical operators. Example:

>>> apple = BloomFilter(100, 0.1, '/tmp/apple') >>> apple.add('apple') False >>> pear = apple.copy_template('/tmp/pear') >>> pear.add('pear') False >>> pear |= apple

BloomFilter.len(item) → Integer Returns the number of distinct elements that have been added to the BloomFilter object, subject to the error given in error_rate.

>>> bf = BloomFilter(100, 0.1, '/tmp/fruit.bloom') >>> bf.add("Apple") >>> bf.add('Apple') >>> bf.add('orange') >>> len(bf) 2 >>> bf2 = bf.copy_template('/tmp/new.bloom') >>> bf2 |= bf >>> len(bf2) Traceback (most recent call last):... pybloomfilter.IndeterminateCountError: Length of BloomFilter object is unavailable after intersection or union called.

pybloom ?快速示例:

from pybloom import BloomFilter from pybloom import ScalableBloomFilterf = BloomFilter(capacity=1000, error_rate=0.001)print([f.add(x) for x in range(10)]) # [False, False, False, False, False, False, False, False, False, False]print(all([(x in f) for x in range(10)])) # Trueprint(10 in f) # Falseprint(5 in f) # Truef = BloomFilter(capacity=1000, error_rate=0.001) for i in range(0, f.capacity):_ = f.add(i) print((1.0 - (len(f) / float(f.capacity))) <= f.error_rate + 2e-18) # Truesbf = ScalableBloomFilter(mode=ScalableBloomFilter.SMALL_SET_GROWTH) count = 10000 for i in range(0, count):_ = sbf.add(i)print((1.0 - (len(sbf) / float(count))) <= sbf.error_rate + 2e-18) # True# len(sbf) may not equal the entire input length. 0.01% error is well # below the default 0.1% error threshold. As the capacity goes up, the # error will approach 0.1%.

五:Bloom Filter 的優(yōu)缺點。

  • 優(yōu)點:節(jié)約緩存空間(空值的映射),不再需要空值映射。減少數(shù)據(jù)庫或緩存的請求次數(shù)。提升業(yè)務的處理效率以及業(yè)務隔離性。
  • 缺點:存在誤判的概率。傳統(tǒng)的 Bloom Filter 不能作刪除操作。

六:Bloom-Filter 的應用場景

Bloom-Filter 一般用于在大數(shù)據(jù)量的集合中判定某元素是否存在。

  • (1) 適用于一些黑名單,垃圾郵件等的過濾,例如郵件服務器中的垃圾郵件過濾器。像網(wǎng)易,QQ這樣的公眾電子郵件(email)提供商,總是需要過濾來自發(fā)送垃圾郵件的人(spamer)的垃圾郵件。一個辦法就是記錄下那些發(fā)垃圾郵件的 email 地址。由于那些發(fā)送者不停地在注冊新的地址,全世界少說也有幾十億個發(fā)垃圾郵件的地址,將他們都存起來則需要大量的網(wǎng)絡服務器。如果用哈希表,每存儲一億個 email地址,就需要 1.6GB的內(nèi)存(用哈希表實現(xiàn)的具體辦法是將每一個 email地址對應成一個八字節(jié)的信息指紋,然后將這些信息指紋存入哈希表,由于哈希表的存儲效率一般只有 50%,因此一個 email地址需要占用十六個字節(jié)。一億個地址大約要 1.6GB,即十六億字節(jié)的內(nèi)存)。因此存貯幾十億個郵件地址可能需要上百 GB的內(nèi)存。而 Bloom Filter 只需要哈希表 1/8 到 1/4 的大小就能解決同樣的問題。BloomFilter 決不會漏掉任何一個在黑名單中的可疑地址。而至于誤判問題,常見的補救辦法是在建立一個小的白名單,存儲那些可能別誤判的郵件地址。
  • (2) 在搜索引擎領域,Bloom-Filte r最常用于網(wǎng)絡蜘蛛(Spider)的 URL 過濾,網(wǎng)絡蜘蛛通常有一個 URL 列表,保存著將要下載和已經(jīng)下載的網(wǎng)頁的 URL,網(wǎng)絡蜘蛛下載了一個網(wǎng)頁,從網(wǎng)頁中提取到新的 URL 后,需要判斷該 URL 是否已經(jīng)存在于列表中。此時,Bloom-Filter 算法是最好的選擇。

Google 的 BigTable。 Google 的 BigTable 也使用了 Bloom Filter,以減少不存在的行或列在磁盤上的查詢,大大提高了數(shù)據(jù)庫的查詢操作的性能。

key-value 加快查詢。

一般 Bloom-Filter 可以與一些 key-value 的數(shù)據(jù)庫一起使用,來加快查詢。一般 key-value 存儲系統(tǒng)的 values 存在硬盤,查詢就是件費時的事。將 Storage 的數(shù)據(jù)都插入Filter,在 Filter 中查詢都不存在時,那就不需要去Storage 查詢了。當 False Position 出現(xiàn)時,只是會導致一次多余的Storage查詢。

由于 Bloom-Filter 所用空間非常小,所有 BF 可以常駐內(nèi)存。這樣子的話對于大部分不存在的元素,只需要訪問內(nèi)存中的 Bloom-Filter 就可以判斷出來了,只有一小部分,需要訪問在硬盤上的 key-value 數(shù)據(jù)庫。從而大大地提高了效率。如圖:

5. scrapy_redis 去重優(yōu)化 (?7億數(shù)據(jù) )

原文鏈接:https://blog.csdn.net/Bone_ACE/article/details/53099042

使用布隆去重代替scrapy_redis(分布式爬蟲)自帶的dupefilter:https://blog.csdn.net/qq_36574108/article/details/82889744

背景:

前些天接手了上一位同事的爬蟲,一個全網(wǎng)爬蟲,用的是 scrapy + redis 分布式,任務調(diào)度用的 scrapy_redis 模塊。

大家應該知道 scrapy 是默認開啟了去重的,用了 scrapy_redis 后去重隊列放在 redis 里面,爬蟲已經(jīng)有7億多條URL的去重數(shù)據(jù)了,再加上一千多萬條 requests 的種子,redis 占用了160多G的內(nèi)存(服務器,Centos7),總共才175G好么。去重占用了大部分的內(nèi)存,不優(yōu)化還能跑?

一言不合就用 Bloomfilter+Redis 優(yōu)化了一下,內(nèi)存占用立馬降回到了二十多G,保證漏失概率小于萬分之一的情況下可以容納50億條URL的去重,效果還是很不錯的!在此記錄一下,最后附上 Scrapy+Redis+Bloomfilter 去重的 Demo(可將去重隊列和種子隊列分開!),希望對使用 scrapy 框架的朋友有所幫助。

記錄:

我們要優(yōu)化的是去重,首先剝絲抽繭查看框架內(nèi)部是如何去重的。

  • 因為 scrapy_redis 會用自己 scheduler 替代 scrapy 框架的 scheduler 進行任務調(diào)度,所以直接去 scrapy_redis 模塊下查看scheduler.py 源碼即可。
  • 在 open() 方法中有句:self.df = load_object(self.dupefilter_cls).from_spider(spider),其中?load_object(self.dupefilter_cls) 是根據(jù)對象的絕對路徑而載入一個對象并返回,self.dupefilter_cls 就是?SCHEDULER_DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter',from_spider(spider) 是返回一個??RFPDupeFilter類 的實例。

    再看下面的 enqueue_request() 方法,

    里面有句?if not request.dont_filter and self.df.request_seen(request) ,self.df.request_seen()這就是用來去重的了。按住Ctrl再左鍵點擊request_seen查看它的代碼,可看到下面的代碼:

    首先得到一個 request 的指紋,然后使用 Redis 的 set 保存指紋。可見 scrapy_redis 是利用 set 數(shù)據(jù)結(jié)構(gòu)來去重的,去重的對象是 request 的 fingerprint。至于這個 fingerprint 到底是什么,可以再深入去看 request_fingerprint() 方法的源碼(其實就是用 hashlib.sha1() 對 request 對象的某些字段信息進行壓縮)。我們用調(diào)試也可以看到,其實 fp 就是 request 對象加密壓縮后的一個字符串(40個字符,0~f)。

是否可用 Bloomfilter 進行優(yōu)化?

以上步驟可以看出,我們只要在?request_seen()?方法上面動些手腳即可。由于現(xiàn)有的七億多去重數(shù)據(jù)存的都是這個 fingerprint,所有 Bloomfilter 去重的對象仍然是 request 對象的 fingerprint。更改后的代碼如下:

def request_seen(self, request):fp = request_fingerprint(request)if self.bf.isContains(fp): # 如果已經(jīng)存在return Trueelse:self.bf.insert(fp)return False

self.bf 是類 Bloomfilter() 的實例化,關于這個Bloomfilter()類,看下面的?基于 Redis 的 Bloomfilter 去重

以上,優(yōu)化的思路和代碼就是這樣;以下將已有的七億多的去重數(shù)據(jù)轉(zhuǎn)成 Bloomfilter 去重。

  • 內(nèi)存將爆,動作稍微大點機器就能死掉,更別說Bloomfilter在上面申請內(nèi)存了。當務之急肯定是將那七億多個fingerprint導出到硬盤上,而且不能用本機導,并且先要將redis的自動持久化給關掉。
  • 因為常用Mongo,所以習慣性首先想到Mongodb,從redis取出2000條再一次性插入Mongo,但速度還是不樂觀,瓶頸在于MongoDB。(猜測是MongoDB對_id的去重導致的,也可能是物理硬件的限制)
  • 后來想用SSDB,因為SSDB和Redis很相似,用list存肯定速度快很多。然而SSDB唯獨不支持Centos7,其他版本的系統(tǒng)都可。。
  • 最后才想起來用txt,這個最傻的方法,卻是非常有效的方法。速度很快,只是為了防止讀取時內(nèi)存不足,每100萬個fingerprint存在了一個txt,四臺機器txt總共有七百個左右。
  • fingerprint取出來后redis只剩下一千多萬的Request種子,占用內(nèi)存9G+。然后用Bloomfilter將txt中的fingerprint寫回Redis,寫完以后Redis占用內(nèi)存25G,開啟redis自動持久化后內(nèi)存占用49G左右。

6. 基于 Redis 的 Bloomfilter 去重

原文鏈接:http://blog.csdn.net/bone_ace/article/details/53107018

前言:

“去重” 是日常工作中會經(jīng)常用到的一項技能,在爬蟲領域更是常用,并且規(guī)模一般都比較大。去重需要考慮兩個點:去重的數(shù)據(jù)量、去重速度。為了保持較快的去重速度,一般選擇在內(nèi)存中進行去重。

  • 數(shù)據(jù)量不大時,可以直接放在內(nèi)存里面進行去重,例如 python 可以使用 set() 進行去重。
  • 當去重數(shù)據(jù)需要持久化時可以使用 redis 的 set 數(shù)據(jù)結(jié)構(gòu)。
  • 當數(shù)據(jù)量再大一點時,可以用不同的加密算法先將長字符串壓縮成 16/32/40 個字符,再使用上面兩種方法去重;
  • 當數(shù)據(jù)量達到億(甚至十億、百億)數(shù)量級時,內(nèi)存有限,必須用 “位” 來去重,才能夠滿足需求。Bloomfilter 就是將去重對象映射到幾個內(nèi)存“位”,通過幾個位的 0/1值來判斷一個對象是否已經(jīng)存在。
  • 然而 Bloomfilter 運行在一臺機器的內(nèi)存上,不方便持久化(機器 down 掉就什么都沒啦),也不方便分布式爬蟲的統(tǒng)一去重。如果可以在 Redis 上申請內(nèi)存進行 Bloomfilter,以上兩個問題就都能解決了。

本文即是用 Python 基于 Redis 實現(xiàn) Bloomfilter 去重。下面先放代碼,最后附上說明。

代碼:

# encoding=utf-8import redis from hashlib import md5class SimpleHash(object):def __init__(self, cap, seed):self.cap = capself.seed = seeddef hash(self, value):ret = 0for i in range(len(value)):ret += self.seed * ret + ord(value[i])return (self.cap - 1) & retclass BloomFilter(object):def __init__(self, host='localhost', port=6379, db=0, blockNum=1, key='bloomfilter'):""":param host: the host of Redis:param port: the port of Redis:param db: witch db in Redis:param blockNum: one blockNum for about 90,000,000; if you have more strings for filtering, increase it.:param key: the key's name in Redis"""self.server = redis.Redis(host=host, port=port, db=db)# Redis 的 String 類型最大容量為512M,現(xiàn)使用 256M= 2^8 * 2^20 字節(jié) = 2^28 * 2^3 bitself.bit_size = 1 << 31 self.seeds = [5, 7, 11, 13, 31, 37, 61]self.key = keyself.blockNum = blockNumself.hashfunc = []for seed in self.seeds:self.hashfunc.append(SimpleHash(self.bit_size, seed))def isContains(self, str_input):if not str_input:return Falsem5 = md5()m5.update(str_input.encode("utf8"))str_input = m5.hexdigest()ret = Truename = self.key + str(int(str_input[0:2], 16) % self.blockNum)for f in self.hashfunc:loc = f.hash(str_input)ret = ret & self.server.getbit(name, loc)return retdef insert(self, str_input):m5 = md5()m5.update(str_input.encode("utf8"))str_input = m5.hexdigest()name = self.key + str(int(str_input[0:2], 16) % self.blockNum)for f in self.hashfunc:loc = f.hash(str_input)self.server.setbit(name, loc, 1)if __name__ == '__main__':""" 第一次運行時會顯示 not exists!,之后再運行會顯示 exists! """bf = BloomFilter()if bf.isContains('http://www.baidu.com'): # 判斷字符串是否存在print('exists!')else:print('not exists!')bf.insert('http://www.baidu.com')

說明:

  • Bloomfilter 算法如何使用位去重,這個百度上有很多解釋。簡單點說就是有幾個 seeds,現(xiàn)在申請一段內(nèi)存空間,一個seed 可以和字符串哈希映射到這段內(nèi)存上的一個位,幾個位都為1即表示該字符串已經(jīng)存在。插入的時候也是,將映射出的幾個位都置為1。
  • 需要提醒一下的是 Bloomfilter 算法會有漏失概率,即不存在的字符串有一定概率被誤判為已經(jīng)存在。這個概率的大小與seeds 的數(shù)量、申請的內(nèi)存大小、去重對象的數(shù)量有關。下面有一張表,m 表示內(nèi)存大小(多少個位),n 表示去重對象的數(shù)量,k 表示seed的個數(shù)。例如我代碼中申請了256M,即1<<31(m=2^31,約21.5億。即 256 * 1024 *1024 * 8),seed設置了7個。看k=7那一列,當漏失率為8.56e-05時,m/n值為23。所以n = 21.5/23 = 0.93(億),表示漏失概率為 8.56e-05 時,256M 內(nèi)存可滿足0.93億條字符串的去重。同理當漏失率為 0.000112 時,256M內(nèi)存可滿足 0.98 億條字符串的去重。

  • 基于 Redis 的 Bloomfilter 去重,其實就是利用了 Redis的String 數(shù)據(jù)結(jié)構(gòu),但 Redis 一個 String 最大只能 512M,所以如果去重的數(shù)據(jù)量大,需要申請多個去重塊(代碼中 blockNum 即表示去重塊的數(shù)量)。

    代碼中使用了 MD5 加密壓縮,將字符串壓縮到了 32 個字符(也可用 hashlib.sha1()壓縮成40個字符)。

    它有兩個作用,

    • 一是 Bloomfilter 對一個很長的字符串哈希映射的時候會出錯,經(jīng)常誤判為已存在,壓縮后就不再有這個問題;

    • 二是壓縮后的字符為 0~f 共16中可能,我截取了前兩個字符,再根據(jù)blockNum將字符串指定到不同的去重塊進行去重。

    總結(jié):

    基于 Redis 的 Bloomfilter 去重,既用上了 Bloomfilter 的海量去重能力,又用上了 Redis 的可持久化能力,基于 Redis 也方便分布式機器的去重。在使用的過程中,要預算好待去重的數(shù)據(jù)量,則根據(jù)上面的表,適當?shù)卣{(diào)整 seed 的數(shù)量和 blockNum 數(shù)量(seed 越少肯定去重速度越快,但漏失率越大)。

    7. scrapy_redis 種子優(yōu)化

    前言:

    繼?scrapy_redis去重優(yōu)化(已有7億條數(shù)據(jù))【?https://blog.csdn.net/bone_ace/article/details/53099042,優(yōu)化去重之后,Redis 的內(nèi)存消耗降了許多,然而還不滿足。這次對 scrapy_redis 的種子隊列作了一些優(yōu)化(嚴格來說并不能用上“優(yōu)化”這詞,其實就是結(jié)合自己的項目作了一些改進,對本項目能稱作優(yōu)化,對 scrapy_redis 未必是個優(yōu)化)。

    scrapy_redis 默認是將 Request 對象序列化后(變成一條字符串)存入 Redis 作為種子,需要的時候再取出來進行反序列化,還原成一個 Request 對象。

    現(xiàn)在的問題是:序列化后的字符串太長,短則幾百個字符,長則上千。我的爬蟲平時至少也要維護包含幾千萬種子的種子隊列,占用內(nèi)存在20G~50G之間(Centos)。想要縮減種子的長度,這樣不僅 Redis 的內(nèi)存消耗會降低,各個 slaver 從 Redis 拿種子的速度也會有所提高,從而整個分布式爬蟲系統(tǒng)的抓取速度也會有所提高(效果視具體情況而定,要看爬蟲主要阻塞在哪里)。

    記錄:

    1、首先看調(diào)度器,即 scrapy_redis 模塊下的 scheduler.py 文件,可以看到?enqueue_request()方法和?next_request()方法就是種子 入隊列出隊列 的地方,self.queue?指的是我們在 setting.py 里面設定的?SCHEDULER_QUEUE_CLASS?值,常用的是?'scrapy_redis.queue.SpiderPriorityQueue'。

    2、進入 scrapy_redis 模塊下的 queue.py 文件,SpiderPriorityQueue 類的代碼如下:

    class SpiderPriorityQueue(Base):"""Per-spider priority queue abstraction using redis' sorted set"""def __len__(self):"""Return the length of the queue"""return self.server.zcard(self.key)def push(self, request):"""Push a request"""data = self._encode_request(request)pairs = {data: -request.priority}self.server.zadd(self.key, **pairs)def pop(self, timeout=0):"""Pop a requesttimeout not support in this queue class"""pipe = self.server.pipeline()pipe.multi()pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)results, count = pipe.execute()if results:return self._decode_request(results[0])

    可以看到,上面用到了 Redis 的 zset 數(shù)據(jù)結(jié)構(gòu)(它可以給種子加優(yōu)先級),在進 Redis 之前用 _encode_request() 方法將Request 對象轉(zhuǎn)成字符串,_encode_request() 和 _decode_request 是 Base類下面的兩個方法:

    def _encode_request(self, request):"""Encode a request object"""return pickle.dumps(request_to_dict(request, self.spider), protocol=-1)def _decode_request(self, encoded_request):"""Decode an request previously encoded"""return request_from_dict(pickle.loads(encoded_request), self.spider)

    可以看到,這里先將 Request 對象轉(zhuǎn)成一個字典,再將字典序列化成一個字符串。Request 對象怎么轉(zhuǎn)成一個字典呢?看下面的代碼,一目了然。

    def request_to_dict(request, spider=None):"""Convert Request object to a dict.If a spider is given, it will try to find out the name of the spider methodused in the callback and store that as the callback."""cb = request.callbackif callable(cb):cb = _find_method(spider, cb)eb = request.errbackif callable(eb):eb = _find_method(spider, eb)d = {'url': to_unicode(request.url), # urls should be safe (safe_string_url)'callback': cb,'errback': eb,'method': request.method,'headers': dict(request.headers),'body': request.body,'cookies': request.cookies,'meta': request.meta,'_encoding': request._encoding,'priority': request.priority,'dont_filter': request.dont_filter,}return d

    調(diào)試截圖:(?注:d 為 Request 對象轉(zhuǎn)過來的字典,data 為字典序列化后的字符串。 )

    3、了解完 scrapy_redis 默認的種子處理方式,現(xiàn)在針對自己的項目作一些調(diào)整。我的是一個全網(wǎng)爬蟲,每個種子需要記錄的信息主要有兩個:url 和 callback 函數(shù)名。此時我們選擇不用序列化,直接用簡單粗暴的方式,將 callback 函數(shù)名和 url 拼接成一條字符串作為一條種子,這樣種子的長度至少會減少一半。另外我們的種子并不需要設優(yōu)先級,所以也不用 zset 了,改用 Redis 的list。以下是我新建的 SpiderSimpleQueue 類,加在 queue.py 中。如果在 settings.py 里將

    SCHEDULER_QUEUE_CLASS?值設置成??'scrapy_redis.queue.SpiderSimpleQueue'?即可使用我這種野蠻粗暴的種子。

    from scrapy.utils.reqser import request_to_dict, request_from_dict, _find_methodclass SpiderSimpleQueue(Base):""" url + callback """def __len__(self):"""Return the length of the queue"""return self.server.llen(self.key)def push(self, request):"""Push a request"""url = request.urlcb = request.callbackif callable(cb):cb = _find_method(self.spider, cb)data = '%s--%s' % (cb, url)self.server.lpush(self.key, data)def pop(self, timeout=0):"""Pop a request"""if timeout > 0:data = self.server.brpop(self.key, timeout=timeout)if isinstance(data, tuple):data = data[1]else:data = self.server.rpop(self.key)if data:cb, url = data.split('--', 1)try:cb = getattr(self.spider, str(cb))return Request(url=url, callback=cb)except AttributeError:raise ValueError("Method %r not found in: %s" % (cb, self.spider))__all__ = ['SpiderQueue', 'SpiderPriorityQueue', 'SpiderSimpleQueue', 'SpiderStack']

    4、另外需要提醒的是,如果 scrapy 中加了中間件?process_request(),當 yield 一個 Request 對象的時候,scrapy_redis 會直接將它丟進 Redis 種子隊列,未執(zhí)行?process_requset();需要一個 Request 對象的時候,scrapy_redis 會從 Redis 隊列中取出種子,此時才會處理?process_request()方法,接著去抓取網(wǎng)頁。
    所以并不需要擔心?process_request()里面添加的 Cookie 在 Redis 中放太久會失效,因為進 Redis 的時候它壓根都還沒執(zhí)行process_request()。事實上 Request 對象序列化的時候帶上的字段很多都是沒用的默認字段,很多爬蟲都可以用 “callback+url” 的方式來優(yōu)化種子。

    5、最后,在 Scrapy_Redis_Bloomfilter ( https://github.com/LiuXingMing/Scrapy_Redis_Bloomfilter)這個 demo 中我已作了修改,大家可以試試效果。

    結(jié)語:

    經(jīng)過以上優(yōu)化,Redis 的內(nèi)存消耗從 42G 降到了 27G!里面包含7億多條種子的去重數(shù)據(jù) 和 4000W+ 條種子。并且六臺子爬蟲的抓取速度都提升了一些。

    兩次優(yōu)化,內(nèi)存消耗從160G+降到現(xiàn)在的27G,效果也是讓人滿意!

    原文鏈接:http://blog.csdn.net/bone_ace/article/details/53306629

    8. scrapy 引擎源碼解析

    本節(jié)內(nèi)容將介紹下 scrapy 引擎具體實現(xiàn)的功能。 engine.py 提供了2個類:Slot 和 ExecutionEngine

    • Slot:? 提供了幾個方法,添加請求,刪除請求,關閉自己,觸發(fā)關閉方法。它使用 Twisted 的主循環(huán) reactor 來不斷的調(diào)度執(zhí)行 Engine 的 "_next_request" 方法,這個方法也是核心循環(huán)方法。
    • ExecutionEngine:? 引擎的執(zhí)行任務 。

    爬蟲引擎控制調(diào)度器下載器? 爬蟲 的。 This is the Scrapy engine which controls the Scheduler, Downloader and Spiders. For more information see docs/topics/architecture.rst

    引擎 是 整個 scrapy 的核心控制和調(diào)度scrapy運行的。Engine 的 open_spider 方法完成了一些初始化,以及啟動調(diào)度器獲取種子隊列,以及去重隊列,最后調(diào)用 self._nest_request 開始一次爬取過程。

    open_spider 中 slot 調(diào)用 _next_request,接下來我們看看 _next_request ,先是通過 _needs_backout(spider) 判斷是否需要結(jié)束爬蟲,然后返回,然后通過 self._next_request_from_scheduler(spider) 方法判斷是否還有 URL 需要去爬。

    def _next_request(self, spider):slot = self.slotif not slot:returnif self.paused:return while not self._needs_backout(spider): # 是否需要返回if not self._next_request_from_scheduler(spider): # 是否還有 URL 需要爬取breakif slot.start_requests and not self._needs_backout(spider):try:request = next(slot.start_requests)except StopIteration:slot.start_requests = Noneexcept Exception:slot.start_requests = Nonelogger.error('Error while obtaining start requests',exc_info=True, extra={'spider': spider})else:self.crawl(request, spider)if self.spider_is_idle(spider) and slot.close_if_idle:self._spider_idle(spider)

    _next_request 循環(huán)通過 _next_request_from_scheduler(self,?spider) 方法從 scheduler 獲取下一個需要爬取的 request,然后送到下載器下載頁面。

    def _next_request_from_scheduler(self, spider):slot = self.slotrequest = slot.scheduler.next_request() # 從隊列獲取下一個待爬取的 requestif not request:returnd = self._download(request, spider) # 使用 download 下載 requestd.addBoth(self._handle_downloader_output, request, spider) # 輸出下載的 responsed.addErrback(lambda f: logger.info('Error while handling downloader output',exc_info=failure_to_exc_info(f),extra={'spider': spider}))d.addBoth(lambda _: slot.remove_request(request))d.addErrback(lambda f: logger.info('Error while removing request from slot',exc_info=failure_to_exc_info(f),extra={'spider': spider}))d.addBoth(lambda _: slot.nextcall.schedule())d.addErrback(lambda f: logger.info('Error while scheduling new request',exc_info=failure_to_exc_info(f),extra={'spider': spider}))return d

    繼續(xù)看?_download(request, spider) 函數(shù)

    ccdef _download(self, request, spider):slot = self.slotslot.add_request(request)def _on_success(response):assert isinstance(response, (Response, Request))if isinstance(response, Response): # 如果返回的是 Response 對象打印日志response.request = request # tie request to response receivedlogkws = self.logformatter.crawled(request, response, spider)logger.log(*logformatter_adapter(logkws), extra={'spider': spider})self.signals.send_catch_log(signal=signals.response_received, \response=response, request=request, spider=spider)return responsedef _on_complete(_):slot.nextcall.schedule()return _dwld = self.downloader.fetch(request, spider) # 使用downloader的fetch下載requestdwld.addCallbacks(_on_success) # 添加成功回掉方法dwld.addBoth(_on_complete)return dwld

    scrapy源碼分析 < 一 >:入口函數(shù)以及是如何運行

    運行 scrapy crawl example 命令的時候,就會執(zhí)行我們寫的爬蟲程序。

    下面我們從源碼分析一下scrapy執(zhí)行的流程:入口函數(shù)以及是如何運行:http://www.30daydo.com/article/530

    Scrapy 閱讀源碼分析

    原文鏈接:https://blog.csdn.net/weixin_37947156/category_6959928.html

    scrapy 命令

    當用 scrapy 寫好一個爬蟲后,使用?scrapy crawl <spider_name>命令就可以運行這個爬蟲,那么這個過程中到底發(fā)生了什么?

    scrapy?命令從何而來? 實際上,當你成功安裝 scrapy 后,使用如下命令,就能找到這個命令:

    $ which scrapy /usr/local/bin/scrapy

    使用?vim?或其他編輯器打開它:$ vim /usr/local/bin/scrapy 。其實它就是一個 python 腳本,而且代碼非常少。

    #!/usr/bin/python # -*- coding: utf-8 -*- import re import sys from scrapy.cmdline import execute if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) sys.exit(execute())

    安裝 scrapy 后,為什么入口點是這里呢? 原因是在 scrapy 的安裝文件?setup.py?中,聲明了程序的入口處:

    from os.path import dirname, join from setuptools import setup, find_packageswith open(join(dirname(__file__), 'scrapy/VERSION'), 'rb') as f:version = f.read().decode('ascii').strip() setup(name='Scrapy',version=version,url='http://scrapy.org',description='A high-level Web Crawling and Screen Scraping framework',long_description=open('README.rst').read(),author='Scrapy developers',maintainer='Pablo Hoffman',maintainer_email='pablo@pablohoffman.com',license='BSD',packages=find_packages(exclude=('tests', 'tests.*')),include_package_data=True,zip_safe=False,entry_points={'console_scripts': ['scrapy = scrapy.cmdline:execute']},classifiers=['Framework :: Scrapy','Development Status :: 5 - Production/Stable','Environment :: Console','Intended Audience :: Developers','License :: OSI Approved :: BSD License','Operating System :: OS Independent','Programming Language :: Python','Programming Language :: Python :: 2','Programming Language :: Python :: 2.7','Topic :: Internet :: WWW/HTTP','Topic :: Software Development :: Libraries :: Application Frameworks','Topic :: Software Development :: Libraries :: Python Modules',],install_requires=['Twisted>=10.0.0','w3lib>=1.8.0','queuelib','lxml','pyOpenSSL','cssselect>=0.9','six>=1.5.2',], )

    entry_points?指明了入口是?cmdline.py?的?execute?方法,在安裝過程中,setuptools?這個包管理工具,就會把上述那一段代碼生成放在可執(zhí)行路徑下。

    入口(execute.py)

    既然現(xiàn)在已經(jīng)知道了 scrapy 的入口是?scrapy/cmdline.py?的?execute?方法,我們來看一下這個方法。

    def execute(argv=None, settings=None):if argv is None:argv = sys.argvif settings is None:settings = get_project_settings()# set EDITOR from environment if availabletry:editor = os.environ['EDITOR']except KeyError:passelse:settings['EDITOR'] = editorinproject = inside_project()cmds = _get_commands_dict(settings, inproject)cmdname = _pop_command_name(argv)parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter(),conflict_handler='resolve')if not cmdname:_print_commands(settings, inproject)sys.exit(0)elif cmdname not in cmds:_print_unknown_command(settings, cmdname, inproject)sys.exit(2)cmd = cmds[cmdname]parser.usage = "scrapy %s %s" % (cmdname, cmd.syntax())parser.description = cmd.long_desc()settings.setdict(cmd.default_settings, priority='command')cmd.settings = settingscmd.add_options(parser)opts, args = parser.parse_args(args=argv[1:])_run_print_help(parser, cmd.process_options, args, opts)cmd.crawler_process = CrawlerProcess(settings)_run_print_help(parser, _run_command, cmd, args, opts)sys.exit(cmd.exitcode)

    函數(shù)主要是初始化項目配置,在函數(shù)最后是初始化?CrawlerProcess?實例,然后運行對應命令實例的run方法。如果運行命令是scrapy crawl <spider_name>,則運行的就是?commands/crawl.py?的?run:

    run方法中調(diào)用了?CrawlerProcess?實例的?crawl?和?start,就這樣整個爬蟲程序就會運行起來了。

    再來看?CrawlerProcess 初始化。scrapy.core.engine.py( 其他代碼省略。。。?):

    class CrawlerProcess(CrawlerRunner):def __init__(self, settings=None, install_root_handler=True):super(CrawlerProcess, self).__init__(settings)install_shutdown_handlers(self._signal_shutdown)configure_logging(self.settings, install_root_handler)log_scrapy_info(self.settings)

    構(gòu)造方法中調(diào)用了父類?CrawlerRunner?的構(gòu)造:

    class CrawlerRunner:def __init__(self, settings=None):if isinstance(settings, dict) or settings is None:settings = Settings(settings)self.settings = settings# 獲取爬蟲加載器self.spider_loader = self._get_spider_loader(settings)self._crawlers = set()self._active = set()self.bootstrap_failed = Falseself._handle_twisted_reactor()

    初始化時,調(diào)用了?_get_spider_loader?方法:

    默認配置文件中的?spider_loader?配置的是 scrapy.spiderloader.SpiderLoader:

    爬蟲加載器會加載所有的爬蟲腳本,最后生成一個{spider_name: spider_cls}的字典。

    執(zhí)行 crawl 和 start 方法

    CrawlerProcess?初始化完之后,調(diào)用?crawl方法:

    這個過程會創(chuàng)建?Cralwer實例,然后調(diào)用它的?crawl方法,最后調(diào)用?start方法:

    reactor?是個什么東西呢?它是?Twisted模塊的事件管理器,只要把需要執(zhí)行的事件方法注冊到reactor中,然后調(diào)用它的run方法,它就會幫你執(zhí)行注冊好的事件方法,如果遇到網(wǎng)絡IO等待,它會自動幫你切換可執(zhí)行的事件方法,非常高效。

    大家不用在意reactor是如何工作的,你可以把它想象成一個線程池,只是采用注冊回調(diào)的方式來執(zhí)行事件。

    到這里,爬蟲的之后調(diào)度邏輯就交由引擎ExecuteEngine處理了。

    scrapy 源碼分析( 系列?):https://blog.csdn.net/happyAnger6/category_6085726_2.html

    scrapy 是一個基于 twisted 實現(xiàn)的開源爬蟲,要讀懂其源碼,需要對twisted的異步編程模型有一定了解。可以通過之前3篇deferred的相關教程了解。

    下面是總結(jié)的執(zhí)行一個爬蟲任務的整體執(zhí)行流程,請將圖片放大查看,即運行"scrapy crawl? xxxSpider"的執(zhí)行流程:

    流程中主要的顏色框的含義如下 :

    • 1.紅色框是模塊或者類。
    • 2.紫色框是向模塊或者類發(fā)送的消息,一般為函數(shù)調(diào)用。
    • 3.紅色框垂直以下的黑色框即為本模塊或者對象執(zhí)行流程的偽代碼描述。

    幾個關鍵的模塊和類介紹如下:

    • cmdline:命令行執(zhí)行模塊,主要用于配置的獲取,并執(zhí)行相應的ScrapyCommand。
    • ScrapyCommand:命令對象,用于執(zhí)行不同的命令。對于crawl任務,主要是調(diào)用CrawlerProcess的crawl和start方法。
    • CrawlerProcess:顧名思義,爬取進程,主要用于管理Crawler對象,可以控制多個Crawler對象來同時進行多個不同的爬取任務,并調(diào)用Crawler的crawl方法。
    • Crawler:爬取對象,用來控制爬蟲的執(zhí)行,里面會通過一個執(zhí)行引擎engine對象來控制spider從打開到啟動等生命周期。
    • ExecutionEngine:執(zhí)行引擎,主要控制整個調(diào)度過程,通過twisted的task.LoopingCall來不斷的產(chǎn)生爬取任務。

    scrapy 源碼解析

    <scrapy>scrapy源碼剖析https://www.cnblogs.com/shuimohei/p/13363462.html

    scrapy 源碼解析 (一):啟動流程源碼分析(一)命令行啟動:https://www.cnblogs.com/qiu-hua/p/12930422.html
    scrapy 源碼解析 (二):啟動流程源碼分析(二) CrawlerProcess 主進程:https://www.cnblogs.com/qiu-hua/p/12930707.html
    scrapy 源碼解析 (三):啟動流程源碼分析(三) ExecutionEngine 執(zhí)行引擎:https://www.cnblogs.com/qiu-hua/p/12930803.html
    scrapy 源碼解析 (四):啟動流程源碼分析(四) Scheduler調(diào)度器:https://www.cnblogs.com/qiu-hua/p/12932254.html
    scrapy 源碼解析 (五):啟動流程源碼分析(五) Scraper刮取器:https://www.cnblogs.com/qiu-hua/p/12932818.html

    Python 之 Scrapy 框架源碼解析:https://blog.csdn.net/cui_yonghua/article/details/107040329

    scrapy 信號

    官網(wǎng)說明:https://docs.scrapy.org/en/latest/topics/signals.html

    scrapy 基礎組件專題(四):信號運用:https://www.cnblogs.com/qiu-hua/p/12638683.html

    scrapy 的信號(signal)以及對下載中間件的一些總結(jié):https://blog.csdn.net/fiery_heart/article/details/82229871

    9. DNS 解析緩存

    原文鏈接:https://blog.csdn.net/bone_ace/article/details/55000101

    前言:

    這是 Python 爬蟲中 DNS 解析緩存模塊中的核心代碼,是去年的代碼了,現(xiàn)在放出來 有興趣的可以看一下。
    一般一個域名的 DNS 解析時間在 10~60 毫秒之間,這看起來是微不足道,但是對于大型一點的爬蟲而言這就不容忽視了。例如我們要爬新浪微博,同個域名下的請求有1千萬(這已經(jīng)不算多的了),那么耗時在 10~60 萬秒之間,一天才 86400 秒。也就是說單 DNS 解析這一項就用了好幾天時間,此時加上 DNS 解析緩存,效果就明顯了。

    下面直接放代碼,說明在后面。

    代碼:

    # encoding=utf-8 # --------------------------------------- # 版本:0.1 # 日期:2016-04-26 # 作者:九茶<bone_ace@163.com> # 開發(fā)環(huán)境:Win64 + Python 2.7 # ---------------------------------------import socket # from gevent import socket_dnscache = {}def _setDNSCache():""" DNS緩存 """def _getaddrinfo(*args, **kwargs):if args in _dnscache:# print str(args) + " in cache"return _dnscache[args]else:# print str(args) + " not in cache"_dnscache[args] = socket._getaddrinfo(*args, **kwargs)return _dnscache[args]if not hasattr(socket, '_getaddrinfo'):socket._getaddrinfo = socket.getaddrinfosocket.getaddrinfo = _getaddrinfo

    說明:

    其實也沒什么難度,就是將 socket 里面的緩存保存下來,避免重復獲取。可以將上面的代碼放在一個 dns_cache.py 文件里,爬蟲框架里調(diào)用一下這個?_setDNSCache()方法就行了。

    需要說明一下的是,如果你使用了 gevent 協(xié)程,并且用上了 monkey.patch_all(),要注意此時爬蟲已經(jīng)改用 gevent 里面的socket 了,DNS 解析緩存模塊也應該要用 gevent 的 socket 才行。

    10. Scrapy cookies 淺析

    首先打消大家的疑慮,?Scrapy 會自動管理 cookies,?就像瀏覽器一樣:

    Does Scrapy manage cookies automatically?

    Yes, Scrapy receives and keeps track of cookies sent by servers, and sends them back on subsequent requests, like any regular web browser does.

    Cookies 的管理是通過 CookiesMiddleware,?它屬于 DownloadMiddleware 的一部分,所有的 requests 和 response 都要經(jīng)過它的處理。

    首先看下處理 request 的部分,代碼如下:

    class CookiesMiddleware(object):"""This middleware enables working with sites that need cookies"""def __init__(self, debug=False):# 用字典生成多個cookiesjarself.jars = defaultdict(CookieJar)self.debug = debugdef process_request(self, request, spider):if request.meta.get('dont_merge_cookies', False):return# 每個cookiesjar的key都存儲在 meta字典中cookiejarkey = request.meta.get("cookiejar")jar = self.jars[cookiejarkey]cookies = self._get_request_cookies(jar, request)# 把requests的cookies存儲到cookiesjar中for cookie in cookies:jar.set_cookie_if_ok(cookie, request)# set Cookie header# 刪除原有的cookiesrequest.headers.pop('Cookie', None)# 添加cookiesjar中的cookies到requests headerjar.add_cookie_header(request)self._debug_cookie(request, spider)

    流程如下:

    • 使用字典初始化多個 cookies jar
    • 把每個 requests 指定的 cookies jar 提取出來
    • 然后根據(jù) policy 把 requests 中的 cookies 添加 cookies jar
    • 最后把 cookies jar 中合適的 cookies 添加到 requests 首部

    接下來看看如何處理 response 中的 cookies:

    def process_response(self, request, response, spider):if request.meta.get('dont_merge_cookies', False):return response# extract cookies from Set-Cookie and drop invalid/expired cookiescookiejarkey = request.meta.get("cookiejar")jar = self.jars[cookiejarkey]jar.extract_cookies(response, request)self._debug_set_cookie(response, spider)return response

    流程如下:

    • 首先從 cookies jar 字典中把 requests 對應的 cookiesjar 提取出來.
    • 使用 extract_cookies 把 response 首部中的 cookies 添加到 cookies jar

    11. 擴展部分

    python 之 goose3 庫?--- 文章提取工具

    github 地址:https://github.com/goose3/goose3

    goose3 官網(wǎng)文檔:https://goose3.readthedocs.io/en/latest/

    goose3 是什么?

    GOOSE3 最初是用 Java 編寫的一篇文章提取器,最近將它(Auff2011)轉(zhuǎn)換成Scala項目,這是 python 中的完全重寫。該軟件的目標是獲取任何新聞文章或文章類型的網(wǎng)頁,不僅提取文章的主體,而且還提取所有元數(shù)據(jù)和圖片。

    官方網(wǎng)站:https://github.com/goose3/goose3

    安裝

    • pip install goose3
    • mkvirtualenv --no-site-packages goose3 git clone https://github.com/goose3/goose3.git cd goose3 pip install -r ./requirements/python python setup.py install

    使用:

    示例用法:https://pypi.org/project/goose3/3.0.4/

    python 之 goose3 庫:https://blog.csdn.net/weixin_42547344/article/details/100735035

    動手實踐 CURL

    CURL 是利用 URL 語法在命令行方式下工作的開源文件傳輸工具。它支持http、https、ftp、ftps、telnet 等多種協(xié)議,常被用來抓取網(wǎng)頁和監(jiān)控Web服務器狀態(tài)。

    CURL 使用

    curl 命令可以用來構(gòu)造http請求。
    通用語法:curl [option] [URL...]

    使用實例

    curl 是 Linux下一個很強大的 http 命令行工具,其功能十分強大。

    基本用法

    curl http://www.baidu.com

    抓取 www.ip138.com 查詢網(wǎng): 如發(fā)現(xiàn)亂碼,可以使用iconv轉(zhuǎn)碼:

    curl http://ip138.com|iconv -f gb2312

    回車之后,html顯示在屏幕上了 ~

    1.1 get方式提交數(shù)據(jù):

    curl -G -d "name=value&name2=value2" http://www.baidu.com

    1.2 post方式提交數(shù)據(jù):

    curl -d "name=value&name2=value2" http://www.baidu.com #post數(shù)據(jù) curl -d a=b&c=d&txt@/tmp/txt http://www.baidu.com #post文件

    以表單的方式上傳文件:

    curl -F file=@/tmp/me.txt http://www.aiezu.com

    相當于設置form表單的method="POST"和enctype='multipart/form-data'兩個屬性。

    保存訪問的網(wǎng)頁

    curl http://www.baidu.com > page.html

    或者用 curl 的內(nèi)置 option 就好,存下 http 的結(jié)果,用這個 option: -o

    curl -o page.html http://www.baidu.com curl -O http://sh.meituan.com/shop/42030772% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed 100 159k 0 159k 0 0 328k 0 --:--:-- --:--:-- --:--:-- 328k

    下載過程中標準輸出還會顯示下載的統(tǒng)計信息,比如進度、下載字節(jié)數(shù)、下載速度等

    這樣,自動保存文件42030772,看到屏幕上出現(xiàn)一個下載頁面進度指示。顯示100%則表示保存成功

    設置 Header

    curl -H 'Host: 157.166.226.25'-H 'Accept-Language: es'-H 'Cookie: ID=1234' http://cnn.com

    顯示文檔信息

    -I:顯示文檔信息

    curl -I http://www.sina.com.cn/ -H Accept-Encoding:gzip,defalte

    指定proxy服務器以及其端口

    -x :可以指定http訪問所使用的proxy服務器及其端口

    curl -x 123.45.67.89:1080 -o page.html http://www.linuxidc.com curl -x http://username:pwd@ip:port http://www.baidu.com

    使用cookie

    有些網(wǎng)站是使用cookie來記錄session信息。對于chrome這樣的瀏覽器,可以輕易處理cookie信息,但在curl中只要增加相關參數(shù)也是可以很容易的處理cookie

    -c: 保存http的response里面的cookie信息。

    curl -c cookiec.txt http://www.baidu.com

    執(zhí)行后cookie信息就被存到了cookiec.txt里面了

    -D: 保存http的response里面的header信息

    curl -D cookied.txt http://www.baidu.com

    執(zhí)行后cookie信息就被存到了cookied.txt里面了
    注意:-c(小寫)產(chǎn)生的cookie和-D里面的cookie是不一樣的。

    -b:使用cookie

    很多網(wǎng)站都是通過監(jiān)視你的cookie信息來判斷你是否按規(guī)矩訪問他們的網(wǎng)站的,因此我們需要使用保存的cookie信息。

    curl -b cookiec.txt http://www.baidu.com

    模仿瀏覽器信息

    有些網(wǎng)站需要使用特定的瀏覽器去訪問他們,有些還需要使用某些特定的版本。

    -A :指定瀏覽器去訪問網(wǎng)站

    curl -A "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.0)" http://www.baidu.com

    這樣服務器端就會認為是使用IE8.0去訪問的

    偽造referer(盜鏈)

    很多服務器會檢查http訪問的referer從而來控制訪問。比如:你是先訪問首頁,然后再訪問首頁中的郵箱頁面,這里訪問郵箱的referer地址就是訪問首頁成功后的頁面地址,如果服務器發(fā)現(xiàn)對郵箱頁面訪問的referer地址不是首頁的地址,就斷定那是個盜連了

    -e:設定referer

    curl -e "www.baidu.com" http://news.baidu.com/

    這樣就會讓服務器其以為你是從www.baidu.com點擊某個鏈接過來的

    下載文件

    9.1 -o/-O文件下載

    -o: 把輸出寫到該文件中

    curl -o dodo1.jpg http://www.shaimn.com/uploads/allimg/160613/1-160613121111.jpg

    -O:把輸出寫到該文件中,保留遠程文件的文件名

    curl -O http://www.shaimn.com/uploads/allimg/160613/1-160613121111.jpg

    這樣就會以服務器上的名稱保存文件到本地

    9.2 循環(huán)下載

    有時候下載圖片可以能是前面的部分名稱是一樣的,就最后的尾椎名不一樣

    curl -O http://www.shaimn.com/uploads/allimg/160613/1-16061312111[1-5].jpg

    這樣就會把
    1-16061312111.jpg、
    1-16061312112.jpg、
    1-16061312113.jpg、
    1-16061312114.jpg、
    1-16061312115.jpg 全部保存下來

    9.3 下載重命名

    curl http://www.shaimn.com/uploads/allimg/160613/1-16061312111[1-5].jpg -o dodo#1.jpg

    dodo1.jpg,dodo2.jpg,dodo3.jpg,dodo4.jpg,dodo5.jpg

    9.4 分塊下載

    有時候下載的東西會比較大,這個時候我們可以分段下載。使用內(nèi)置option:-r

    curl -r 0-100 -o dodo1_part1.JPG http://www.shaimn.com/uploads/allimg/160613/1-160613121111.jpg curl -r 100-200 -o dodo1_part2.JPG http://www.shaimn.com/uploads/allimg/160613/1-160613121111.jpg curl -r 200- -o dodo1_part3.JPG http://www.shaimn.com/uploads/allimg/160613/1-160613121111.jpg cat dodo1_part* > dodo1.JPG

    這樣就可以查看dodo1.JPG的內(nèi)容了

    9.5 通過ftp下載文件

    curl可以通過ftp下載文件,curl提供兩種從ftp中下載的語法

    curl -O -u 用戶名:密碼 ftp://www.linux.com/dodo1.JPG curl -O ftp://用戶名:密碼@www.linux.com/dodo1.JPG

    9.6 顯示下載進度條

    curl -# -O http://www.linux.com/dodo1.JPG

    9.7:不會顯示下載進度信息

    curl -s -O http://www.linux.com/dodo1.JPG

    斷點續(xù)傳

    在windows中,我們可以使用迅雷這樣的軟件進行斷點續(xù)傳。

    curl可以通過內(nèi)置option:-C同樣可以達到相同的效果
    如果在下載dodo1.JPG的過程中突然掉線了,可以使用以下的方式續(xù)傳

    curl -C -O http://www.linux.com/dodo1.JPG通過使用-C選項可對大文件使用斷點續(xù)傳功能,如:# 當文件在下載完成之前結(jié)束該進程 curl -O http://www.gnu.org/software/gettext/manual/gettext.html ############## 20.1%# 通過添加-C選項繼續(xù)對該文件進行下載,已經(jīng)下載過的文件不會被重新下載 curl -C - -O http://www.gnu.org/software/gettext/manual/gettext.html ############### 21.1%

    上傳文件

    curl不僅僅可以下載文件,還可以上傳文件。通過內(nèi)置option:-T來實現(xiàn)

    # curl -T dodo1.JPG -u 用戶名:密碼 ftp://www.linux.com/img/

    這樣就向ftp服務器上傳了文件dodo1.JPG

    顯示抓取錯誤

    # curl -f http://www.linux.com/error

    對 CURL 使用網(wǎng)絡限速

    通過--limit-rate選項對CURL的最大網(wǎng)絡使用進行限制

    下載速度最大不會超過1000B/secondcurl --limit-rate 1000B -O http://www.gnu.org/software/gettext/manual/gettext.html

    linux curl 命令

    -a/--append 上傳文件時,附加到目標文件 -A/--user-agent <string> 設置用戶代理發(fā)送給服務器 - anyauth 可以使用“任何”身份驗證方法 -b/--cookie <name=string/file> cookie字符串或文件讀取位置 - basic 使用HTTP基本驗證 -B/--use-ascii 使用ASCII /文本傳輸 -c/--cookie-jar <file> 操作結(jié)束后把cookie寫入到這個文件中 -C/--continue-at <offset> 斷點續(xù)轉(zhuǎn) -d/--data <data> HTTP POST方式傳送數(shù)據(jù) --data-ascii <data> 以ascii的方式post數(shù)據(jù) --data-binary <data> 以二進制的方式post數(shù)據(jù) --negotiate 使用HTTP身份驗證 --digest 使用數(shù)字身份驗證 --disable-eprt 禁止使用EPRT或LPRT --disable-epsv 禁止使用EPSV -D/--dump-header <file> 把header信息寫入到該文件中 --egd-file <file> 為隨機數(shù)據(jù)(SSL)設置EGD socket路徑 --tcp-nodelay 使用TCP_NODELAY選項 -e/--referer 來源網(wǎng)址 -E/--cert <cert[:passwd]> 客戶端證書文件和密碼 (SSL) --cert-type <type> 證書文件類型 (DER/PEM/ENG) (SSL) --key <key> 私鑰文件名 (SSL) --key-type <type> 私鑰文件類型 (DER/PEM/ENG) (SSL) --pass <pass> 私鑰密碼 (SSL) --engine <eng> 加密引擎使用 (SSL). "--engine list" for list --cacert <file> CA證書 (SSL) --capath <directory> CA目錄 (made using c_rehash) to verify peer against (SSL) --ciphers <list> SSL密碼 --compressed 要求返回是壓縮的形勢 (using deflate or gzip) --connect-timeout <seconds> 設置最大請求時間 --create-dirs 建立本地目錄的目錄層次結(jié)構(gòu) --crlf 上傳是把LF轉(zhuǎn)變成CRLF -f/--fail 連接失敗時不顯示http錯誤 --ftp-create-dirs 如果遠程目錄不存在,創(chuàng)建遠程目錄 --ftp-method [multicwd/nocwd/singlecwd] 控制CWD的使用 --ftp-pasv 使用 PASV/EPSV 代替端口 --ftp-skip-pasv-ip 使用PASV的時候,忽略該IP地址 --ftp-ssl 嘗試用 SSL/TLS 來進行ftp數(shù)據(jù)傳輸 --ftp-ssl-reqd 要求用 SSL/TLS 來進行ftp數(shù)據(jù)傳輸 -F/--form <name=content> 模擬http表單提交數(shù)據(jù) -form-string <name=string> 模擬http表單提交數(shù)據(jù) -g/--globoff 禁用網(wǎng)址序列和范圍使用{}和[] -G/--get 以get的方式來發(fā)送數(shù)據(jù) -h/--help 幫助 -H/--header <line>自定義頭信息傳遞給服務器 --ignore-content-length 忽略的HTTP頭信息的長度 -i/--include 輸出時包括protocol頭信息 -I/--head 只顯示文檔信息 從文件中讀取-j/--junk-session-cookies忽略會話Cookie - 界面<interface>指定網(wǎng)絡接口/地址使用 - krb4 <級別>啟用與指定的安全級別krb4 -j/--junk-session-cookies 讀取文件進忽略session cookie --interface <interface> 使用指定網(wǎng)絡接口/地址 --krb4 <level> 使用指定安全級別的krb4 -k/--insecure 允許不使用證書到SSL站點 -K/--config 指定的配置文件讀取 -l/--list-only 列出ftp目錄下的文件名稱 --limit-rate <rate> 設置傳輸速度 --local-port<NUM> 強制使用本地端口號 -m/--max-time <seconds> 設置最大傳輸時間 --max-redirs <num> 設置最大讀取的目錄數(shù) --max-filesize <bytes> 設置最大下載的文件總量 -M/--manual 顯示全手動 -n/--netrc 從netrc文件中讀取用戶名和密碼 --netrc-optional 使用 .netrc 或者 URL來覆蓋-n --ntlm 使用 HTTP NTLM 身份驗證 -N/--no-buffer 禁用緩沖輸出 -o/--output 把輸出寫到該文件中 -O/--remote-name 把輸出寫到該文件中,保留遠程文件的文件名 -p/--proxytunnel 使用HTTP代理 --proxy-anyauth 選擇任一代理身份驗證方法 --proxy-basic 在代理上使用基本身份驗證 --proxy-digest 在代理上使用數(shù)字身份驗證 --proxy-ntlm 在代理上使用ntlm身份驗證 -P/--ftp-port <address> 使用端口地址,而不是使用PASV -Q/--quote <cmd>文件傳輸前,發(fā)送命令到服務器 -r/--range <range>檢索來自HTTP/1.1或FTP服務器字節(jié)范圍 --range-file 讀取(SSL)的隨機文件 -R/--remote-time 在本地生成文件時,保留遠程文件時間 --retry <num> 傳輸出現(xiàn)問題時,重試的次數(shù) --retry-delay <seconds> 傳輸出現(xiàn)問題時,設置重試間隔時間 --retry-max-time <seconds> 傳輸出現(xiàn)問題時,設置最大重試時間 -s/--silent靜音模式。不輸出任何東西 -S/--show-error 顯示錯誤 --socks4 <host[:port]> 用socks4代理給定主機和端口 --socks5 <host[:port]> 用socks5代理給定主機和端口 --stderr <file> -t/--telnet-option <OPT=val> Telnet選項設置 --trace <file> 對指定文件進行debug --trace-ascii <file> Like --跟蹤但沒有hex輸出 --trace-time 跟蹤/詳細輸出時,添加時間戳 -T/--upload-file <file> 上傳文件 --url <URL> Spet URL to work with -u/--user <user[:password]>設置服務器的用戶和密碼 -U/--proxy-user <user[:password]>設置代理用戶名和密碼 -v/--verbose -V/--version 顯示版本信息 -w/--write-out [format]什么輸出完成后 -x/--proxy <host[:port]>在給定的端口上使用HTTP代理 -X/--request <command>指定什么命令 -y/--speed-time 放棄限速所要的時間。默認為30 -Y/--speed-limit 停止傳輸速度的限制,速度時間'秒 -z/--time-cond 傳送時間設置 -0/--http1.0 使用HTTP 1.0 -1/--tlsv1 使用TLSv1(SSL) -2/--sslv2 使用SSLv2的(SSL) -3/--sslv3 使用的SSLv3(SSL) --3p-quote like -Q for the source URL for 3rd party transfer --3p-url 使用url,進行第三方傳送 --3p-user 使用用戶名和密碼,進行第三方傳送 -4/--ipv4 使用IP4 -6/--ipv6 使用IP6 -#/--progress-bar 用進度條顯示當前的傳送狀態(tài)

    一些常見的限制方式

    上述都是講的都是一些的基礎的知識,現(xiàn)在我就列一些比較常見的限制方式,如何突破這些限制抓取數(shù)據(jù)。

    Basic Auth 一般會有用戶授權的限制,會在headers的Autheration字段里要求加入;

    Referer 通常是在訪問鏈接時,必須要帶上Referer字段,服務器會進行驗證,例如抓取京東的評論;

    User-Agent 會要求真是的設備,如果不加會用編程語言包里自有User-Agent,可以被辨別出來;

    Cookie 一般在用戶登錄或者某些操作后,服務端會在返回包中包含Cookie信息要求瀏覽器設置Cookie,沒有Cookie會很容易被辨別出來是偽造請求;

    也有本地通過JS,根據(jù)服務端返回的某個信息進行處理生成的加密信息,設置在Cookie里面;

    Gzip 請求headers里面帶了gzip,返回有時候會是gzip壓縮,需要解壓;

    JavaScript 加密操作 一般都是在請求的數(shù)據(jù)包內(nèi)容里面會包含一些被javascript進行加密限制的信息,例如新浪微博會進行SHA1和RSA加密,之前是兩次SHA1加密,然后發(fā)送的密碼和用戶名都會被加密;

    其他字段 因為http的headers可以自定義地段,所以第三方可能會加入了一些自定義的字段名稱或者字段值,這也是需要注意的。

    真實的請求過程中,其實不止上面某一種限制,可能是幾種限制組合在一次,比如如果是類似RSA加密的話,可能先請求服務器得到Cookie,然后再帶著Cookie去請求服務器拿到公鑰,然后再用js進行加密,再發(fā)送數(shù)據(jù)到服務器。所以弄清楚這其中的原理,并且耐心分析很重要。

    防封禁策略

    Scrapy:

    http://doc.scrapy.org/en/master/topics/practices.html#avoiding-getting-banned

    如何讓你的 scrapy 爬蟲不再被 ban

    根據(jù) scrapy 官方文檔:https://docs.scrapy.org/en/master/topics/practices.html#avoiding-getting-banned?里面的描述,要防止 scrapy 被 ban,主要有以下幾個策略。

    • 動態(tài)設置 user agent
    • 禁用 cookies
    • 設置延遲下載
    • 使用?Google cache
    • 使用IP地址池(Tor project、VPN和代理IP)
    • 使用?Crawlera

    由于 Google cache 受國內(nèi)網(wǎng)絡的影響,你懂得; 所以主要從動態(tài)隨機設置user agent、禁用cookies、設置延遲下載和使用代理IP這幾個方式。

    本文以?cnblogs 為例

    創(chuàng)建 middlewares.py

    scrapy代理IP、user agent 的切換都是通過 DOWNLOADER_MIDDLEWARES 進行控制,下面我們創(chuàng)建 middlewares.py文件。

    [root@bogon cnblogs]# vi cnblogs/middlewares.py

    如下內(nèi)容:

    import random import base64 from settings import PROXIESclass RandomUserAgent(object):"""Randomly rotate user agents based on a list of predefined ones"""def __init__(self, agents):self.agents = agents@classmethoddef from_crawler(cls, crawler):return cls(crawler.settings.getlist('USER_AGENTS'))def process_request(self, request, spider):#print "**************************" + random.choice(self.agents)request.headers.setdefault('User-Agent', random.choice(self.agents))class ProxyMiddleware(object):def process_request(self, request, spider):proxy = random.choice(PROXIES)if proxy['user_pass'] is not None:#request.meta['proxy'] = "http://YOUR_PROXY_IP:PORT"request.meta['proxy'] = "http://%s" % proxy['ip_port']#proxy_user_pass = "USERNAME:PASSWORD"encoded_user_pass = base64.encodestring(proxy['user_pass'])request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_passprint "**************ProxyMiddleware have pass************" + proxy['ip_port']else:print "**************ProxyMiddleware no pass************" + proxy['ip_port']request.meta['proxy'] = "http://%s" % proxy['ip_port']

    類 RandomUserAgent 主要用來動態(tài)獲取 user agent,user agent 列表 USER_AGENTS 在 settings.py 中進行配置。

    類 ProxyMiddleware 用來切換代理,proxy 列表 PROXIES 也是在 settings.py 中進行配置。

    如果你用的是 socks5 代理,那么對不起,目前 scrapy 還不能直接支持,可以通過 Privoxy 等軟件將其本地轉(zhuǎn)化為 http 代理

    修改 settings.py 配置 USER_AGENTS 和 PROXIES

    USER_AGENTS = ["Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)","Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)","Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)","Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)","Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)","Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)","Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)","Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)","Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6","Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1","Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0","Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5","Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20","Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52", ]PROXIES = [{'ip_port': '111.11.228.75:80', 'user_pass': ''},{'ip_port': '120.198.243.22:80', 'user_pass': ''},{'ip_port': '111.8.60.9:8123', 'user_pass': ''},{'ip_port': '101.71.27.120:80', 'user_pass': ''},{'ip_port': '122.96.59.104:80', 'user_pass': ''},{'ip_port': '122.224.249.122:8088', 'user_pass': ''}, ]

    代理 IP可以網(wǎng)上搜索一下,上面的代理IP獲取自:http://www.xici.net.co/

    禁用 cookies:

    COOKIES_ENABLED = False

    設置下載延遲:

    DOWNLOAD_DELAY=3

    設置 DOWNLOADER_MIDDLEWARES?

    DOWNLOADER_MIDDLEWARES = {'cnblogs.middlewares.RandomUserAgent': 1,'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 110,'cnblogs.middlewares.ProxyMiddleware': 100, }

    保存 settings.py

    3、測試

    [root@bogon cnblogs]# scrapy crawl CnblogsSpider

    本文的 user agent 和 proxy 列表都是采用 settings.py 的方式進行設置的,實際生產(chǎn)中 user agent 和 proxy 有可能會經(jīng)常更新,每次更改配置文件顯得很笨拙也不便于管理。因而,可以根據(jù)需要保存在 mysql 數(shù)據(jù)庫

    SSL中間人監(jiān)測關鍵技術 --- SSL會話劫持

    數(shù)據(jù)流重定向技術是SSL中間人監(jiān)測的基礎,該技術的使用使得被監(jiān)測主機與SSL服務器的通信流量都會經(jīng)過監(jiān)測主機。對于一般的中間人監(jiān)測來說,再加上數(shù)據(jù)轉(zhuǎn)發(fā)機制就已經(jīng)足夠。但對于SSL中間人監(jiān)測來說,僅僅通過數(shù)據(jù)流重定向得到的都是經(jīng)過加密后的數(shù)據(jù),無法直接用來進行HTTP協(xié)議解析。故此必需使用SSL會話劫持技術,才能得到被監(jiān)測主機與SSL服務器之間通信數(shù)據(jù)的明文。

    自SSL問世以來,在其應用范圍越來越廣泛同時,多種針對SSL協(xié)議本身的缺陷或者其不規(guī)范引用的SSL會話劫持方法也隨之出現(xiàn),下面將詳細分析兩種典型的SSL會話劫持的實現(xiàn)原理和實現(xiàn)條件。

    一、利用偽造的 X.509 證書

    1.1 會話劫持原理

    當 SSL 客戶端與 SSL 服務端建立連接時,在正常的連接握手階段,客戶端必定會要求服務端出示其X.509公鑰證書,并根據(jù)以下3個要素驗證服務器證書的有效性:

    • a) 該公鑰證書的subject name(主題名)和所訪問的服務器站點的名稱是否一致;
    • b) 該公鑰證書的是否過期;
    • c) 該公鑰證書及其簽發(fā)者證書鏈中的證書的數(shù)字簽名是否有效(層層驗證,一直驗證到根CA證書為止)。

    當 SSL 客戶端訪問一個基于 HTTPS 的加密 Web 站點時,只要上述三個要素有一個驗證沒有通過,SSL 協(xié)議就會發(fā)出告警,大多數(shù)瀏覽器會彈出一個提示框,提示服務器證書存在的問題,但不會直接斷開SSL連接,而是讓用戶決定是否繼續(xù)。下圖展示了IE 瀏覽器彈出的安全警報提示框。

    大多數(shù)瀏覽器在驗證到服務器證書存在問題后的處理方式是存在巨大隱患的,因為用戶往往由于缺乏安全意識或者圖方便而選擇接受不安全的證書,這就使得偽造一個和合法證書極為相似的“偽證書”騙取 SSL 客戶端用戶信任的手段成為可能。下圖展示了這種 SSL 會話劫持的主要流程(圖中,C 為 SSL 客戶端,M 為監(jiān)測主機,S 為 SSL 服務端)

    上圖就是基于偽造證書進行劫持的流程,文字描述如下 :主機M通過數(shù)據(jù)流重定向技術,使得主機C與主機S之間的通信流量都流向主機M,主機C本欲與主機S建立SSL連接,但發(fā)送的連接建立請求被重定向到了主機M; 主機C首先與主機M建立TCP連接,然后向主機M發(fā)起SSL連接請求; 主機M收到來自主機C的連接請求后,首先與主機S建立TCP連接,然后向主機S發(fā)起SSL連接請求; 主機S響應主機M的請求,由此主機M與主機S之間成功建立SSL連接,主機M同時獲得主機S的X.509公鑰證書Certificate_S; 主機M根據(jù)Certificate_S中的關鍵信息(主要是subject name、有效期限等)偽造一個極相似的自簽名證書Certificate_S’,并以此證書響應第②步中,來自主機C的SSL連接請求; 主機C的瀏覽器驗證Certificate_S’的有效性,發(fā)現(xiàn)subject name與請求的站點名稱一致,證書還在有效期內(nèi),但是并非由信任的機構(gòu)頒發(fā)。于是彈出提示框,讓用戶選擇是否繼續(xù)。由于Certificate_S’與Certificate_S從外表上幾乎看不出來差別,大部分用戶會選擇繼續(xù)( 這是SSL會話劫持可以成功的關鍵 ),由此主機C與主機M成功建立SSL連接。 這樣以后,主機C發(fā)往SSL服務端的數(shù)據(jù),主機M可以捕獲并解密查看;主機S返回給SSL客戶端的數(shù)據(jù),主機M也可以捕獲并解密查看。至此,主機M實現(xiàn)了完整的SSL中間人監(jiān)測。 經(jīng)過以上步驟,主機M成功實現(xiàn)了主機C(SSL客戶端)與主機S(SSL服務端)之間的會話劫持,并可以對明文形式的會話內(nèi)容進行監(jiān)測。

    1.2 成功的必要條件 這種類型的 SSL 會話劫持成功的必要條件如下:

    • a) 能夠通過ARP欺騙、DNS欺騙或者瀏覽器數(shù)據(jù)重定向等欺騙技術,使得SSL客戶端C和服務端S之間的數(shù)據(jù)都流向中間人監(jiān)測主機;
    • b) SSL客戶端在接收到偽造的X.509證書后,用戶選擇信任該證書,并繼續(xù)SSL連接;
    • c) SSL服務端未要求對SSL客戶端進行認證。

    二、利用 HTTP 與 HTTPS 之間跳轉(zhuǎn)的驗證漏洞

    2.1 會話劫持原理

    用戶瀏覽網(wǎng)頁時,使用 SSL 協(xié)議的方式一般有兩種。一種是在瀏覽器地址欄輸入網(wǎng)址時直接指定協(xié)議類型為HTTPS,另一種是通過HTTP響應的302狀態(tài)將網(wǎng)頁重定向到HTTPS 鏈接。2009年2月在美國拉斯維加斯舉行的BlackHat黑客大會上,安全研究人員Moxie Marlinspike 演示了通過自己研發(fā)的SSLstrip工具劫持SSL會話來截獲注冊數(shù)據(jù)的方法,為SSL會話劫持提供了新思路。

    SSLstrip 使用了社會工程學的原理:許多人為了圖方便省事,在輸入網(wǎng)址時一般不考慮傳輸協(xié)議,習慣上只是簡單輸入主機名,瀏覽器默認在這種情況下會使用 HTTP 協(xié)議。例如用戶為了使用Gmail郵箱,直接輸入accounts.google.com,瀏覽器會給谷歌服務器發(fā)送一個HTTP 請求,谷歌服務器認為電子郵件屬于應加密的重要事務,使用HTTP不恰當,應改為使用HTTPS,于是它返回一個狀態(tài)碼為302的HTTP 響應,給出一個重定向網(wǎng)址https://accounts.google.com/ServiceLogin?passive=1209600&continue=https%3A%2F%2Faccounts.google.com%2FManageAccount&followup=https%3A%2F%2Faccounts.google.com%2FManageAccount,瀏覽器再使用這個重定向網(wǎng)址發(fā)出HTTPS?請求。 一個原本應該從頭到尾使用HTTPS加密會話的過程中混入了使用明文傳輸?shù)腍TTP會話,一旦HTTP會話被劫持,HTTPS會話就可能受到威脅 。SSLstrip 正是利用這一點,通過劫持HTTP 會話劫持了SSL會話,下圖所示 SSLstrip 原理示意圖。

    下面具體闡述基于SSLstrip的SSL會話劫持流程(闡述中依然以主機C為SSL客戶端,主機M為監(jiān)測主機,主機S為SSL服務端):主機M通過ARP重定向技術,使得主機C所有與外網(wǎng)的通信流都會從主機M處經(jīng)過。 主機C向主機S的一個HTTPS頁面發(fā)出一個HTTP請求,主機M監(jiān)聽這個請求并轉(zhuǎn)發(fā)給主機S。 主機S返回一個狀態(tài)碼為302的HTTP 響應報文,報文消息頭中Location頭域以及消息實體中都給出了重定向網(wǎng)址,形式分別為,“Location:?https://***.com/…”與“”。 主機M解析來自主機S的響應報文,將其中所有的https替換成http,指定主機M另一個未使用的端口為通信端口(假設為8181端口),并且記錄修改過的url。需要做的替換包括:消息頭中的“Location:?https://***.com/…”替換成“Location:?http://***.com:8181/…”;消息實體中鏈接“< a href=”https://***.com/…”>”替換成“”。 主機C解析經(jīng)過篡改后的HTTP響應報文,經(jīng)過重定向與主機M的8181端口通過HTTP方式建立了連接,二者之間通信數(shù)據(jù)明文傳輸。 主機M冒充客戶端與主機S建立HTTPS會話,二者之間的通信數(shù)據(jù)通過密文傳輸,但主機M可以自由地解密這些數(shù)據(jù)。 經(jīng)過以上步驟,主機M成功實現(xiàn)了主機C(SSL客戶端)與主機S(SSL服務端)之間的會話劫持,并可以對明文形式的會話內(nèi)容進行監(jiān)測。

    2.2 成功的必要條件 這種類型的SSL會話劫持成功的必要條件如下:

    • a) 能夠通過ARP欺騙、DNS欺騙或者瀏覽器數(shù)據(jù)重定向等欺騙技術,使得SSL客戶端和服務端S之間的數(shù)據(jù)都流向中間人監(jiān)測主機;
    • b) 客戶端訪問的Web頁面存在http頁面至https頁面的跳轉(zhuǎn);
    • c) SSL服務端未要求對SSL客戶端進行認證。

    三、兩種典型 SSL 會話劫持技術的對比小結(jié)

    傳統(tǒng)的基于偽造 X.509 證書的 SSL 會話劫持方式,其最大的問題就在于客戶端瀏覽器會彈出警告提示對話框,這個提示是如此醒目,以至于只要用戶有一定的安全意識和網(wǎng)絡知識,劫持成功的概率就會大大降低。隨著網(wǎng)絡知識的慢慢普及,這種技術的生存空間會越來越小。

    基于 HTTP 與 HTTPS 之間跳轉(zhuǎn)驗證漏洞的 SSL 會話劫持方式,是近幾年新出的一種技術。在此種方式下,客戶端瀏覽器不會有任何不安全的警告或提示,只是原先的HTTPS連接已經(jīng)被HTTP連接所替換,迷惑性大大增強。一般為了進一步加強欺騙效果,監(jiān)測主機還可以一個銀色的“安全鎖”圖案顯示在非安全的網(wǎng)址前面。但其缺陷也很明顯,一旦用戶在瀏覽器地址欄的輸入中指定使用HTTPS協(xié)議,就會發(fā)現(xiàn)網(wǎng)頁根本無法打開。因此只要用戶養(yǎng)成良好的上網(wǎng)習慣,這種方式的會話劫持就會失敗。

    安裝 pycurl

    安裝命令:

    sudo apt-get install libcurl4-openssl-dev pip install pycurl

    雜項

  • scrapy

    了解 scrapy 已經(jīng)做過的功能,優(yōu)化等。。。防止重復造輪子,如,去重,編碼檢測,dns緩存,http長連接,gzip等等。

  • JS相關。

    這個是被問的最多的。看具體情況解決。可模擬相關js執(zhí)行、繞過,或直接調(diào)瀏覽器去訪問。自己用一個JS引擎+模擬一個瀏覽器環(huán)境難度太大了(參見V8的DEMO)。

    調(diào)瀏覽器有很多方法。難以細說,關鍵字如下,selenium,phantomjs,casperjs,ghost,webkit,scrapyjs,splash。一些細節(jié)如關掉CSS渲染,圖片加載等。只有scrapyjs是完全異步的,相對是速度最快的,scrapyjs將webkit的事件循環(huán)和twisted的事件循環(huán)合在一起了。其他的方案要么阻塞,要么用多進程。簡單的js需求(對效率要求不高)隨意選,最優(yōu)方案是scrapyjs+定制webkit(去掉不需要的功能)。調(diào)瀏覽器開頁面是比較耗資源的(主要是cpu)

  • 內(nèi)容解析。

    對于頁面解析最強大的當然是XPATH、css選擇器、正則表達式,這個對于不同網(wǎng)站不同的使用者都不一樣,就不用過多的說明,附兩個比較好的網(wǎng)址:

    正則表達式入門:http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html

    正則表達式在線測試:http://tool.oschina.net/regex/

    其次就是解析庫了,常用的有兩個lxml和BeautifulSoup,對于這兩個的使用介紹兩個比較好的網(wǎng)站:

    lxml:http://my.oschina.net/jhao104/blog/639448

    BeautifulSoup:http://cuiqingcai.com/1319.html

    對于這兩個庫,都是HTML/XML的處理庫,Beautifulsoup純python實現(xiàn),效率低,但是功能實用,比如能用通過結(jié)果搜索獲得某個HTML節(jié)點的源碼;lxmlC語言編碼,高效,支持Xpath

    機器學習不一定好用(效果問題,人工問題-需要訓練)。還有寫些正則去模糊匹配。

    新聞類似的正文提取有readability,boilerplate。

  • 分布式。

    首先考慮按任務(目標)切分,然后讓不同目標的爬蟲在不同機器上跑

    完全的對等分布式(多爬蟲爬一個目標),把任務隊列替換掉爬蟲改改即可。github里面有幾個現(xiàn)有的實現(xiàn)參考。

    分布式需求可能是偽命題。想清楚為何要分布式。硬件夠不夠,像什么拿一個不支持持久化的url隊列的爬蟲說量大需要分布式的,我只能默念,你為何這么吊。

  • 部署,調(diào)度

    部署推薦scrapyd。這也是官方推薦的方法。

    大量爬蟲的調(diào)度,這個目前(13-10)沒有現(xiàn)成的合適方法,期望是實現(xiàn)爬蟲的某些配置放數(shù)據(jù)庫,提供web后臺 ,然后按配置周期、定時運行爬蟲,終止,暫停爬蟲等等。可以實現(xiàn),但要自己寫不少東西。

  • ip 限制問題

    買的起大量ip的可買(買大量同網(wǎng)段爬可能導致整網(wǎng)段被封)。

    找大量免費的開放http代理,篩選可用的,免費開放代理不可靠,寫個調(diào)度機制,自動根據(jù)成功次數(shù),延遲等選擇合適代理,這個功能難以在scrapy內(nèi)實現(xiàn),參考scrapinghub的crawlera,我完成了一個本地版。

    在開發(fā)爬蟲過程中經(jīng)常會遇到IP被封掉的情況,這時就需要用到代理IP;

    在urllib2包中有ProxyHandler類,通過此類可以設置代理訪問網(wǎng)頁,如下代碼片段:

    import urllib2proxy = urllib2.ProxyHandler({'http': '127.0.0.1:8087'})opener = urllib2.build_opener(proxy)urllib2.install_opener(opener)response = urllib2.urlopen('http://www.baidu.com')print response.read()
  • url 去重

    如果有千萬級的URL需要去重,需要仔細看下scrapy的去重機制和bloom filter(布隆過濾器)。bloomfilter有個公式可以算需要多少內(nèi)存。另bloomfilter + scrapy在github有現(xiàn)有實現(xiàn)可以參考。

  • 存儲。

    mongodb,mongodb不滿足某些功能時考慮hbase,參考http://blog.scrapinghub.com/2013/05/13/mongo-bad-for-scraped-data/

  • 硬件

    硬件扛不住別玩爬蟲。。。曾在I3 4G 1T上跑爬蟲。卡在磁盤io(量大,磁盤io差,內(nèi)存低),出現(xiàn)內(nèi)存占用飆升。很難調(diào)試(調(diào)試爬蟲看實際跑耗時較長),初步以為是爬蟲有問題內(nèi)存占用高導致數(shù)據(jù)庫卡。調(diào)試結(jié)果確認為,配置低量太大,導致數(shù)據(jù)庫慢,數(shù)據(jù)庫慢之后爬蟲任務隊列占滿內(nèi)存并開始寫磁盤,又循環(huán)導致數(shù)據(jù)庫慢。

  • 爬蟲監(jiān)控

    scrapyd自帶簡單的監(jiān)控,不夠的話用scrapy的webservice自己寫

  • 如何防止死循環(huán)

    在 Scrapy 的默認配置中,是根據(jù) url 進行去重的。這個對付一般網(wǎng)站是夠的。但是有一些網(wǎng)站的 SEO 做的很變態(tài):為了讓爬蟲多抓,會根據(jù) request,動態(tài)的生成一些鏈接,導致爬蟲 在網(wǎng)站上抓取大量的隨機頁面,甚至是死循環(huán)。。

    為了解決這個問題,有2個方案:

    (1) 在 setting.py 中,設定爬蟲的嵌套次數(shù)上限(全局設定,實際是通過 DepthMiddleware 實現(xiàn)的):

    DEPTH_LIMIT = 20

    (2) 在 parse 中通過讀取 response 來自行判斷( spider級別設定 ) :

    def parse(self, response):if response.meta['depth'] > 100:print 'Loop?'

    學習爬蟲的正確打開方式

    看了大部分回答不禁嘆口氣,主要是因為看到很多大牛在回答像? "如何入門爬蟲"? 這種問題的時候,一如當年學霸講解題目,跳步無數(shù),然后留下一句? "?不就是這樣推嘛 ",讓一眾小白菜鳥一臉懵逼。。作為一個0起步(之前連python都不會),目前總算掌握基礎,開始向上進階的菜鳥,深知其中的不易,所以我會在這個回答里,盡可能全面、細節(jié)地分享給大家從0學習爬蟲的各種步驟,如果對你有幫助,請點贊~


    首先!你要對爬蟲有個明確的認識。。。

    在戰(zhàn)略上藐視:

    “所有網(wǎng)站皆可爬”:互聯(lián)網(wǎng)的內(nèi)容都是人寫出來的,而且都是偷懶寫出來的(不會第一頁是a,下一頁是8),所以肯定有規(guī)律,這就給人有了爬取的可能,可以說,天下沒有不能爬的網(wǎng)站 “框架不變”:網(wǎng)站不同,但是原理都類似,大部分爬蟲都是從 發(fā)送請求——獲得頁面——解析頁面——下載內(nèi)容——儲存內(nèi)容 這樣的流程來進行,只是用的工具不同

    在戰(zhàn)術上重視:

    持之以恒,戒驕戒躁:對于初學入門,不可輕易自滿,以為爬了一點內(nèi)容就什么都會爬了,爬蟲雖然是比較簡單的技術,但是往深學也是沒有止境的(比如搜索引擎等)!只有不斷嘗試,刻苦鉆研才是王道!(為何有種小學作文即視感)

    然后,你需要一個宏偉的目標,來讓你有持續(xù)學習的動力(沒有實操項目,真的很難有動力)

    我要爬整個豆瓣!... 我要爬整個草什么榴社區(qū)!我要爬知乎各種妹子的聯(lián)系方式*&^#%^$#

    接著,你需要捫心自問一下,自己的 python 基本功吼不吼啊? 吼啊!——OK,開始歡快地學習爬蟲吧 !

    不吼?你還需要學習一個!趕緊回去看Python核心編程教程。至少這些功能和語法你要有基本的掌握 : list,dict:用來序列化你爬的東西 切片:用來對爬取的內(nèi)容進行分割,生成 條件判斷(if等):用來解決爬蟲過程中哪些要哪些不要的問題 循環(huán)和迭代(for while ):用來循環(huán),重復爬蟲動作 文件讀寫操作:用來讀取參數(shù)、保存爬下來的內(nèi)容等

    然后,你需要補充一下下面幾個內(nèi)容,作為你的知識儲備:

    (注:這里并非要求“掌握”,下面講的兩點,只需要先了解,然后通過具體項目來不斷實踐,直到熟練掌握)

    1、網(wǎng)頁的基本知識:

    基本的HTML語言知識(知道href等大學計算機一級內(nèi)容即可)

    理解網(wǎng)站的發(fā)包和收包的概念(POST GET)

    稍微一點點的js知識,用于理解動態(tài)網(wǎng)頁(當然如果本身就懂當然更好啦)

    2、一些分析語言,為接下來解析網(wǎng)頁內(nèi)容做準備

    NO.1 正則表達式:扛把子技術,總得會最基礎的:

    NO.2 XPATH:高效的分析語言,表達清晰簡單,掌握了以后基本可以不用正則

    參考:XPath 教程[http://link.zhihu.com/?target=http%3A//www.w3school.com.cn/xpath/]

    NO.3 Beautifulsoup:

    美麗湯模塊解析網(wǎng)頁神器,一款神器,如果不用一些爬蟲框架(如后文講到的scrapy),配合request,urllib等模塊(后面會詳細講),可以編寫各種小巧精干的爬蟲腳本

    官網(wǎng)文檔:Beautiful Soup 4.2.0 文檔[http://link.zhihu.com/?target=http%3A//beautifulsoup.readthedocs.org/zh_CN/latest/]

    參考案例:

    接著,你需要一些高效的工具來輔助

    (同樣,這里先了解,到具體的項目的時候,再熟悉運用)

    NO.1 F12 開發(fā)者工具:

    看源代碼:快速定位元素

    分析xpath:1、此處建議谷歌系瀏覽器,可以在源碼界面直接右鍵看

    NO.2 抓包工具:

    推薦httpfox,火狐瀏覽器下的插件,比谷歌火狐系自帶的F12工具都要好,可以方便查看網(wǎng)站收包發(fā)包的信息

    NO.3 XPATH CHECKER (火狐插件):

    非常不錯的 xpath 測試工具,但是有幾個坑,都是個人踩過的,,在此告誡大家:

    • 1、xpath checker 生成的是絕對路徑,遇到一些動態(tài)生成的圖標(常見的有列表翻頁按鈕等),飄忽不定的絕對路徑很有可能造成錯誤,所以這里建議在真正分析的時候,只是作為參考
    • 2、記得把如下圖 xpath 框里的“x:”去掉,貌似這個是早期版本xpath的語法,目前已經(jīng)和一些模塊不兼容(比如scrapy),還是刪去避免報錯

    NO.4 正則表達測試工具:

    在線正則表達式測試(http://link.zhihu.com/?target=http%3A//tool.oschina.net/regex/) ,拿來多練練手,也輔助分析!里面有很多現(xiàn)成的正則表達式可以用,也可以進行參考!

    ok!這些你都基本有一些了解了,現(xiàn)在開始進入抓取時間,上各種模塊吧!python 的火,很大原因就是各種好用的模塊,這些模塊是居家旅行爬網(wǎng)站常備的——

    urllib urllib2 requests

    **不想重復造輪子,有沒有現(xiàn)成的框架? 華麗麗的scrapy(這塊我會重點講,我的最愛)**

    遇到動態(tài)頁面怎么辦?

    selenium,會了這個配合 scrapy 無往不利,是居家旅行爬網(wǎng)站又一神器,

    爬來的東西怎么用?

    pandas(基于 numpy 的數(shù)據(jù)分析模塊,相信我,如果你不是專門搞 TB 級數(shù)據(jù)的,這個就夠了)

    然后是數(shù)據(jù)庫,這里我認為開始并不需要非常深入,在需要的時候再學習即可

    mysql mongodb sqllite

    遇到反爬蟲策略驗證碼之類咋整?

    PIL opencv pybrain

    進階技術

    多線程、分布式

    Python 網(wǎng)頁爬蟲 & 文本處理 & 科學計算 & 機器學習 & 數(shù)據(jù)挖掘兵器譜

    曾經(jīng)因為NLTK的緣故開始學習Python,之后漸漸成為我工作中的第一輔助腳本語言,雖然開發(fā)語言是C/C++,但平時的很多文本數(shù)據(jù)處理任務都交給了Python。離開騰訊創(chuàng)業(yè)后,第一個作品課程圖譜也是選擇了Python系的Flask框架,漸漸的將自己的絕大部分工作交給了Python。這些年來,接觸和使用了很多Python工具包,特別是在文本處理,科學計算,機器學習和數(shù)據(jù)挖掘領域,有很多很多優(yōu)秀的Python工具包可供使用,所以作為Pythoner,也是相當幸福的。其實如果仔細留意微博,你會發(fā)現(xiàn)很多這方面的分享,自己也Google了一下,發(fā)現(xiàn)也有同學總結(jié)了“Python機器學習庫”,不過總感覺缺少點什么。最近流行一個詞,全棧工程師(full stack engineer),作為一個苦逼的創(chuàng)業(yè)者,天然的要把自己打造成一個full stack engineer,而這個過程中,這些Python工具包給自己提供了足夠的火力,所以想起了這個系列。當然,這也僅僅是拋磚引玉,希望大家能提供更多的線索,來匯總整理一套Python網(wǎng)頁爬蟲,文本處理,科學計算,機器學習和數(shù)據(jù)挖掘的兵器譜。

    一、Python網(wǎng)頁爬蟲工具集

    一個真實的項目,一定是從獲取數(shù)據(jù)開始的。無論文本處理,機器學習和數(shù)據(jù)挖掘,都需要數(shù)據(jù),除了通過一些渠道購買或者下載的專業(yè)數(shù)據(jù)外,常常需要大家自己動手爬數(shù)據(jù),這個時候,爬蟲就顯得格外重要了,幸好,Python提供了一批很不錯的網(wǎng)頁爬蟲工具框架,既能爬取數(shù)據(jù),也能獲取和清洗數(shù)據(jù),我們也就從這里開始了:

    1.?Scrapy

    Scrapy, a fast high-level screen scraping and web crawling framework for Python.

    鼎鼎大名的Scrapy,相信不少同學都有耳聞,課程圖譜中的很多課程都是依靠Scrapy抓去的,這方面的介紹文章有很多,推薦大牛pluskid早年的一篇文章:《Scrapy 輕松定制網(wǎng)絡爬蟲》,歷久彌新。

    官方主頁:http://scrapy.org/
    Github代碼頁:?https://github.com/scrapy/scrapy

    2.?Beautiful Soup

    You didn’t write that awful page. You’re just trying to get some data out of it. Beautiful Soup is here to help. Since 2004, it’s been saving programmers hours or days of work on quick-turnaround screen scraping projects.

    讀書的時候通過《集體智慧編程》這本書知道Beautiful Soup的,后來也偶爾會用用,非常棒的一套工具。客觀的說,Beautifu Soup不完全是一套爬蟲工具,需要配合urllib使用,而是一套HTML/XML數(shù)據(jù)分析,清洗和獲取工具。

    官方主頁:http://www.crummy.com/software/BeautifulSoup/

    3.?Python-Goose

    Html Content / Article Extractor, web scrapping lib in Python

    Goose最早是用Java寫得,后來用Scala重寫,是一個Scala項目。Python-Goose用Python重寫,依賴了Beautiful Soup。前段時間用過,感覺很不錯,給定一個文章的URL, 獲取文章的標題和內(nèi)容很方便。

    Github主頁:https://github.com/grangier/python-goose

    二、Python文本處理工具集

    從網(wǎng)頁上獲取文本數(shù)據(jù)之后,依據(jù)任務的不同,就需要進行基本的文本處理了,譬如對于英文來說,需要基本的tokenize,對于中文,則需要常見的中文分詞,進一步的話,無論英文中文,還可以詞性標注,句法分析,關鍵詞提取,文本分類,情感分析等等。這個方面,特別是面向英文領域,有很多優(yōu)秀的工具包,我們一一道來。

    1.?NLTK?— Natural Language Toolkit

    NLTK is a leading platform for building Python programs to work with human language data. It provides easy-to-use interfaces to over 50 corpora and lexical resources such as WordNet, along with a suite of text processing libraries for classification, tokenization, stemming, tagging, parsing, and semantic reasoning, and an active discussion forum.

    搞自然語言處理的同學應該沒有人不知道NLTK吧,這里也就不多說了。不過推薦兩本書籍給剛剛接觸NLTK或者需要詳細了解NLTK的同學: 一個是官方的《Natural Language Processing with Python》,以介紹NLTK里的功能用法為主,同時附帶一些Python知識,同時國內(nèi)陳濤同學友情翻譯了一個中文版,這里可以看到:推薦《用Python進行自然語言處理》中文翻譯-NLTK配套書;另外一本是《Python Text Processing with NLTK 2.0 Cookbook》,這本書要深入一些,會涉及到NLTK的代碼結(jié)構(gòu),同時會介紹如何定制自己的語料和模型等,相當不錯。

    官方主頁:http://www.nltk.org/
    Github代碼頁:https://github.com/nltk/nltk

    2.?Pattern

    Pattern is a web mining module for the Python programming language.

    It has tools for data mining (Google, Twitter and Wikipedia API, a web crawler, a HTML DOM parser), natural language processing (part-of-speech taggers, n-gram search, sentiment analysis, WordNet), machine learning (vector space model, clustering, SVM), network analysis and canvas visualization.

    Pattern由比利時安特衛(wèi)普大學CLiPS實驗室出品,客觀的說,Pattern不僅僅是一套文本處理工具,它更是一套web數(shù)據(jù)挖掘工具,囊括了數(shù)據(jù)抓取模塊(包括Google, Twitter, 維基百科的API,以及爬蟲和HTML分析器),文本處理模塊(詞性標注,情感分析等),機器學習模塊(VSM, 聚類,SVM)以及可視化模塊等,可以說,Pattern的這一整套邏輯也是這篇文章的組織邏輯,不過這里我們暫且把Pattern放到文本處理部分。我個人主要使用的是它的英文處理模塊Pattern.en, 有很多很不錯的文本處理功能,包括基礎的tokenize, 詞性標注,句子切分,語法檢查,拼寫糾錯,情感分析,句法分析等,相當不錯。

    官方主頁:http://www.clips.ua.ac.be/pattern

    3.?TextBlob: Simplified Text Processing

    TextBlob is a Python (2 and 3) library for processing textual data. It provides a simple API for diving into common natural language processing (NLP) tasks such as part-of-speech tagging, noun phrase extraction, sentiment analysis, classification, translation, and more.

    TextBlob是一個很有意思的Python文本處理工具包,它其實是基于上面兩個Python工具包NLKT和Pattern做了封裝(TextBlob stands on the giant shoulders of NLTK and pattern, and plays nicely with both),同時提供了很多文本處理功能的接口,包括詞性標注,名詞短語提取,情感分析,文本分類,拼寫檢查等,甚至包括翻譯和語言檢測,不過這個是基于Google的API的,有調(diào)用次數(shù)限制。TextBlob相對比較年輕,有興趣的同學可以關注。

    官方主頁:http://textblob.readthedocs.org/en/dev/
    Github代碼頁:https://github.com/sloria/textblob

    4.?MBSP?for Python

    MBSP is a text analysis system based on the TiMBL and MBT memory based learning applications developed at CLiPS and ILK. It provides tools for Tokenization and Sentence Splitting, Part of Speech Tagging, Chunking, Lemmatization, Relation Finding and Prepositional Phrase Attachment.

    MBSP與Pattern同源,同出自比利時安特衛(wèi)普大學CLiPS實驗室,提供了Word Tokenization, 句子切分,詞性標注,Chunking, Lemmatization,句法分析等基本的文本處理功能,感興趣的同學可以關注。

    官方主頁:http://www.clips.ua.ac.be/pages/MBSP

    5.?Gensim: Topic modeling for humans

    Gensim是一個相當專業(yè)的主題模型Python工具包,無論是代碼還是文檔,我們曾經(jīng)用《如何計算兩個文檔的相似度》介紹過Gensim的安裝和使用過程,這里就不多說了。

    官方主頁:http://radimrehurek.com/gensim/index.html
    github代碼頁:https://github.com/piskvorky/gensim

    6.?langid.py: Stand-alone language identification system

    語言檢測是一個很有意思的話題,不過相對比較成熟,這方面的解決方案很多,也有很多不錯的開源工具包,不過對于Python來說,我使用過langid這個工具包,也非常愿意推薦它。langid目前支持97種語言的檢測,提供了很多易用的功能,包括可以啟動一個建議的server,通過json調(diào)用其API,可定制訓練自己的語言檢測模型等,可以說是“麻雀雖小,五臟俱全”。

    Github主頁:https://github.com/saffsd/langid.py

    7.?Jieba: 結(jié)巴中文分詞

    “結(jié)巴”中文分詞:做最好的Python中文分詞組件 “Jieba” (Chinese for “to stutter”) Chinese text segmentation: built to be the best Python Chinese word segmentation module.

    好了,終于可以說一個國內(nèi)的Python文本處理工具包了:結(jié)巴分詞,其功能包括支持三種分詞模式(精確模式、全模式、搜索引擎模式),支持繁體分詞,支持自定義詞典等,是目前一個非常不錯的Python中文分詞解決方案。

    Github主頁:https://github.com/fxsjy/jieba

    8.?xTAS

    xtas, the eXtensible Text Analysis Suite, a distributed text analysis package based on Celery and Elasticsearch.

    感謝微博朋友?@大山坡的春?提供的線索:我們組同事之前發(fā)布了xTAS,也是基于python的text mining工具包,歡迎使用,鏈接:http://t.cn/RPbEZOW。看起來很不錯的樣子,回頭試用一下。

    Github代碼頁:https://github.com/NLeSC/xtas

    三、Python 科學計算工具包

    說起科學計算,大家首先想起的是 Matlab,集數(shù)值計算,可視化工具及交互于一身,不過可惜是一個商業(yè)產(chǎn)品。開源方面除了GNU Octave在嘗試做一個類似Matlab的工具包外,Python的這幾個工具包集合到一起也可以替代Matlab的相應功能:NumPy+SciPy+Matplotlib+iPython。同時,這幾個工具包,特別是NumPy和SciPy,也是很多Python文本處理 & 機器學習 & 數(shù)據(jù)挖掘工具包的基礎,非常重要。最后再推薦一個系列《用Python做科學計算》,將會涉及到NumPy, SciPy, Matplotlib,可以做參考。

    1.?NumPy

    NumPy is the fundamental package for scientific computing with Python. It contains among other things:
    1)a powerful N-dimensional array object
    2)sophisticated (broadcasting) functions
    3)tools for integrating C/C++ and Fortran code
    4) useful linear algebra, Fourier transform, and random number capabilities

    Besides its obvious scientific uses, NumPy can also be used as an efficient multi-dimensional container of generic data. Arbitrary data-types can be defined. This allows NumPy to seamlessly and speedily integrate with a wide variety of databases.

    NumPy幾乎是一個無法回避的科學計算工具包,最常用的也許是它的N維數(shù)組對象,其他還包括一些成熟的函數(shù)庫,用于整合C/C++和Fortran代碼的工具包,線性代數(shù)、傅里葉變換和隨機數(shù)生成函數(shù)等。NumPy提供了兩種基本的對象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray是存儲單一數(shù)據(jù)類型的多維數(shù)組,而ufunc則是能夠?qū)?shù)組進行處理的函數(shù)。

    官方主頁:http://www.numpy.org/

    2.?SciPy:Scientific Computing Tools for Python

    SciPy refers to several related but distinct entities:

    1)The SciPy Stack, a collection of open source software for scientific computing in Python, and particularly a specified set of core packages.
    2)The community of people who use and develop this stack.
    3)Several conferences dedicated to scientific computing in Python – SciPy, EuroSciPy and SciPy.in.
    4)The SciPy library, one component of the SciPy stack, providing many numerical routines.

    “SciPy是一個開源的Python算法庫和數(shù)學工具包,SciPy包含的模塊有最優(yōu)化、線性代數(shù)、積分、插值、特殊函數(shù)、快速傅里葉變換、信號處理和圖像處理、常微分方程求解和其他科學與工程中常用的計算。其功能與軟件MATLAB、Scilab和GNU Octave類似。 Numpy和Scipy常常結(jié)合著使用,Python大多數(shù)機器學習庫都依賴于這兩個模塊。”—-引用自“Python機器學習庫”

    官方主頁:http://www.scipy.org/

    3.?Matplotlib

    matplotlib is a python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms. matplotlib can be used in python scripts, the python and ipython shell (ala MATLAB?* or Mathematica??), web application servers, and six graphical user interface toolkits.

    matplotlib 是python最著名的繪圖庫,它提供了一整套和matlab相似的命令API,十分適合交互式地進行制圖。而且也可以方便地將它作為繪圖控件,嵌入GUI應用程序中。Matplotlib可以配合ipython shell使用,提供不亞于Matlab的繪圖體驗,總之用過了都說好。

    官方主頁:http://matplotlib.org/

    4.?iPython

    IPython provides a rich architecture for interactive computing with:

    1)Powerful interactive shells (terminal and Qt-based).
    2)A browser-based notebook with support for code, text, mathematical expressions, inline plots and other rich media.
    3)Support for interactive data visualization and use of GUI toolkits.
    4)Flexible, embeddable interpreters to load into your own projects.
    5)Easy to use, high performance tools for parallel computing.

    “iPython 是一個Python 的交互式Shell,比默認的Python Shell 好用得多,功能也更強大。 她支持語法高亮、自動完成、代碼調(diào)試、對象自省,支持 Bash Shell 命令,內(nèi)置了許多很有用的功能和函式等,非常容易使用。 ” 啟動iPython的時候用這個命令“ipython –pylab”,默認開啟了matploblib的繪圖交互,用起來很方便。

    官方主頁:http://ipython.org/

    四、Python 機器學習 & 數(shù)據(jù)挖掘 工具包

    機器學習和數(shù)據(jù)挖掘這兩個概念不太好區(qū)分,這里就放到一起了。這方面的開源Python工具包有很多,這里先從熟悉的講起,再補充其他來源的資料,也歡迎大家補充。

    1.?scikit-learn: Machine Learning in Python

    scikit-learn (formerly scikits.learn) is an open source machine learning library for the Python programming language. It features various classification, regression and clustering algorithms including support vector machines, logistic regression, naive Bayes, random forests, gradient boosting, k-means and DBSCAN, and is designed to interoperate with the Python numerical and scientific libraries NumPy and SciPy.

    首先推薦大名鼎鼎的scikit-learn,scikit-learn是一個基于NumPy, SciPy, Matplotlib的開源機器學習工具包,主要涵蓋分類,回歸和聚類算法,例如SVM, 邏輯回歸,樸素貝葉斯,隨機森林,k-means等算法,代碼和文檔都非常不錯,在許多Python項目中都有應用。例如在我們熟悉的NLTK中,分類器方面就有專門針對scikit-learn的接口,可以調(diào)用scikit-learn的分類算法以及訓練數(shù)據(jù)來訓練分類器模型。這里推薦一個視頻,也是我早期遇到scikit-learn的時候推薦過的:推薦一個Python機器學習工具包Scikit-learn以及相關視頻–Tutorial: scikit-learn – Machine Learning in Python

    官方主頁:http://scikit-learn.org/

    2.?Pandas: Python Data Analysis Library

    Pandas is a software library written for the Python programming language for data manipulation and analysis. In particular, it offers data structures and operations for manipulating numerical tables and time series.

    第一次接觸Pandas是由于Udacity上的一門數(shù)據(jù)分析課程“Introduction to Data Science” 的Project需要用Pandas庫,所以學習了一下Pandas。Pandas也是基于NumPy和Matplotlib開發(fā)的,主要用于數(shù)據(jù)分析和數(shù)據(jù)可視化,它的數(shù)據(jù)結(jié)構(gòu)DataFrame和R語言里的data.frame很像,特別是對于時間序列數(shù)據(jù)有自己的一套分析機制,非常不錯。這里推薦一本書《Python for Data Analysis》,作者是Pandas的主力開發(fā),依次介紹了iPython, NumPy, Pandas里的相關功能,數(shù)據(jù)可視化,數(shù)據(jù)清洗和加工,時間數(shù)據(jù)處理等,案例包括金融股票數(shù)據(jù)挖掘等,相當不錯。

    官方主頁:http://pandas.pydata.org/

    =====================================================================
    分割線,以上工具包基本上都是自己用過的,以下來源于其他同學的線索,特別是《Python機器學習庫》,《23個python的機器學習包》,做了一點增刪修改,歡迎大家補充
    =====================================================================

    3.?mlpy – Machine Learning Python

    mlpy is a Python module for Machine Learning built on top of NumPy/SciPy and the GNU Scientific Libraries.

    mlpy provides a wide range of state-of-the-art machine learning methods for supervised and unsupervised problems and it is aimed at finding a reasonable compromise among modularity, maintainability, reproducibility, usability and efficiency. mlpy is multiplatform, it works with Python 2 and 3 and it is Open Source, distributed under the GNU General Public License version 3.

    官方主頁:http://mlpy.sourceforge.net/

    4.?MDP:The Modular toolkit for Data Processing

    Modular toolkit for Data Processing (MDP) is a Python data processing framework.
    From the user’s perspective, MDP is a collection of supervised and unsupervised learning algorithms and other data processing units that can be combined into data processing sequences and more complex feed-forward network architectures.
    From the scientific developer’s perspective, MDP is a modular framework, which can easily be expanded. The implementation of new algorithms is easy and intuitive. The new implemented units are then automatically integrated with the rest of the library.
    The base of available algorithms is steadily increasing and includes signal processing methods (Principal Component Analysis, Independent Component Analysis, Slow Feature Analysis), manifold learning methods ([Hessian] Locally Linear Embedding), several classifiers, probabilistic methods (Factor Analysis, RBM), data pre-processing methods, and many others.

    “MDP用于數(shù)據(jù)處理的模塊化工具包,一個Python數(shù)據(jù)處理框架。 從用戶的觀點,MDP是能夠被整合到數(shù)據(jù)處理序列和更復雜的前饋網(wǎng)絡結(jié)構(gòu)的一批監(jiān)督學習和非監(jiān)督學習算法和其他數(shù)據(jù)處理單元。計算依照速度和內(nèi)存需求而高效的執(zhí)行。從科學開發(fā)者的觀點,MDP是一個模塊框架,它能夠被容易地擴展。新算法的實現(xiàn)是容易且直觀的。新實現(xiàn)的單元然后被自動地與程序庫的其余部件進行整合。MDP在神經(jīng)科學的理論研究背景下被編寫,但是它已經(jīng)被設計為在使用可訓練數(shù)據(jù)處理算法的任何情況中都是有用的。其站在用戶一邊的簡單性,各種不同的隨時可用的算法,及應用單元的可重用性,使得它也是一個有用的教學工具。”

    官方主頁:http://mdp-toolkit.sourceforge.net/

    5.?PyBrain

    PyBrain is a modular Machine Learning Library for Python. Its goal is to offer flexible, easy-to-use yet still powerful algorithms for Machine Learning Tasks and a variety of predefined environments to test and compare your algorithms.

    PyBrain is short for Python-Based Reinforcement Learning, Artificial Intelligence and Neural Network Library. In fact, we came up with the name first and later reverse-engineered this quite descriptive “Backronym”.

    “PyBrain(Python-Based Reinforcement Learning, Artificial Intelligence and Neural Network)是Python的一個機器學習模塊,它的目標是為機器學習任務提供靈活、易應、強大的機器學習算法。(這名字很霸氣)

    PyBrain正如其名,包括神經(jīng)網(wǎng)絡、強化學習(及二者結(jié)合)、無監(jiān)督學習、進化算法。因為目前的許多問題需要處理連續(xù)態(tài)和行為空間,必須使用函數(shù)逼近(如神經(jīng)網(wǎng)絡)以應對高維數(shù)據(jù)。PyBrain以神經(jīng)網(wǎng)絡為核心,所有的訓練方法都以神經(jīng)網(wǎng)絡為一個實例。”

    官方主頁:http://www.pybrain.org/

    6.?PyML?– machine learning in Python

    PyML is an interactive object oriented framework for machine learning written in Python. PyML focuses on SVMs and other kernel methods. It is supported on Linux and Mac OS X.

    “PyML是一個Python機器學習工具包,為各分類和回歸方法提供靈活的架構(gòu)。它主要提供特征選擇、模型選擇、組合分類器、分類評估等功能。”

    項目主頁:http://pyml.sourceforge.net/

    7.?Milk:Machine learning toolkit in Python.

    Its focus is on supervised classification with several classifiers available:
    SVMs (based on libsvm), k-NN, random forests, decision trees. It also performs
    feature selection. These classifiers can be combined in many ways to form
    different classification systems.

    “Milk是Python的一個機器學習工具箱,其重點是提供監(jiān)督分類法與幾種有效的分類分析:SVMs(基于libsvm),K-NN,隨機森林經(jīng)濟和決策樹。它還可以進行特征選擇。這些分類可以在許多方面相結(jié)合,形成不同的分類系統(tǒng)。對于無監(jiān)督學習,它提供K-means和affinity propagation聚類算法。”

    官方主頁:http://luispedro.org/software/milk

    http://luispedro.org/software/milk

    8.?PyMVPA: MultiVariate Pattern Analysis (MVPA) in Python

    PyMVPA is a Python package intended to ease statistical learning analyses of large datasets. It offers an extensible framework with a high-level interface to a broad range of algorithms for classification, regression, feature selection, data import and export. It is designed to integrate well with related software packages, such as scikit-learn, and MDP. While it is not limited to the neuroimaging domain, it is eminently suited for such datasets. PyMVPA is free software and requires nothing but free-software to run.

    “PyMVPA(Multivariate Pattern Analysis in Python)是為大數(shù)據(jù)集提供統(tǒng)計學習分析的Python工具包,它提供了一個靈活可擴展的框架。它提供的功能有分類、回歸、特征選擇、數(shù)據(jù)導入導出、可視化等”

    官方主頁:http://www.pymvpa.org/

    9.?Pyrallel?– Parallel Data Analytics in Python

    Experimental project to investigate distributed computation patterns for machine learning and other semi-interactive data analytics tasks.

    “Pyrallel(Parallel Data Analytics in Python)基于分布式計算模式的機器學習和半交互式的試驗項目,可在小型集群上運行”

    Github代碼頁:http://github.com/pydata/pyrallel

    10.?Monte?– gradient based learning in Python

    Monte (python) is a Python framework for building gradient based learning machines, like neural networks, conditional random fields, logistic regression, etc. Monte contains modules (that hold parameters, a cost-function and a gradient-function) and trainers (that can adapt a module’s parameters by minimizing its cost-function on training data).

    Modules are usually composed of other modules, which can in turn contain other modules, etc. Gradients of decomposable systems like these can be computed with back-propagation.

    “Monte (machine learning in pure Python)是一個純Python機器學習庫。它可以迅速構(gòu)建神經(jīng)網(wǎng)絡、條件隨機場、邏輯回歸等模型,使用inline-C優(yōu)化,極易使用和擴展。”

    官方主頁:http://montepython.sourceforge.net

    11.?Theano

    Theano is a Python library that allows you to define, optimize, and evaluate mathematical expressions involving multi-dimensional arrays efficiently. Theano features:
    1)tight integration with NumPy – Use numpy.ndarray in Theano-compiled functions.
    2)transparent use of a GPU – Perform data-intensive calculations up to 140x faster than with CPU.(float32 only)
    3)efficient symbolic differentiation – Theano does your derivatives for function with one or many inputs.
    4)speed and stability optimizations – Get the right answer for log(1+x) even when x is really tiny.
    5)dynamic C code generation – Evaluate expressions faster.
    6) extensive unit-testing and self-verification – Detect and diagnose many types of mistake.
    Theano has been powering large-scale computationally intensive scientific investigations since 2007. But it is also approachable enough to be used in the classroom (IFT6266 at the University of Montreal).

    “Theano 是一個 Python 庫,用來定義、優(yōu)化和模擬數(shù)學表達式計算,用于高效的解決多維數(shù)組的計算問題。Theano的特點:緊密集成Numpy;高效的數(shù)據(jù)密集型GPU計算;高效的符號微分運算;高速和穩(wěn)定的優(yōu)化;動態(tài)生成c代碼;廣泛的單元測試和自我驗證。自2007年以來,Theano已被廣泛應用于科學運算。theano使得構(gòu)建深度學習模型更加容易,可以快速實現(xiàn)多種模型。PS:Theano,一位希臘美女,Croton最有權勢的Milo的女兒,后來成為了畢達哥拉斯的老婆。”

    12.?Pylearn2

    Pylearn2 is a machine learning library. Most of its functionality is built on top of Theano. This means you can write Pylearn2 plugins (new models, algorithms, etc) using mathematical expressions, and theano will optimize and stabilize those expressions for you, and compile them to a backend of your choice (CPU or GPU).

    “Pylearn2建立在theano上,部分依賴scikit-learn上,目前Pylearn2正處于開發(fā)中,將可以處理向量、圖像、視頻等數(shù)據(jù),提供MLP、RBM、SDA等深度學習模型。”

    總結(jié)

    以上是生活随笔為你收集整理的爬虫教程( 6 ) --- 爬虫 进阶、扩展的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    激情综合网天天干 | 亚洲一区日韩精品 | 久草在线资源视频 | 四虎www. | 五月婷久 | 亚洲va欧洲va国产va不卡 | 福利一区在线视频 | 国产a精品 | 国产视频精品视频 | 日女人电影 | 美女av免费| 国产不卡高清 | 欧美日韩电影在线播放 | 久久久久久久久亚洲精品 | 免费在线一区二区 | 欧美在线1| 一区精品久久 | 日韩一区二区三区免费视频 | 欧美日韩精品综合 | 中文字幕一区二区三区在线播放 | 夜又临在线观看 | 九九九九色 | 欧美性生交大片免网 | 96亚洲精品久久久蜜桃 | 国产在线超碰 | 久久成人精品视频 | 国产成人精品999 | 国产成人久久精品亚洲 | 成年人在线看片 | 麻豆视频在线看 | 激情av资源网 | 又黄又爽又无遮挡免费的网站 | 国产黄色免费 | 国产无遮挡又黄又爽在线观看 | 三级av中文字幕 | 麻豆视频一区 | 国产精品久久久久久久久久久久午夜 | 精品视频9999| 在线观看91av | 亚洲3级 | 久久久久 免费视频 | 免费99视频| 亚洲三级毛片 | 久草网在线观看 | 看黄色.com | 欧美a√大片 | 四虎www. | 久久最新视频 | 亚洲视频免费在线观看 | 黄色a视频 | 中文字幕在线观看第二页 | 丰满少妇一级片 | 91大神精品视频在线观看 | 天天射天天射 | 亚洲欧美在线观看视频 | 欧美韩日精品 | 久久夜色精品国产欧美一区麻豆 | 人人操日日干 | 精品久久久久久久久久国产 | 精品人妖videos欧美人妖 | 国产精品久久久久国产a级 激情综合中文娱乐网 | 伊人网综合在线观看 | 在线观看国产一区二区 | 国产精品一区二区av麻豆 | 国产福利精品视频 | 天天干天天射天天爽 | 一区二区三区免费 | 最新av在线免费观看 | 欧美a级成人淫片免费看 | 亚洲精品视频免费观看 | 在线观看av免费观看 | 狠狠狠狠狠狠 | 欧美视频在线观看免费网址 | 欧美一级黄色片 | 丁香花在线观看免费完整版视频 | 91网在线观看 | 日韩欧美电影在线 | 中文字幕无吗 | 日本狠狠干 | 日韩av快播电影网 | 国产免费高清视频 | 久久久久区 | 亚洲理论在线观看 | 国产精品乱码一区二区视频 | 久久视频二区 | 午夜精品久久久久久久久久 | 96亚洲精品久久久蜜桃 | 国产精品99久久久精品免费观看 | 91在线观 | 久久精品99久久久久久 | 超碰国产在线观看 | 99视频在线精品国自产拍免费观看 | 国产日韩三级 | 国产区免费在线 | 最近av在线| 日韩久久久久久久久久久久 | 国外av在线 | 一级黄毛片 | 国产日韩视频在线播放 | 免费合欢视频成人app | 国产成人精品三级 | 国产精品中文字幕在线 | 日日天天干 | 毛片基地黄久久久久久天堂 | av中文资源在线 | 久久影院中文字幕 | 久久99精品久久久久婷婷 | 毛片精品免费在线观看 | 91桃色在线播放 | 97人人爽 | 亚洲精品在线免费 | 亚洲va在线va天堂va偷拍 | 国产精品综合久久久 | 亚洲专区在线播放 | 久久久久久久久久免费视频 | 欧美视频日韩视频 | www.激情五月.com | 国产日韩精品久久 | 人人爽人人爽人人爽 | 亚洲国产一区在线观看 | 日韩午夜三级 | 在线观看自拍 | 不卡日韩av| 国产91精品一区二区麻豆亚洲 | 精品视频资源站 | 成年人视频免费在线播放 | 97超碰人人| av线上看 | 俺要去色综合狠狠 | 粉嫩aⅴ一区二区三区 | 99色免费 | 日本久久中文字幕 | 久草视频2| 国产剧在线观看片 | 亚洲婷婷在线 | 日韩欧美精选 | 韩日电影在线观看 | 久久字幕网 | 欧美成人h版 | 色网址99| 丁香九月激情综合 | 在线国产能看的 | 91在线蜜桃臀 | 亚洲精品99 | 美女黄频在线观看 | 国产黄色特级片 | 亚洲精品国偷自产在线99热 | 久久中国精品 | 久久亚洲在线 | 欧美在线视频免费 | 亚洲国产午夜视频 | 亚洲一级性| 色五月色开心色婷婷色丁香 | 日韩免费av在线 | 久草资源在线观看 | 亚洲最新av在线 | 国产精品一区二 | 成人在线中文字幕 | 精品理论片 | 欧美精品一区二区免费 | 亚洲成av人片在线观看香蕉 | 日韩a级黄色 | 国产精品白丝jk白祙 | 成人毛片一区二区三区 | 99热超碰在线 | 国内小视频 | 久久99视频精品 | 日三级在线 | 亚洲在线视频免费 | 亚洲婷婷网 | 午夜av大片 | 激情久久久久久久久久久久久久久久 | 最新午夜| 黄色大片日本免费大片 | 免费人成网 | 久久精品视频在线播放 | 天天操狠狠干 | 97精品在线 | 国产精品福利午夜在线观看 | 青青草国产精品 | 午夜精品一区二区三区在线观看 | 亚州黄色一级 | 69亚洲乱 | 国产精品免费大片视频 | 黄色日批网站 | 久草视频免费在线观看 | 免费在线观看国产精品 | av电影在线不卡 | 国产黄色在线 | 日本丰满少妇免费一区 | 最近日本中文字幕a | 欧美视屏一区二区 | 午夜国产福利视频 | 91av在线播放 | 视频在线91| 日韩久久精品一区二区三区下载 | 91视视频在线直接观看在线看网页在线看 | 国产精品黄色影片导航在线观看 | 亚洲日本成人网 | 国偷自产视频一区二区久 | 永久免费的av电影 | 国产在线a视频 | 国产视频在线观看一区 | 777久久久 | 一区二区国产精品 | 亚洲黄色片在线 | 久久久精品欧美 | 国产精品电影在线 | 亚洲黄色在线播放 | 综合久久网站 | 性色在线视频 | 在线观看一区 | 综合色在线| 日本性生活免费看 | 婷婷亚洲五月 | 欧美国产在线看 | 最新国产中文字幕 | 久草免费在线观看视频 | 国产玖玖在线 | 中文一区在线 | 在线观看电影av | 欧美日本高清视频 | 久爱综合| 一区二区三区在线不卡 | 欧美黑人巨大xxxxx | 色射爱| 91福利免费 | 日韩啪啪小视频 | 国产在线观看二区 | 亚洲精品h | 美女网站视频久久 | 黄色视屏在线免费观看 | 亚洲国产欧美一区二区三区丁香婷 | 欧美精品少妇xxxxx喷水 | 夜夜躁日日躁狠狠久久88av | 狠狠狠狠狠狠 | 日韩肉感妇bbwbbwbbw | 亚洲国产av精品毛片鲁大师 | 特黄免费av | 在线观看av免费 | 黄色精品免费 | 精品国产亚洲日本 | 激情网婷婷 | 国产精品久久久久久久久久久久午夜 | 亚洲dvd | zzijzzij亚洲日本少妇熟睡 | 欧美精品一区二区在线观看 | 国产亚洲在线视频 | 亚洲一区二区三区91 | 日韩 精品 一区 国产 麻豆 | 国产日韩中文在线 | 日韩一区二区三区高清在线观看 | 在线激情小视频 | 久久久久欧美精品999 | 天天干夜夜夜操天 | 亚洲理论视频 | 五月天天色| 狠狠久久婷婷 | 国产一区高清在线 | 国产视频中文字幕在线观看 | 91视频3p| 亚洲综合日韩在线 | 日韩在线观看视频在线 | 91av精品 | 男女视频久久久 | 久久久免费看片 | 欧美国产日韩中文 | av在线色 | 国产免费a | 亚洲精品小区久久久久久 | 在线观看91久久久久久 | 欧美日韩免费一区二区 | 亚洲国产偷| 久久成年人视频 | 九九热免费视频在线观看 | 日韩美一区二区三区 | 久久夜色精品国产欧美乱 | 中文字幕丝袜美腿 | 日韩一级电影网站 | 五月婷婷综合在线视频 | 99久久久| japanesefreesexvideo高潮 | 天天曰天天曰 | 免费网址在线播放 | 亚洲午夜精品久久久久久久久久久久 | 最新av在线网站 | 午夜精品电影 | 又黄又爽的免费高潮视频 | 久久99国产精品自在自在app | 久久久久人人 | 日韩小视频 | 精品欧美在线视频 | 91福利影院在线观看 | avwww在线观看 | 亚洲伊人第一页 | 91麻豆精品国产自产在线游戏 | 91视频在线观看下载 | 狠狠五月天| 久久久影院一区二区三区 | 免费观看丰满少妇做爰 | 国产一区网址 | 九九热久久久 | 欧美一级特黄aaaaaa大片在线观看 | 国产在线播放不卡 | 色综合久久五月天 | 五月情婷婷 | 日本黄色黄网站 | 欧美精品资源 | 特黄特色特刺激视频免费播放 | 亚洲精品视频一二三 | 国产成人一区二区三区影院在线 | 一区二区三区在线免费观看视频 | 久久99国产综合精品 | 精品高清美女精品国产区 | av电影免费看| 久久久国产精品网站 | 毛片随便看| 男女啪啪视屏 | 日本久久精品 | 亚洲每日更新 | 欧美激情视频一区二区三区 | 2017狠狠干 | 最近更新好看的中文字幕 | 91香蕉视频好色先生 | 中文字幕在线色 | 国产精品久久久久久久久久直播 | 91正在播放 | 国产精品欧美在线 | 日韩av三区 | 丁香九月激情 | 狠狠狠狠狠狠操 | 精品亚洲一区二区 | 在线观看视频一区二区 | 婷婷av综合 | 99热999| 久久免费99精品久久久久久 | av大片免费 | 国产精品久久久久久久久久不蜜月 | 狠狠色丁香婷婷综合久小说久 | 国产精品一区二区免费看 | 亚洲更新最快 | 在线看国产视频 | 亚洲va综合va国产va中文 | 日韩av手机在线观看 | 二区中文字幕 | 97久久精品午夜一区二区 | 看毛片网站 | av看片网 | 国产99色 | 悠悠av资源片 | 九九亚洲精品 | 久久人人97超碰com | 69av视频在线 | 日韩 在线a | av东方在线 | 在线播放日韩av | 一二三久久久 | 激情综合网色播五月 | 国产精品久久99综合免费观看尤物 | 午夜123| 亚洲激情中文 | 日韩色一区二区三区 | 国产一级片直播 | 久久桃花网 | 这里只有精品视频在线 | 免费观看丰满少妇做爰 | 免费观看性生活大片3 | 久久永久免费 | 九九欧美| 九九综合在线 | 伊人五月综合 | 天天撸夜夜操 | 久久午夜精品 | 日本久久成人 | 九色91福利 | 国产精品久久久久一区二区三区 | 久久午夜剧场 | 九九热在线视频免费观看 | 在线观看www91 | 97av精品| 色香com. | 午夜久操 | 日本久久综合网 | 一区二区三区在线观看免费 | 成人av高清在线观看 | 久热电影| 成人在线观看免费 | 超碰成人免费电影 | 国产又黄又爽又猛视频日本 | 国产精品资源网 | 欧美一级高清片 | 国产亚洲精品久久久久久电影 | 国产69精品久久久久久久久久 | 在线免费观看黄色 | 久操视频在线免费看 | 亚洲天堂网在线观看视频 | 欧美与欧洲交xxxx免费观看 | 亚洲精品色婷婷 | 欧美日韩久久久 | 91人人爽人人爽人人精88v | 久久精品久久久久 | 在线高清av | 日韩在线观看免费 | 一级黄色片在线观看 | 高清不卡免费视频 | 麻豆国产露脸在线观看 | 成人av片免费观看app下载 | 久久歪歪| 国产成人中文字幕 | 99热在线看 | 精品视频成人 | 911国产 | 麻豆精品传媒视频 | 久久久久久久久久久免费 | 日韩在线视频在线观看 | 国产在线观看午夜 | 超碰免费97 | 五月婷在线 | 国产手机av在线 | 欧美日韩精品在线观看视频 | 91香蕉嫩草 | 亚洲高清视频在线观看 | av网站手机在线观看 | 久久99久久99免费视频 | 成人一区二区三区在线 | 伊人影院在线观看 | 国产精品一区二区白浆 | 国产午夜麻豆影院在线观看 | 久久久久中文 | 香蕉在线观看 | 在线观看你懂的网址 | 超碰97人 | 一级免费黄视频 | 久久情爱 | 国内精品国产三级国产aⅴ久 | 国产精品第10页 | 久久精品一区八戒影视 | 日韩一三区| 国产中文a | 欧美国产日韩激情 | 日韩av成人在线观看 | 看毛片网站 | 又黄又爽的视频在线观看网站 | 久久专区 | 久久96国产精品久久99漫画 | 成人毛片网| 日日草天天干 | 一本一本久久aa综合精品 | 亚洲精品乱码久久久久久蜜桃欧美 | 97视频免费 | 国产视频精选 | 在线视频免费观看 | 色欧美综合| 亚洲国内精品视频 | 午夜三级影院 | 精品免费国产一区二区三区四区 | 天天爽综合网 | www.久艹| 天天色综合天天 | 在线成人免费av | 久久精品成人热国产成 | 国产精品自产拍在线观看 | 欧美日韩亚洲一 | 国产91学生粉嫩喷水 | 国产亚洲精品久久久久5区 成人h电影在线观看 | 欧美日韩中文国产一区发布 | 91精品老司机久久一区啪 | 久久人人爽人人人人片 | 三级av网 | 99视频在线观看一区三区 | 天天干天天操天天 | 国产91精品一区二区 | 色狠狠婷婷 | 99视频在线精品国自产拍免费观看 | 国产手机av在线 | 国产系列 在线观看 | 亚洲精品www久久久 www国产精品com | 激情深爱五月 | 国产精品第二页 | 亚洲涩涩网站 | 五月天欧美精品 | 蜜桃视频色 | 欧美色综合天天久久综合精品 | 久久视频这里只有精品 | 在线观看av片 | 一本一本久久a久久 | 精品一区在线 | av888.com| 最近日本韩国中文字幕 | 国产中文字幕三区 | 99久久精品免费看国产免费软件 | 一本大道久久精品懂色aⅴ 五月婷社区 | 综合色综合 | 免费国产一区二区 | 精品国产免费观看 | 中文字幕av免费 | 国产一区国产精品 | 在线观看中文字幕 | 六月色婷| avav99| 在线观看黄色免费视频 | 欧美亚洲免费在线一区 | 超碰97久久 | 国产亚洲精品久久久久久电影 | 国产很黄很色的视频 | 欧美激情精品久久久久久免费印度 | 五月婷婷视频在线 | 国产成a人亚洲精v品在线观看 | 国产精品美女久久久久久久 | 91av网址 | 91香蕉视频污在线 | 国内精品久久久久久中文字幕 | 国产四虎影院 | 色av资源网| 黄色片视频在线观看 | 碰碰影院 | 日韩专区视频 | 国产美腿白丝袜足在线av | 六月婷色 | 色婷婷av在线 | 天天操夜夜干 | 网站你懂的 | 色吊丝在线永久观看最新版本 | 久久久久久毛片 | 97在线免费观看 | 97人人网| 日韩一级电影在线 | 亚洲,国产成人av | 超碰97人人爱 | 精品国产自在精品国产精野外直播 | 精品国产精品久久 | 激情自拍av | 日日夜夜爱| 成人免费看电影 | 日韩视频欧美视频 | 日韩欧美国产视频 | 在线观看深夜福利 | 国产成人精品国内自产拍免费看 | 国产精品一区二区三区99 | 四虎8848免费高清在线观看 | 免费观看www小视频的软件 | 一区二区 久久 | 成人av av在线 | 国产视频每日更新 | 婷婷社区五月天 | 久久久久亚洲精品成人网小说 | 99久久夜色精品国产亚洲 | 国产91精品久久久久久 | 免费观看高清 | 国产精品一区二区免费看 | 91午夜精品 | 99久久www | 亚洲 在线| 免费黄a大片 | 亚洲黄色av | 亚洲精品中文字幕在线观看 | 亚洲国产成人久久综合 | 中文在线中文资源 | 天天草夜夜 | 亚洲 中文 欧美 日韩vr 在线 | 中文字幕在线视频国产 | 最新国产精品亚洲 | 六月丁香综合 | 久久精品国产亚洲a | 亚洲一级国产 | 亚洲欧洲中文日韩久久av乱码 | 99色在线播放 | 91av网站在线观看 | www.色的| 永久av免费在线观看 | 亚洲精品中文字幕在线观看 | 日韩中文字幕国产精品 | 久久久久影视 | 久久99久久99精品中文字幕 | 激情av在线资源 | 天天天干夜夜夜操 | 99热精品久久| 9热精品 | 国产福利不卡视频 | 天天激情天天干 | 亚洲精品国 | 国产黄在线观看 | 激情综合亚洲 | 在线观看日本高清mv视频 | 欧美性生活久久 | 日韩精品免费在线 | 91精彩在线视频 | 久久午夜剧场 | 2024国产精品视频 | 亚洲理论在线观看电影 | 亚洲一区二区视频 | 国产一级电影免费观看 | 免费国产ww| 男女视频91 | 日韩mv欧美mv国产精品 | 色婷婷天天干 | 不卡的av中文字幕 | 亚洲午夜久久久久久久久电影网 | 日韩亚洲国产中文字幕 | 国产精品精品国产 | 国产黄色一级片在线 | 久久久精品小视频 | 精品国产理论 | 国产在线精品一区二区 | 久久久久久亚洲精品 | av资源免费在线观看 | 高潮久久久久久 | 91中文字幕一区 | 国产精品视频你懂的 | 在线a人v观看视频 | 最近中文字幕免费视频 | 人人澡人 | 手机av片 | 国产精品成人自产拍在线观看 | 在线色吧| 一级全黄毛片 | 国产精品久久久久久久久岛 | 91禁在线看 | 啪啪小视频网站 | 国产精品久久久久久久久久直播 | 999久久久免费精品国产 | 久久精品久久国产 | 国产亚洲观看 | 18岁免费看片 | 亚洲专区路线二 | 国产999视频在线观看 | a久久久久| 国内视频在线观看 | 国产视频一区二区在线 | 久久黄色免费视频 | 麻豆一区二区 | 国产在线黄 | 精品久久久国产 | 97精品国产一二三产区 | 91麻豆精品91久久久久同性 | 欧美在线视频a | 在线观看你懂的网站 | 韩日av在线 | 最新婷婷色| 成人网中文字幕 | 国产美女视频免费 | 久久亚洲国产精品 | 中文字幕在线免费观看视频 | 久草网在线观看 | 成片免费 | 天天插天天射 | www.夜色.com| 91秒拍国产福利一区 | 日本一区二区三区免费看 | 国产a视频免费观看 | 精品99999 | 国产一级淫片免费看 | 黄色片亚洲 | 日韩免费不卡视频 | 精品一区免费 | av大片网址 | 亚洲精品99久久久久久 | 黄色免费网站 | 777视频在线观看 | 九九九在线观看 | av在线电影播放 | 成人a视频片观看免费 | 天天久久综合 | 91成人黄色| 天天干夜夜爽 | 操操碰 | 久久久久亚洲国产精品 | 一区二区三区高清在线 | 亚洲国产中文字幕在线观看 | 永久av免费在线观看 | 91在线91拍拍在线91 | 综合伊人av | 国产精品成人自产拍在线观看 | 久久久久亚洲精品成人网小说 | 色婷婷在线播放 | 超碰人人舔 | 国产黄色片一级三级 | 又黄又爽又刺激的视频 | 一级做a爱片性色毛片www | av免费观看网站 | 久久久久久久久久久国产精品 | 成人一区电影 | 国产高清日韩欧美 | 久久久久亚洲天堂 | 99在线观看视频 | 久久精品网站视频 | 欧美黄污视频 | 亚洲视频999 | 韩日三级av | 亚洲成人黄色网址 | 亚洲欧美综合精品久久成人 | 在线黄色国产电影 | 丁香婷婷综合网 | 免费a视频| 99精品小视频| 在线黄频| 久久夜色精品国产欧美乱极品 | 成人在线观看免费 | 久久国产精品视频 | 国产精品久久久一区二区三区网站 | 成人精品国产免费网站 | 看污网站 | 99久久综合国产精品二区 | 69av在线播放 | 国产一区二区手机在线观看 | 99久高清在线观看视频99精品热在线观看视频 | 免费色视频| 九九免费在线观看 | 操操综合 | 国产视频在线播放 | 午夜av不卡 | 婷婷色在线资源 | a级国产乱理论片在线观看 特级毛片在线观看 | 成人久久18免费 | 中文字幕av免费在线观看 | 高清av中文在线字幕观看1 | av免费网站 | 一区二区影院 | 久久免费国产精品1 | 色94色欧美 | 亚洲国产日韩在线 | 黄污视频网站大全 | 可以免费观看的av片 | 91一区二区三区久久久久国产乱 | 欧美激情视频免费看 | 亚洲五月激情 | 国产999久久久 | 国产91成人在在线播放 | 日韩av不卡播放 | 深夜免费福利视频 | 午夜精品av在线 | 一区二区三区在线观看 | 久久久久久高潮国产精品视 | 日韩性xxxx| 久久试看 | 成人a在线 | 中文字幕91视频 | 欧美另类交在线观看 | 91色吧 | 成人sm另类专区 | 国产日产在线观看 | 国产成人av网址 | 欧美小视频在线观看 | 天天操天天草 | 午夜精品福利一区二区 | 国产精品久久免费看 | v片在线看 | 国产精品com | 久久免费视频一区 | 白丝av免费观看 | 久久久精品久久日韩一区综合 | 丁香婷婷深情五月亚洲 | 亚洲精品在线视频 | 国产精品视频99 | 天天操狠狠操夜夜操 | 欧美日韩中文字幕在线视频 | 六月色婷婷 | 久久这里 | 欧美一区二区三区在线播放 | 97日日碰人人模人人澡分享吧 | 欧美 日韩 视频 | 四虎精品成人免费网站 | 久久精品久久综合 | 欧美一级裸体视频 | 99免在线观看免费视频高清 | 国产美腿白丝袜足在线av | 精品久久精品 | 天天操夜夜看 | 四虎在线视频 | 色播激情五月 | 日韩视频免费播放 | 亚洲特级毛片 | 久久试看| 国产精品18久久久久白浆 | 国产色影院| 韩国av免费 | 伊人狠狠色丁香婷婷综合 | 久久99久久99精品免观看软件 | 国产精品白丝av | 97超碰色偷偷 | 日韩久久精品一区 | 亚洲精品免费在线观看 | 精品国产成人av | 欧美色久 | 成人h视频在线播放 | 亚洲开心色| 91精品免费 | 丁香五月网久久综合 | 亚洲国产三级在线观看 | 在线免费观看av网站 | 国产一级不卡毛片 | 水蜜桃亚洲一二三四在线 | 国产一区 在线播放 | 日韩高清网站 | 干干干操操操 | 91在线视频一区 | 中文字幕日韩有码 | 国产直播av | sesese图片 | 日韩三级视频在线看 | 欧美一级在线 | 国产小视频在线观看免费 | 亚洲人成人天堂h久久 | 免费男女羞羞的视频网站中文字幕 | 中文字幕在线观看免费 | 日韩高清三区 | 国内免费久久久久久久久久久 | 国产在线播放一区二区三区 | 色姑娘综合天天 | 欧美日韩国产色综合一二三四 | 成人小视频免费在线观看 | 国产一区精品在线观看 | 一区二区三区视频 | 91在线看视频免费 | 欧美亚洲精品在线观看 | 欧美性色网站 | 精品国产成人 | 国产日韩精品在线 | 日韩黄色中文字幕 | 久久免费视频99 | 日韩av免费一区 | 欧美日韩一区二区视频在线观看 | 欧美亚洲精品一区 | 日韩女同一区二区三区在线观看 | 国产成人久久精品一区二区三区 | 日韩精品一区二区三区在线视频 | 国产精品久久99综合免费观看尤物 | 亚洲精品成人在线 | 亚洲第一区精品 | 国产综合福利在线 | 日韩高清不卡一区二区三区 | 国产精品9区 | 91精品国产综合久久福利 | 精品国产乱码久久 | 国产一区二区久久精品 | 国产精品美女久久久 | 亚洲成年人免费网站 | 激情视频免费观看 | 91视频在线网址 | 99免费看片 | 国产日韩在线看 | 国产免费久久久久 | 免费在线看v | 国产在线观看h | 日本护士三级少妇三级999 | 国产91精品看黄网站在线观看动漫 | 天天碰天天操 | www色网站| 国产精品福利一区 | 97伊人网| 婷婷网在线| 一区在线播放 | 中文字幕一区二区三区精华液 | 久久伊人精品一区二区三区 | 国产成人亚洲在线观看 | 国产99久久久精品 | 亚洲美女免费精品视频在线观看 | 综合久久婷婷 | 一区二区激情视频 | 日韩精品亚洲专区在线观看 | 欧美在线视频第一页 | 人人爽网站 | 天天搞天天干天天色 | 久草在线看片 | 日韩精选在线 | 欧美日韩国产精品久久 | 国产色婷婷在线 | 午夜10000| 欧美色图亚洲图片 | 久草视频视频在线播放 | 免费www视频 | 免费热情视频 | www色婷婷com| 婷婷新五月 | 探花视频网站 | 最近中文字幕大全 | 日本中文字幕系列 | 欧美一级日韩免费不卡 | 91视频免费网址 | 亚洲第一av在线 | 一区二区三区免费在线 | 久久国产精品视频 | 亚洲午夜久久久综合37日本 | 96超碰在线 | 91精品国产自产老师啪 | 日韩二区在线播放 | 日韩在线视 | 成人午夜精品久久久久久久3d | 在线观看的黄色 | 亚洲影音先锋 | 中文字幕日本特黄aa毛片 | 伊人狠狠操 | 水蜜桃亚洲一二三四在线 | 日韩在线视频一区 | 国产福利精品视频 | 五月婷婷av在线 | 国产综合精品一区二区三区 | 天天做日日爱夜夜爽 | 国产精品免费看久久久8精臀av | 91视频91自拍 | 91精品入口 | 天天操天天射天天爱 | av成人黄色| 欧美另类z0zx | 久久视频这里有久久精品视频11 | 精品毛片一区二区免费看 | 天天干,夜夜操 | 国产亚洲精品久久久网站好莱 | 亚洲爽爽网 | 国产视频1区2区3区 久久夜视频 | 亚洲天堂视频在线 | 高潮久久久久久久久 | www.看片网站 | 国产一级精品绿帽视频 | 91最新中文字幕 | 国产黄色观看 | 久久99亚洲网美利坚合众国 | 久久欧美在线电影 | 91在线91| 狠狠色噜噜狠狠 | 99久久久久久久久 | 欧美 日韩 成人 | 久久久久久97三级 | 婷婷九九 | 久久五月精品 | 国产96在线视频 | 亚洲第五色综合网 | 亚洲va欧美 | 日韩免费高清在线观看 | 黄色亚洲 | 国产专区视频在线观看 | 日韩高清在线一区二区 | 中文日韩在线视频 | 夜夜爽88888免费视频4848 | 国产精品99久久久精品 | 91精品一区在线观看 | 中文在线字幕免费观 | 免费99视频 | 最新国产福利 | 五月天视频网站 | 色婷婷视频在线观看 | 日韩高清不卡在线 | 欧美韩国日本在线观看 | 国产精品久久久久一区二区国产 | 伊人网综合在线观看 | 久久综合九九 | 欧美日韩一区二区三区视频 | 高清不卡一区二区三区 | 人成免费网站 | 国产成人精品免高潮在线观看 | 日韩一级电影网站 | 国产免费叼嘿网站免费 | 色偷偷88888欧美精品久久 | 国产精品久久久久久久久搜平片 | 日韩有码第一页 | 中文字幕一区二区在线播放 | 欧美精品中文 | 中文字幕之中文字幕 | www国产在线 | 国产精品一区二区麻豆 | 日韩视频精品在线 | 中文字幕电影网 | 免费观看一级成人毛片 | 91资源在线视频 | 尤物一区二区三区 | 深夜视频久久 | 色吊丝av中文字幕 | 日本视频网 | 亚洲精欧美一区二区精品 | 免费在线观看一区 | 91视频传媒 | 久草在线观看 | 成全免费观看视频 | 在线v片免费观看视频 | 久久精品99国产精品 | 欧美一级激情 | 日韩在线中文字幕视频 | www.香蕉| 午夜国产在线 | 伊人狠狠干| 男女激情麻豆 | 999久久久欧美日韩黑人 | 精品福利网| 日韩在线观看电影 | 国产女人40精品一区毛片视频 | 国产高清 不卡 | 国产美女在线观看 | 日韩欧美在线播放 | 国产第页 | 黄色视屏免费在线观看 | 久久精品综合视频 | 91av在线看| 99久久99热这里只有精品 | 麻豆传媒视频在线免费观看 | 国产精久久久久久久 | 丁香在线观看完整电影视频 | 亚洲自拍自偷 | 国产97免费 |