爬蟲入門

爬蟲

簡單的說網絡爬蟲(Web crawler)也叫作網絡鏟(Web scraper)、網絡蜘蛛(Web spider),其行爲通常是先「爬」到對應的網頁上,再把須要的信息「鏟」下來。

分類

網絡爬蟲按照系統結構和實現技術,大體能夠分爲如下幾種類型:通用網絡爬蟲(General Purpose Web Crawler)、聚焦網絡爬蟲(Focused Web Crawler)、增量式網絡爬蟲(Incremental Web Crawler)、深層網絡爬蟲(Deep Web Crawler)。實際的網絡爬蟲系統一般是幾種爬蟲技術相結合實現的。css

通用網絡爬蟲

通用網絡爬蟲又稱全網爬蟲(Scalable Web Crawler),爬取對象從一些種子 URL 擴充到整個 Web。主要爲門戶站點搜索引擎和大型 Web 服務提供商採集數據。html

通用網絡爬蟲的結構大體能夠分爲頁面爬取模塊 、頁面分析模塊、連接過濾模塊、頁面存儲模塊、URL 隊列、初始 URL 集合幾個部分。爲提升工做效率,通用網絡爬蟲會採起必定的爬取策略。 經常使用的爬取策略有:深度優先策略、廣度優先策略。python

1) 深度優先策略(DFS):其基本方法是按照深度由低到高的順序,依次訪問下一級網頁連接,直到不能再深刻爲止。git

2) 廣度優先策略(BFS):此策略按照網頁內容目錄層次深淺來爬取頁面,處於較淺目錄層次的頁面首先被爬取。 當同一層次中的頁面爬取完畢後,爬蟲再深刻下一層繼續爬取。github

聚焦網絡爬蟲

聚焦網絡爬蟲(Focused Crawler),又稱主題網絡爬蟲(Topical Crawler),是指選擇性地爬取那些與預先定義好的主題相關頁面的網絡爬蟲。 和通用網絡爬蟲相比,聚焦爬蟲只須要爬取與主題相關的頁面,極大地節省了硬件和網絡資源,保存的頁面也因爲數量少而更新快,還能夠很好地知足一些特定人羣對特定領域信息的需求。咱們以前爬的歌單就屬於這一種。web

增量式網絡爬蟲

增量式網絡爬蟲(Incremental Web Crawler)是指對已下載網頁採起增 量式更新和只爬取新產生的或者已經發生變化網頁的爬蟲,它可以在必定程度上保證所爬取的頁面是儘量新的頁面。 和週期性爬取和刷新頁面的網絡爬蟲相比,增量式爬蟲只會在須要的時候爬取新產生或發生更新的頁面 ,並不從新下載沒有發生變化的頁面,可有效減小數據下載量,及時更新已爬取的網頁,減少時間和空間上的耗費,可是增長了爬取算法的複雜度和實現難度。如今比較火的輿情爬蟲通常都是增量式網絡爬蟲。算法

深網爬蟲

Web 頁面按存在方式能夠分爲表層網頁(Surface Web)和深層網頁(Deep Web,也稱 Invisible Web Pages 或 Hidden Web)。 表層網頁是指傳統搜索引擎能夠索引的頁面,以超連接能夠到達的靜態網頁爲主構成的 Web 頁面。Deep Web 是那些大部份內容不能經過靜態連接獲取的、隱藏在搜索表單後的,只有用戶提交一些關鍵詞才能得到的 Web 頁面。例如那些用戶註冊後內容纔可見的網頁就屬於 Deep Web。數據庫

開源框架

Project Language Star Watch Fork
Nutch Java 1111 195 808
webmagic Java 4216 618 2306
WebCollector Java 1222 255 958
heritrix3 Java 773 141 428
crawler4j Java 1831 242 1136
Pyspider Python 8581 687 2273
Scrapy Python 19642 1405 5261

Nutch

介紹:json

Nutch是一個開源的Java實現的搜索引擎。它提供了咱們運行本身的搜索引擎所需的所有工具,包括全文搜索和網絡爬蟲。後端

儘管搜索是上網的基本要求,可是現有的搜索引擎的數目卻在降低。而且這頗有可能進一步演變成爲一個公司壟斷了幾乎全部的網頁搜索爲其謀取商業利益。這顯然不利於廣大Internet用戶。

Nutch爲咱們提供了這樣一個不一樣的選擇。相對於那些商用的搜索引擎,Nutch做爲開放源代碼的搜索引擎將會更加透明,從而更值得你們信賴。如今全部主要的搜索引擎都採用私有的排序算法, 而不會解釋爲何一個網頁會排在一個特定的位置。除此以外, 有的搜索引擎依照網站所付的費用, 而不是根據它們自己的價值進行排序。與它們不一樣,Nutch沒有什麼須要隱瞞,也沒有動機去扭曲搜索的結果。Nutch將盡本身最大的努力爲用戶提供最好的搜索結果。

優勢:

Nutch支持分佈式抓取,並有Hadoop支持,能夠進行多機分佈抓取,存儲和索引。另外很吸引人的一點在於,它提供了一種插件框架,使得其對各類網頁內容的解析、各類數據的採集、查詢、集羣、過濾等功能可以方便的進行擴展。正是因爲有此框架,使得 Nutch 的插件開發很是容易,第三方的插件也層出不窮,極大的加強了 Nutch 的功能和聲譽。

缺點:

對於大多數用戶來講,通常是想作一個精確數據爬取的爬蟲,就像第一篇裏爬歌單那個「主題爬蟲」。而第二篇介紹的「通用爬蟲」適合作搜索引擎,這種需求就比較少。若是以此爲標準,那麼爲搜索引擎而生的Nutch就有着天生缺點。Nutch的架構裏大部分爲搜索引擎而設計的,對精確爬取沒有特別的考慮。也就是說,用Nutch作主題爬蟲,會浪費不少的時間在沒必要要的計算上。並且若是你試圖經過對Nutch進行二次開發來擴展其定製能力,基本上就要破壞Nutch的框架,反而不如本身寫了。

Pyspider

介紹:

Pyspider是一個國人編寫的強大的網絡爬蟲系統並帶有強大的WebUI。採用Python語言編寫,分佈式架構,支持多種數據庫後端,強大的WebUI支持腳本編輯器,任務監視器,項目管理器以及結果查看器。

Pyspider的主要功能包括,抓取、更新調度多站點的特定的頁面;須要對頁面進行結構化信息提取;靈活可擴展,穩定可監控。知足了絕大多數Python爬蟲的需求 —— 定向抓取,結構化化解析。可是面對結構迥異的各類網站,單一的抓取模式並不必定能知足,靈活的抓取控制是必須的。爲了達到這個目的,單純的配置文件每每不夠靈活,因而,經過腳本去控制抓取成爲了最後的選擇。而去重調度,隊列,抓取,異常處理,監控等功能做爲框架,提供給抓取腳本,並保證靈活性。最後加上web的編輯調試環境,以及web任務監控,即成爲了最終的框架。

優勢:

  1. 支持分佈式部署。
  2. 徹底可視化,對用戶很是友好:WEB 界面編寫調試腳本,起停腳本,監控執行狀態,查看活動歷史,獲取結果產出。
  3. 簡單,五分鐘就能上手。腳本規則簡單,開發效率高。
  4. 支持抓取JavaScript的頁面。

總之,Pyspider很是強大,強大到更像一個產品而不是一個框架。

缺點:

  1. URL去重使用數據庫而不是布隆過濾器,億級存儲的db io將致使效率急劇下降。
  2. 使用上的人性化犧牲了靈活度,定製化能力下降。

Scrapy

介紹:

Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。 能夠應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。Scrapy 使用 Twisted這個異步網絡庫來處理網絡通信,架構清晰,而且包含了各類中間件接口,能夠靈活的完成各類需求。Scratch,是抓取的意思,這個Python的爬蟲框架叫Scrapy,大概也是這個意思吧。

優勢:

  1. 極其靈活的定製化爬取。
  2. 社區人數多、文檔完善。
  3. URL去重採用布隆過濾器方案。
  4. 能夠處理不完整的HTML,Scrapy已經提供了selectors(一個在lxml的基礎上提供了更高級的接口),能夠高效地處理不完整的HTML代碼。

缺點:

  1. 不支持分佈式部署。
  2. 原生不支持抓取JavaScript的頁面。
  3. 全命令行操做,對用戶不友好,須要必定學習週期。

Scrapy入門

架構簡介

組件

Engine: 引擎負責控制數據流在系統中全部組件中流動,並在相應動做發生時觸發事件。

Scheduler: 調度器從引擎接受Request並將他們入隊,以便以後引擎請求他們時提供給引擎。

Downloader: 下載器負責獲取頁面數據並提供給引擎,然後提供給Spider。

Spiders: Spider是Scrapy用戶編寫的用於分析Response並提取Item或提取更多須要下載的URL的類。 每一個Spider負責處理特定網站。

Item Pipeline: 負責處理被Spider提取出來的Item。典型的功能有清洗、 驗證及持久化操做。

Downloader middlewares: 下載器中間件是在Engine及Downloader之間的特定鉤子(specific hooks),處理Downloader傳遞給Engine的Response。 其提供了一個簡便的機制,經過插入自定義代碼來擴展Scrapy功能。

Spider middlewares: 是在Engine及Spider之間的特定鉤子(specific hook),處理Spider的輸入(Response)和輸出(Items及Requests)。 其提供了一個簡便的機制,經過插入自定義代碼來擴展Scrapy功能。

數據流

Scrapy中的數據流由執行引擎控制,其過程以下:

  1. Engine從Spider獲取第一個須要爬取URL(s)。
  2. Engine用Scheduler調度Requests,並向Scheduler請求下一個要爬取的URL。
  3. Scheduler返回下一個要爬取的URL給Engine。
  4. Engine將URL經過Downloader middlewares轉發給Downloader。
  5. 一旦頁面下載完畢,下載器生成一個該頁面的Response,並將其經過Downloader middlewares發送給Engine。
  6. 引擎從Downloader中接收到Response並經過Spider middlewares發送給Spider處理。
  7. Spider處理Response並返回爬取到的Item及新的Request給Engine。
  8. Engine將爬取到的Item給Item Pipeline,而後將Request給Scheduler。
  9. 從第一步開始重複這個流程,直到Scheduler中沒有更多的URLs。

架構就是這樣,流程和我第二篇裏介紹的迷你架構差很少,但擴展性很是強大。

入門

安裝scrapy

pip install scrapy

建立項目

在開始爬取以前,您必須建立一個新的Scrapy項目。 進入您打算存儲代碼的目錄中,運行下列命令:

scrapy startproject tutorial

該命令將會建立包含下列內容的 tutorial 目錄:

tutorial/
    scrapy.cfg            # 項目的配置文件
    tutorial/             # 該項目的python模塊。以後您將在此加入代碼
        __init__.py
        items.py          # 項目中的item文件
        pipelines.py      # 項目中的pipelines文件
        settings.py       # 項目的設置文件
        spiders/          # 放置spider代碼的目錄
            __init__.py

編寫第一個爬蟲

Spider是用戶編寫用於從單個網站(或者一些網站)爬取數據的類。其包含了一個用於下載的初始URL,以及如何跟進網頁中的連接以及如何分析頁面中的內容的方法。

如下爲咱們的第一個Spider代碼,保存在 tutorial/spiders 目錄下的 quotes_spider.py文件中:

import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes"

    def start_requests(self):
        urls = [
            'http://quotes.toscrape.com/page/1/',
            'http://quotes.toscrape.com/page/2/',
        ]
        for url in urls:
            yield scrapy.Request(url=url, callback=self.parse)

    def parse(self, response):
        page = response.url.split("/")[-2]
        filename = 'quotes-%s.html' % page
        with open(filename, 'wb') as f:
            f.write(response.body)
        self.log('Saved file %s' % filename)

爲了建立一個Spider,你必須繼承 scrapy.Spider 類, 且定義如下三個屬性:

  • name: 用於區別Spider。 該名字必須是惟一的,您不能夠爲不一樣的Spider設定相同的名字。
  • start_urls: 包含了Spider在啓動時進行爬取的url列表。 所以,第一個被獲取到的頁面將是其中之一。 後續的URL則從初始的URL獲取到的數據中提取。
  • parse() 是spider的一個方法。 被調用時,每一個初始URL完成下載後生成的Response 對象將會做爲惟一的參數傳遞給該函數。 該方法負責解析返回的數據(response data),提取數據以及生成須要進一步處理的URL的 Request 對象。

進入項目的根目錄,執行下列命令啓動spider:

scrapy s crawl quotes

這個命令啓動用於爬取 quotes.toscrape.com 的spider,你將獲得相似的輸出:

2017-05-10 20:36:17 [scrapy.core.engine] INFO: Spider opened
2017-05-10 20:36:17 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2017-05-10 20:36:17 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
2017-05-10 20:36:17 [scrapy.core.engine] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None)
2017-05-10 20:36:17 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/1/> (referer: None)
2017-05-10 20:36:17 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/page/2/> (referer: None)
2017-05-10 20:36:17 [quotes] DEBUG: Saved file quotes-1.html
2017-05-10 20:36:17 [quotes] DEBUG: Saved file quotes-2.html
2017-05-10 20:36:17 [scrapy.core.engine] INFO: Closing spider (finished)

提取數據

咱們以前只是保存了HTML頁面,並無提取數據。如今升級一下代碼,把提取功能加進去。至於如何使用瀏覽器的開發者模式分析網頁,以前已經介紹過了。

import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = [
        'http://quotes.toscrape.com/page/1/',
        'http://quotes.toscrape.com/page/2/',
    ]

    def parse(self, response):
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.css('small.author::text').extract_first(),
                'tags': quote.css('div.tags a.tag::text').extract(),
            }

再次運行這個爬蟲,你將在日誌裏看到被提取出的數據:

2017-05-10 20:38:33 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['life', 'love'], 'author': 'André Gide', 'text': '「It is better to be hated for what you are than to be loved for what you are not.」'}
2017-05-10 20:38:33 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/page/1/>
{'tags': ['edison', 'failure', 'inspirational', 'paraphrased'], 'author': 'Thomas A. Edison', 'text': "「I have not failed. I've just found 10,000 ways that won't work.」"}

保存爬取的數據

最簡單存儲爬取的數據的方式是使用 Feed exports:

scrapy crawl quotes -o quotes.json

該命令將採用 JSON 格式對爬取的數據進行序列化,生成quotes.json文件。

Pyspider

架構簡介

pyspider的架構主要分爲 scheduler(調度器), fetcher(抓取器), processor(腳本執行):

  • 各個組件間使用消息隊列鏈接,除了scheduler是單點的,fetcher 和 processor 都是能夠多實例分佈式部署的。 scheduler 負責總體的調度控制
  • 任務由 scheduler 發起調度,fetcher 抓取網頁內容, processor 執行預先編寫的python腳本,輸出結果或產生新的提鏈任務(發往 scheduler),造成閉環。
  • 每一個腳本能夠靈活使用各類python庫對頁面進行解析,使用框架API控制下一步抓取動做,經過設置回調控制解析動做。

入門

抓取電影的相關信息

安裝

pip install pyspider

啓動

pyspider

運行成功後用瀏覽器打開http://localhost:5000/訪問控制檯

選取url

既然咱們要爬全部的電影,首先咱們須要抓一個電影列表,一個好的列表應該:

  • 包含足夠多的電影的 URL
  • 經過翻頁,能夠遍歷到全部的電影
  • 一個按照更新時間排序的列表,能夠更快抓到最新更新的電影

咱們在 http://movie.douban.com/ 掃了一遍,發現並無一個列表能包含全部電影,只能退而求其次,經過抓取分類下的全部的標籤列表頁,來遍歷全部的電影: http://movie.douban.com/tag/

建立項目

在 pyspider 的 dashboard 的右下角,點擊 「Create」 按鈕

替換 on_start 函數的 self.crawl 的 URL:

@every(minutes=24 * 60)
def on_start(self):
    self.crawl('http://movie.douban.com/tag/', callback=self.index_page)
  • self.crawl 告訴 pyspider 抓取指定頁面,而後使用 callback 函數對結果進行解析。
  • @every 修飾器,表示 on_start 天天會執行一次,這樣就能抓到最新的電影了。

點擊綠色的 run 執行,你會看到 follows 上面有一個紅色的 1,切換到 follows 面板,點擊綠色的播放按鈕:

Tag 列表頁

在 tag 列表頁 中,咱們須要提取出全部的 電影列表頁 的 URL。你可能已經發現了,sample handler 已經提取了很是多大的 URL,全部,一種可行的提取列表頁 URL 的方法就是用正則從中過濾出來:

import re
...

    @config(age=10 * 24 * 60 * 60)
    def index_page(self, response):
        for each in response.doc('a[href^="http"]').items():
            if re.match("http://movie.douban.com/tag/\w+", each.attr.href, re.U):
                self.crawl(each.attr.href, callback=self.list_page)
  • 因爲 電影列表頁和 tag列表頁長的並不同,在這裏新建了一個 callback 爲 self.list_page
  • @config(age=10 24 60 * 60) 在這表示咱們認爲 10 天內頁面有效,不會再次進行更新抓取

因爲 pyspider 是純 Python 環境,你可使用 Python 強大的內置庫,或者你熟悉的第三方庫對頁面進行解析。不過更推薦使用 CSS選擇器

電影列表頁

再次點擊 run 讓咱們進入一個電影列表頁(list_page)。在這個頁面中咱們須要提取:

CSS Selector Helper

在 pyspider 中,還內置了一個 CSS Selector Helper,當你點擊頁面上的元素的時候,能夠幫你生成它的 CSS選擇器 表達式。你能夠點擊 Enable CSS selector helper 按鈕,而後切換到 web 頁面:

開啓後,鼠標放在元素上,會被黃色高亮,點擊後,全部擁有相同 CSS選擇器 表達式的元素會被高亮。表達式會被插入到 python 代碼當前光標位置。建立下面的代碼,將光標停留在單引號中間:

def list_page(self, response):
    for each in response.doc('').items():

點擊一個電影的連接,CSS選擇器 表達式將會插入到你的代碼中,如此重複,插入翻頁的連接:

def list_page(self, response):
    for each in response.doc('HTML>BODY>DIV#wrapper>DIV#content>DIV.grid-16-8.clearfix>DIV.article>DIV>TABLE TR.item>TD>DIV.pl2>A').items():
        self.crawl(each.attr.href, callback=self.detail_page)
    # 翻頁
    for each in response.doc('HTML>BODY>DIV#wrapper>DIV#content>DIV.grid-16-8.clearfix>DIV.article>DIV.paginator>A').items():
        self.crawl(each.attr.href, callback=self.list_page)
翻頁是一個到本身的 callback 回調

電影詳情頁

再次點擊 run,follow 到詳情頁。使用 css selector helper 分別添加電影標題,打分和導演:

def detail_page(self, response):
    return {
        "url": response.url,
        "title": response.doc('HTML>BODY>DIV#wrapper>DIV#content>H1>SPAN').text(),
        "rating": response.doc('HTML>BODY>DIV#wrapper>DIV#content>DIV.grid-16-8.clearfix>DIV.article>DIV.indent.clearfix>DIV.subjectwrap.clearfix>DIV#interest_sectl>DIV.rating_wrap.clearbox>P.rating_self.clearfix>STRONG.ll.rating_num').text(),
        "導演": [x.text() for x in response.doc('a[rel="v:directedBy"]').items()],
    }

開始抓取

  1. 使用 run 單步調試你的代碼,對於用一個 callback 最好使用多個頁面類型進行測試。而後保存。
  2. 回到 Dashboard,找到你的項目
  3. 將 status 修改成 DEBUG 或 RUNNING
  4. 按 run 按鈕

反反爬蟲技術

愈來愈多的網站具備反爬蟲特性,有的用圖片隱藏關鍵數據,有的使用反人類的驗證碼等

爬蟲:

爬蟲的根本就是獲得一個網頁的源代碼數據。更深刻一些,就會出現和網頁進行POST交互從而獲取服務器接收POST請求後返回的數據!總結:爬蟲就是由計算機自動與服務器交互獲取數據的工具。(爬蟲請注意網站的Robot.txt文件!不要讓爬蟲違法!也不要讓爬蟲對網站形成傷害!)

反爬及反反爬概念:

基於服務器資源,保護數據等,不少網站是限制了爬蟲效果。那麼由人來充當爬蟲的角色時,咱們怎麼獲取網頁源代碼?大部分都是右鍵源文件。那麼有些網站屏蔽了右鍵,又該怎麼辦?基本常識,固然是按咱們的F12了

在把人看成爬蟲時,網頁屏蔽右鍵就是反爬取措施,F12就是咱們反反爬取的方式!

網站: https://github.com/luyishisi/...

例子

例子在

https://pan.baidu.com/disk/ho...

相關文章
相關標籤/搜索