Scrapy 和 Redis 分散式crawlers (蘋果日報為例)


目的

Scrapy 是一個強大通用型框架,但是資料一旦多了起來,就採用多機器進行加速爬取,但是 Scrapy不支持分散式,Scrapy-Redis 就因此而誕生,假設一個簡單的情形有100個 urls 會需要爬取,一個機器爬100次,兩個機器各爬50次,時間隨機器的增加而線性減少,我們只要可以有效地分配100個 urls 給多數機器就可以達到分散式爬取,Redis就是處理分配 urls 的任務。

網路上相關的介紹和原理非常多,但是詳細的實例卻很少或是常常有錯,因此本篇 blog 主要為介紹從零到一建出一個分散式爬取的簡單例子。

安裝Redis並測試遠端連線

a 機器 ip: 10.2.0.10
b 機器
兩台機器都需要安裝 Redis

sudo apt install redis-server

然後 a 機器需要設定外連ip

vim /etc/redis.conf

找到這行 -> #bind 127.0.0.1
修改為你自己的ip -> bind 10.2.0.10
重啟 Redis

sudo /etc/init.d/redis-server restart

b 機器測試能不能連過去

redis-cli -h 10.2.0.10

如果出現,代表連線成功(6379是預設的port)

10.2.0.10:6379

實戰 (蘋果日報)


url是 http://www.appledaily.com.tw/realtimenews/section/new/1 從1到10
要抓 list 的 title

也會進到內文中抓 content 的 title
總共抓取即時資訊的10頁,每一頁的標題和點進去標題後內文的標題

先安裝 scrapy 和 scrapy-redis

pip install scrapy
pip install scrapy-redis

scrapy 是一定要安裝的,scrapy-redis 則是改造了collection.deque,變成用redis來分配urls。
scrapy 不太好裝,不同OS和版本都會有不同問題,網路上資源也蠻多的但就要自己慢慢修好,裝起來也先跑跑看example,等到確定可以 work 再來以下的範例。

開始建立 scrapy project 下

scrapy startproject apple

進到剛建立好的 project

cd apple

看一下結構

tree


以下修改的方式都是參考 Scrapy-Redis 的 example
python 版本皆為 2.7
首先编辑 settings 文件 (settings.py)::

# -*- coding: utf-8 -*-

# 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

BOT_NAME = 'apple'
SPIDER_MODULES = ['apple.spiders']
NEWSPIDER_MODULE = 'apple.spiders'
USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)'
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# SCHEDULER_PERSIST 設定是否中斷後會繼續下載 (測試的時候最好條為 False,不然抓一次抓完就不會跑了)

SCHEDULER_PERSIST = False
# 判斷從Redis取出urls時的方式 PriorityQueue, Queue, Stack

SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"

#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"

ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}
# 調整LOG的形式,可改為 INFO ERROR WARNING...

LOG_LEVEL = 'DEBUG'
# Introduce an artifical delay to make use of parallelism. to speed up the

# crawl.

# 設定延遲時間

DOWNLOAD_DELAY = 1
# Redis 的 ip (此範例為 a機器)

REDIS_HOST = '10.2.0.10'
REDIS_PORT = 6379

編輯 items (items.py):

# Define here the models for your scraped items

# See documentation in:

# http://doc.scrapy.org/en/latest/topics/items.html

import scrapy
class AppleItem(scrapy.Item):
    # define the fields for your item here like:

    # name = scrapy.Field()

    title = scrapy.Field()

在 spiders 的資料夾中建立 apple.py ,爬蟲的主體

# -*- coding: utf-8 -*-

from scrapy_redis.spiders import RedisCrawlSpider
from ..items import AppleItem
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
import scrapy
from bs4 import BeautifulSoup

class AppleSpider(RedisCrawlSpider):
    name = 'apple'
    redis_key = 'apple'
    # LinkExtractor 可以給爬取urls制定規則,[1-10] 數字1,2,3...9,10

    rules = [Rule(LinkExtractor(allow=('/realtimenews/section/new/[1-10]$')),callback='parse_list',follow=True)]
    def parse_list(self, response):
        domain = 'http://www.appledaily.com.tw'
        res = BeautifulSoup(response.text)
        for news in res.select('.rtddt'):
            list_title = news.h1.string.encode('utf-8')
            print list_title
            # 傳入 list 的 url 給 parse_detail 去 parse content title

            yield scrapy.Request(domain + news.select('a')[0]['href'], self.parse_detail)
    def parse_detail(self, response):
        res = BeautifulSoup(response.text)
        content_title = res.find("h1", {"id": "h1"}).string.encode('utf-8')
        print content_title
        appleitem = AppleItem()
        appleitem['title'] = res.find("h1", {"id": "h1"}).string
        return appleitem

把 a 機器的整個 project 複製到 b 機器上

scp -r apple/ user@b機器ip:your path/apple/

分別進到兩機器

cd apple

執行 (兩台都要輸入這個指令)
scrapy crawl apple
這時兩個爬蟲都會啟動,呈現待機狀態,因為目前 Redis 沒有 url,因此要 push 進去
b 機器執行把 url push 到 redis 中

redis-cli -h 10.2.0.10 lpush apple http://appledaily.com.tw/realtimenews/section/new/

然後就可以看到兩個爬蟲開始爬了,仔細看一下 print 的過程,會發現爬取的東西都不一樣就代表成功了!
或是要輸出 json 來驗證也可以
只要把指令改成

scrapy crawl apple -o apple.json

兩台機器存的內容也會不一樣,或是第一次開一台,第二次開兩台,把第兩次的檔案合併會等於第一次爬取的檔案內容。

結尾

注意:

  1. RedisSpider 不需要寫 start_urls (很多教學寫分散式但是都沒用到這個 RedisSpider 有點傻眼,一般的 scrapy 是用這個 CrawlSpider,要啟用 Redis ,就要用 RedisSpider (apple.py裡面))
  2. 必須指定 redis_key (apple.py裡面),爬蟲才會去讀取 Redis 這個 key 中存的值,並根據指令的 key ,由 redis-cli -h 10.2.0.10 lpush key start_urls。
  3. 爬取的地方,我有點偷懶使用 BeautifulSoup ,官網就有直白的說 BeautifulSoup 就是慢,建議使用內建的 xpath, css selector...
    附上 scrapy_redis_example
#scrapy #Redis #Crawler






Related Posts

D3v4 & Canvas 工作坊 - D3 + Canvas 繪製動態路線圖

D3v4 & Canvas 工作坊 - D3 + Canvas 繪製動態路線圖

MTR04_1023

MTR04_1023

9. Composite

9. Composite




Sponsored



Comments