第一個分散式爬蟲專案

江玉郎發表於2018-08-15

首先,你想一下,怎樣提升自己的程式設計能力呢? 當然是閱讀優秀的程式碼,並且大量練習。

一 使用git 下載github 上優秀的程式碼

github 大家都不會陌生,今天我們先從github 上找一個優秀的分散式爬蟲程式碼來閱讀與學習。
首先開啟github, 搜尋 scrapy redis ,如圖所示,找到一個星數多的,這裡我取第一個,
這裡寫圖片描述
點進去之後出現如圖所示,我們要下載這些程式碼,下載的方式有兩種,一個是點選Download Zip 下載好後直接解壓出來,
今天介紹使用git 下載。
這裡寫圖片描述
先到該網站下載自己電腦對應版本,根據提示,點選安裝
這裡寫圖片描述
這裡有一點需要注意,一直選預設選項就好,不是特殊需要的話,不要隨便修改,
這裡寫圖片描述
假如這一步你選擇了第一個,以後還要手動配置多個環境變數,比較麻煩,所以強烈建議一直選預設的選項。
安裝完了以後,首先複製第二個圖片裡箭頭指的那行程式碼,其次,在命令列cd 進你要把程式碼儲存的資料夾,輸入 git clone和你剛才複製的網址,點選確認,如圖所示
這裡寫圖片描述
(箭頭所指表示下載好的檔案,我下載過了,所以下載的是另一個,原理都一樣)

二 介紹分散式爬蟲

首先,我們在Pycharm裡開啟剛才下載好的資料夾,可以看到,裡面有很多不同格式的檔案,作為學習,我們只介紹 其中 example 中的py 檔案,其中example資料夾後面的幾個Py 檔案,在以前都介紹過,所以這次主要介紹以前沒見過的檔案,偶爾有的需要修改會單獨提出來介紹
這裡寫圖片描述
今天的學習方法主要是類比法,就是通過和以前學習的scrapy檔案做比較,來說明分散式爬蟲的不同,這樣也能更加加深理解。

1 dmoz.py 檔案
這個檔案相當於我們以前在scrapy 中寫的爬蟲主檔案(只是說相當於,並不是說兩者的作用是相同的),可以看到前半部分和最後的parse 函式,都與以前學的相差無幾。
下面這個圖片是下載的該檔案的原始碼,
這裡寫圖片描述
這個程式碼是新增了說明之後的版本,為了更清晰的對比,下面程式碼中註釋掉的漢字是對程式碼的解釋說明,註釋掉的程式碼是以前我們的寫法,而且把url該為了紅袖添香 的網址

# 連結提取器
from scrapy.linkextractors import LinkExtractor
# CrawlSpider是爬取那些具有一定規則網站的常用的爬蟲,它基於Spider並有一些獨特屬性
from scrapy.spiders import CrawlSpider, Rule
# CrawlSpider是Spider的派生類,Spider類的設計原則是隻爬取start_urls中的url
# 而CrawlSpider類定義了一些規則(rules)來提供跟進連結(link)的方便機制,從爬取的網頁中獲取link並繼續爬取的工作更適合。


class DmozSpider(CrawlSpider):
    """Follow categories and extract links."""
    name = 'dmoz'
    # name = 'bole'
    allowed_domains = ['hongxiu.com']
    # allowed_domains = ['bole.com']
    start_urls = ['https://www.hongxiu.com/finish']

# rules: 是Rule物件的集合,用於匹配目標網站並排除干擾
    rules = [
        # 獲取網頁的指定內容,然後進入到指定的方法裡面
        # 找到class類名為.top-cat 的標籤
        Rule(LinkExtractor(restrict_css=('.top-cat', '.sub-cat', '.cat-item')),
             # 設定是否繼續執行後面的內容
             # 如果有回撥函式,則預設為False ,否則為True
             callback='parse_directory', follow=True),
    ]

    # def parse(self, response):
    def parse_directory(self, response):
        div_list = response.xpath('//div')
        for div in response.css('.title-and-desc'):
            yield {
                'name': div.css('.site-title::text').extract_first(),
                'description': div.css('.site-descr::text').extract_first().strip(),
                'link': div.css('a::attr(href)').extract_first(),
            }

作對比後,可以看到首先是匯入的模組不同,繼承的模組也不一樣。這裡只多了一個rules 使用規則,將在下面介紹它的詳細用法。
2 mycralwer_redis.py 檔案
這個就是今天的重點內容,分散式爬蟲的主檔案,先看一看都有什麼內容
這裡寫圖片描述
可以到與剛才的dmoz檔案很相似,但也有不同的地方,下面的程式碼結合著註釋,來一起看看每行程式碼的意思

from scrapy.spiders import Rule
from scrapy.linkextractors import LinkExtractor

from scrapy_redis.spiders import RedisCrawlSpider


class MyCrawler(RedisCrawlSpider):
    """Spider that reads urls from redis queue (myspider:start_urls)."""
    name = 'mycrawler_redis'
    # allowed_domains = [] 這裡沒有allowed_domains ,start_url也沒有的,
    # start_urls = [''] 這個是以前的寫法,這裡也有,只是不在這裡寫。

    # 類名: + start_urls 推薦寫法: 標準寫法
    redis_key = 'mycrawler:start_urls'

    rules = (
        # follow all links
        Rule(LinkExtractor(), callback='parse_page', follow=True),
    )

    # 這個裡面的內容以後再詳細介紹,這裡知道相關單詞就行
    def __init__(self, *args, **kwargs):
        # 動態的 定義 允許 域名 列表
        # Dynamically define the allowed domains list.
        # 此處動態定義域名列表
        domain = kwargs.pop('domain', '')
        self.allowed_domains = filter(None, domain.split(','))
        # super後面寫自己的類名
        super(MyCrawler, self).__init__(*args, **kwargs)

    def parse_page(self, response):
    # 這樣寫是為了自己測試用
        print('--------------------------')
        print(response.url)
        # return {
        #     'name': response.css('title::text').extract_first(),
        #     'url': response.url,
        # }

以上就是所有說明,我們來執行試一下,首先在命令列輸入redis-server redis.windows.conf 來啟動redis (若是提示錯誤,暫時可先用redis-server 啟動)
再重新進入進入一個命令列,輸入redis-cli, 啟動客戶端,輸入lpush mycrawler:start_urls https://www.hongxiu.com 點選確認(最後的網址是我自己作為測試用的,當然可以修改)
這裡寫圖片描述
再重新進入一個新的命令列,cd 進 example 這個資料夾,輸入scrapy crawl mycrawler_redis, 點選確認。
開啟redis.exe檔案,檢視我們輸入的網頁,如圖所示
這裡寫圖片描述
表示執行成功。
3 myspider_redis.py 檔案
首先看一下原始碼
這裡寫圖片描述
再看看註釋過後的程式碼

from scrapy_redis.spiders import RedisSpider


class MySpider(RedisSpider):
    """Spider that reads urls from redis queue (myspider:start_urls)."""
    name = 'myspider_redis'
    redis_key = 'myspider:start_urls'
    #
    # def __init__(self, *args, **kwargs):
    #     # Dynamically define the allowed domains list.
    #     domain = kwargs.pop('domain', '')
    #     self.allowed_domains = filter(None, domain.split(','))
    #     super(MySpider, self).__init__(*args, **kwargs)

    def parse(self, response):
        # return {
        #     'name': response.css('title::text').extract_first(),
        #     'url': response.url,
        # }
        print('----------------')
        print(response.url)

這個和mycrawler_redis檔案差不多,後面的命令列輸入都差不多,不過多介紹
至於兩者有什麼區別,以後再寫。
4 setting.py 檔案
先看原檔案:
這裡寫圖片描述
下來對以後可能要用到的進行說明

# Scrapy settings for example project
#
# For simplicity, this file contains only the most important settings by
# default. All the other settings are documented here:
#
#     http://doc.scrapy.org/topics/settings.html
#
SPIDER_MODULES = ['example.spiders']
NEWSPIDER_MODULE = 'example.spiders'

USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)'

# 使用scrapy-redis 的去重類,不使用scrapy 預設的去重類
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrpyu-redis 的排程器,不使用scrapy 預設的排程器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 控制爬蟲是否允許暫停
SCHEDULER_PERSIST = True
# 佇列形式,先進先出
# 那個請求先放入到請求佇列裡面, 那個請求就先執行
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
# 棧形式 ,先進後出
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"

#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"

ITEM_PIPELINES = {
    'example.pipelines.ExamplePipeline': 300,
    # 使用redis 資料庫所要新增的管道,
    # 如果使用redis資料庫,必須新增
    'scrapy_redis.pipelines.RedisPipeline': 400,
}

# log 日誌,level 等級
# debug 除錯
# LOG_LEVEL = 'DEBUG'

# 域名為字串,如果不寫,預設為本機
# 資料庫的ip
REDIS_HOST = '127.0.0.1'
# 埠為數字
REDIS_PORT = 6379


# Introduce an artifical delay to make use of parallelism. to speed up the
# crawl.
# 限制爬蟲速度
# DOWNLOAD_DELAY = 1

下面用一個例子過一遍整體流程.
我們中國紅娘網為例,爬取特定人的姓名,年齡等,
(這裡雖然說是分散式爬蟲,但並不是真正的分散式,頂多算是單機的分散式爬蟲)
1命令列cd 進你要建立爬蟲的資料夾,輸入:scrpy startproject hongniang ,然後cd hongniang
2 這裡需要注意,因為要建立分散式爬蟲,所以這裡的命令是

scrapy genspider -t crawl hong hongniang.com

這裡寫圖片描述
3 進入hong.py 檔案,程式碼見下圖

# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from ..items import HongniangItem
from scrapy_redis.spiders import RedisCrawlSpider

class HongSpider(RedisCrawlSpider):
    name = 'hong'
    allowed_domains = ['hongniang.com']
    start_urls = ['http://hongniang.com/']
    redis_key = 'hongspider:start_urls'
    # 特定人的url
    page_link = LinkExtractor(allow=r'http://www.hongniang.com/index/search?sort=0&wh=0&sex=2&starage=1'
                                    r'&province=%E6%B2%B3%E5%8D%97&city=%E9%83%91%E5%B7%9E&page=1')
    # 取每個人的詳細地址
    person_link = LinkExtractor(allow=r'http://www.hongniang.com/user/member/id/\d+')

    rules = (
        # Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),

        # 這個逗號是必須加的,哪怕只有一個Rule,也要加。
        Rule(page_link, True),
        Rule(person_link, callback='parse_item', follow=False)


        # follow 原始碼
        # if follow is None:
        #     self.follow = False if callback else True
        # else:
        #     self.follow = follow
    )

    def parse_item(self, response):
        # i = {}
        # #i['domain_id'] = response.xpath('//input[@id="sid"]/@value').extract()
        # #i['name'] = response.xpath('//div[@id="name"]').extract()
        # #i['description'] = response.xpath('//div[@id="description"]').extract()
        # return i
        header = response.xpath('//img[@id="pic_"]/@src').get()
        name = response.xpath('//div[@class="name nickname"]/text()').get()
        age = response.xpath('//div[@class="info2"]//ul[1]/li[1]/text()').get()
        height = response.xpath('//div[@class="info2"]//ul[2]/li[1]/text()').get()
        item = HongniangItem()

        item['name'] = name
        item['age'] = age
        item['header'] = header
        item['height'] = height

        yield item

4 在items.py 進行相關配置,在setting.py 進行配置,把上面介紹過的setting裡面example 改成這裡資料夾的名字就行。
5 最關鍵的三步:
5.1 終端命令列 輸入 redis-server 啟動redis
5.2 重新開啟終端命令列先cd進爬蟲檔案,輸入 scrapy crawl hong 會看到下圖,表示等待的狀態
這裡寫圖片描述
5.3 重新開啟一個終端命令列: 輸入 redis-cli 啟動客戶端,點選確認,再輸入:lpush hongspider:start_urls http://www.hongniang.com 點選確認
5.4 開啟redis.exe 檔案,看到如圖所示表示成功
這裡寫圖片描述
(2和3的順序也可以互換)

至此,一個簡單的“分散式爬蟲”就算完成了,以後我將接受如何在多臺電腦上使用分散式爬蟲

相關文章