scrapy學習筆記

scrapy是python最有名的爬蟲框架之一,能夠很方便的進行web抓取,而且提供了很強的定製型,這裏記錄簡單學習的過程和在實際應用中會遇到的一些常見問題css

1、安裝

在安裝scrapy以前有一些依賴須要安裝,不然可能會安裝失敗,scrapy的選擇器依賴於lxml,還有Twisted網絡引擎,下面是ubuntu下安裝的過程html

1. linux下安裝

# 1. 安裝xml依賴庫
$ sudo apt-get install libxml2 libxml2-dev
$ sudo apt-get install libxslt1-dev
$ sudo apt-get install python-libxml2

# 2. 安裝lxml
$ sudo pip install lxml

# 3. 安裝Twisted(版本能夠換成最新的),用pip也能夠,若是失敗的話下載源碼安裝,以下
$ wget https://pypi.python.org/packages/6b/23/8dbe86fc83215015e221fbd861a545c6ec5c9e9cd7514af114d1f64084ab/Twisted-16.4.1.tar.bz2#md5=c6d09bdd681f538369659111f079c29d
$ tar xjf Twisted-16.4.1.tar.bz2
$ cd Twisted-16.4.1
$ sudo python setup.py install

# 3. 安裝scrapy
$ sudo pip install scrapy

http://lxml.de/installation.htmlnode

2. Mac下安裝

# 安裝xml依賴庫
$ xcode-select —install

# 其實相關依賴pip會自動幫咱們裝上
$ pip install scrapy

mac下安裝有時候會失敗,建議使用virtualenv安裝在獨立的環境下,能夠減小一些問題,由於mac系統自帶python,例如一些依賴庫依賴的一些新的版本,而升級新版本會把舊版本卸載掉,卸載可能會有權限的問題python

2、基本使用

1. 初始化scrapy項目

咱們可使用命令行初始化一個項目linux

$ scrapy startproject tutorial

這裏能夠查看scrapy更多其餘的命令git

初始化完成後,咱們獲得下面目錄結構github

scrapy.cfg:         項目的配置文件
tutorial/:          該項目的python模塊, 在這裏添加代碼
    items.py:       項目中的item文件
    pipelines.py:   項目中的pipelines文件.
    settings.py:    項目全局設置文件.
    spiders/        爬蟲模塊目錄

咱們先看一下scrapy的處理流程
流程圖web

scrapy由下面幾個部分組成正則表達式

  • spiders:爬蟲模塊,負責配置須要爬取的數據和爬取規則,以及解析結構化數據數據庫

  • items:定義咱們須要的結構化數據,使用至關於dict

  • pipelines:管道模塊,處理spider模塊分析好的結構化數據,如保存入庫等

  • middlewares:中間件,至關於鉤子,能夠對爬取先後作預處理,如修改請求header,url過濾等

咱們先來看一個例子,在spiders目錄下新建一個模塊DmozSpider.py

import scrapy

class DmozSpider(scrapy.Spider):
    # 必須定義
    name = "dmoz"
    # 初始urls
    start_urls = [
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/",
        "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
    ]

    # 默認response處理函數
    def parse(self, response):
        # 把結果寫到文件中
        filename = response.url.split("/")[-2]
        with open(filename, 'wb') as f:
            f.write(response.body)

打開終端進入根目錄,執行下面命令

$ scrapy crawl dmoz

爬蟲開始爬取start_urls定義的url,並輸出到文件中,最後輸出爬去報告,會輸出爬取得統計結果

2016-09-13 10:36:43 [scrapy] INFO: Spider opened
2016-09-13 10:36:43 [scrapy] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2016-09-13 10:36:43 [scrapy] DEBUG: Telnet console listening on 127.0.0.1:6023
2016-09-13 10:36:44 [scrapy] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/> (referer: None)
2016-09-13 10:36:45 [scrapy] DEBUG: Crawled (200) <GET http://www.dmoz.org/Computers/Programming/Languages/Python/Books/> (referer: None)
2016-09-13 10:36:45 [scrapy] INFO: Closing spider (finished)
2016-09-13 10:36:45 [scrapy] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 548,
 'downloader/request_count': 2,
 'downloader/request_method_count/GET': 2,
 'downloader/response_bytes': 16179,
 'downloader/response_count': 2,
 'downloader/response_status_count/200': 2,
 'finish_reason': 'finished',
 'finish_time': datetime.datetime(2016, 9, 13, 2, 36, 45, 585113),
 'log_count/DEBUG': 3,
 'log_count/INFO': 7,
 'response_received_count': 2,
 'scheduler/dequeued': 2,
 'scheduler/dequeued/memory': 2,
 'scheduler/enqueued': 2,
 'scheduler/enqueued/memory': 2,
 'start_time': datetime.datetime(2016, 9, 13, 2, 36, 43, 935790)}
2016-09-13 10:36:45 [scrapy] INFO: Spider closed (finished)

這裏咱們完成了簡單的爬取和保存的操做,會在根目錄生成兩個文件ResourcesBooks

2. 經過代碼運行爬蟲

每次進入控制檯運行爬蟲仍是比較麻煩的,並且很差調試,咱們能夠經過CrawlerProcess經過代碼運行爬蟲,新建一個模塊run.py

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

from spiders.DmozSpider import DmozSpider

# 獲取settings.py模塊的設置
settings = get_project_settings()
process = CrawlerProcess(settings=settings)

# 能夠添加多個spider
# process.crawl(Spider1)
# process.crawl(Spider2)
process.crawl(DmozSpider)

# 啓動爬蟲,會阻塞,直到爬取完成
process.start()

參考:http://doc.scrapy.org/en/latest/topics/practices.html#run-scrapy-from-a-script

3、Scrapy類

如上面的DmozSpider類,爬蟲類繼承自scrapy.Spider,用於構造Request對象給Scheduler

1. 經常使用屬性與方法

屬性

  • name:爬蟲的名字,必須惟一(若是在控制檯使用的話,必須配置)

  • start_urls:爬蟲初始爬取的連接列表

  • parse:response結果處理函數

  • custom_settings:自定義配置,覆蓋settings.py中的默認配置

方法

  • start_requests:啓動爬蟲的時候調用,默認是調用make_requests_from_url方法爬取start_urls的連接,能夠在這個方法裏面定製,若是重寫了該方法,start_urls默認將不會被使用,能夠在這個方法裏面定製一些自定義的url,如登陸,從數據庫讀取url等,本方法返回Request對象

  • make_requests_from_url:默認由start_requests調用,能夠配置Request對象,返回Request對象

  • parse:response到達spider的時候默認調用,若是在Request對象配置了callback函數,則不會調用,parse方法能夠迭代返回ItemRequest對象,若是返回Request對象,則會進行增量爬取

2. Request與Response對象

每一個請求都是一個Request對象,Request對象定義了請求的相關信息(url, method, headers, body, cookie, priority)和回調的相關信息(meta, callback, dont_filter, errback),一般由spider迭代返回

其中meta至關於附加變量,能夠在請求完成後經過response.meta訪問

請求完成後,會經過Response對象發送給spider處理,經常使用屬性有(url, status, headers, body, request, meta, )

詳細介紹參考官網

看下面這個例子

from scrapy import Spider
from scrapy import Request

class TestSpider(Spider):
    name = 'test'
    start_urls = [
        "http://www.qq.com/",
    ]

    def login_parse(self, response):
        ''' 若是登陸成功,手動構造請求Request迭代返回 '''
        print response
        for i in range(0, 10):
            yield Request('http://www.example.com/list/1?page={0}'.format(i))

    def start_requests(self):
        ''' 覆蓋默認的方法(忽略start_urls),返回登陸請求頁,制定處理函數爲login_parse '''
        return Request('http://www.example.com/login', method="POST" body='username=bomo&pwd=123456', callback=self.login_parse)


    def parse(self, response):
        ''' 默認請求處理函數 '''
        print response

4、Selector

上面咱們只是爬取了網頁的html文本,對於爬蟲,咱們須要明確咱們須要爬取的結構化數據,須要對原文本進行解析,解析的方法一般有下面這些

  • 普通文本操做

  • 正則表達式:re

  • Dom樹操做:BeautifulSoup

  • XPath選擇器:lxml

scrapy默認支持選擇器的功能,自帶的選擇器構建與lxml之上,並對其進行了改進,使用起來更爲簡潔明瞭

1. XPath選擇器

XPpath是標準的XML文檔查詢語言,能夠用於查詢XML文檔中的節點和內容,關於XPath語法,能夠參見這裏

先看一個例子,經過html或xml構造Selector對象,而後經過xpath查詢節點,並解析出節點的內容

from scrapy import Selector

html = '<html><body><span>good</span><span>buy</span></body></html>'
sel = Selector(text=html)
nodes = sel.xpath('//span')
for node in nodes:
    print node.extract()

Selector至關於節點,經過xpath去到子節點集合(SelectorList),能夠繼續搜索,經過extract方法能夠取出節點的值,extract方法也能夠做用於SelectorList,對於SelectorList能夠經過extract_first取出第一個節點的值

  • 經過text()取出節點的內容

  • 經過@href去除節點屬性值(這裏是取出href屬性的值)

  • 直接對節點取值,則是輸出節點的字符串

2. CSS選擇器

除了XPath選擇器,scrapy還支持css選擇器

html = """
        <html>
            <body>
                <span>good</span>
                <span>buy</span>
                <ul>
                    <li class="video_part_lists">aa<li>
                    <li class="video_part_lists">bb<li>
                    <li class="audio_part_lists">cc<li>
                    <li class="video_part_lists">
                        <a href="/">主頁</a>
                    <li>
                </ul>
            </body>
        </html>
        """
sel = Selector(text=html)

# 選擇class爲video_part_lists的li節點
lis = sel.css('li.video_part_lists')

for li in lis:
    # 選擇a節點的屬性
    print li.css('a::attr(href)').extract()

關於css選擇器更多的規則,能夠見w3c官網

5、Item類

上面咱們只是爬取了網頁的html文本,對於爬蟲,咱們須要明確咱們須要爬取的結構化數據,咱們定義一個item存儲分類信息,scrapy的item繼承自scrapy.Item

from scrapy import Item, Field

class DmozItem(Item):
    title = Field()
    link = Field()
    desc = Field()

scrapy.Item的用法與python中的字典用法基本同樣,只是作了一些安全限制,屬性定義使用Field,這裏只是進行了聲明,而不是真正的屬性,使用的時候經過鍵值對操做,不支持屬性訪問

what, 好坑爹,這意味着全部的屬性賦值都得用字符串了,這裏有解釋(仍是沒太明白)

修改DmozSpider的parse方法

class DmozSpider(scrapy.Spider):
    ...
    def parse(self, response):
        for sel in response.xpath('//ul/li'):
            dmoz_item = DmozItem()
            dmoz_item['title'] = sel.xpath('a/text()').extract()
            dmoz_item['link'] = sel.xpath('a/@href').extract()
            dmoz_item['desc'] = sel.xpath('text()').extract()
            print dmoz_item

6、Pipeline

spider負責爬蟲的配置,item負責聲明結構化數據,而對於數據的處理,在scrapy中使用管道的方式進行處理,只要註冊過的管道均可以處理item數據(處理,過濾,保存)

下面看看管道的聲明方式,這裏定義一個預處理管道PretreatmentPipeline.py,若是item的title爲None,則設置爲空字符串

class PretreatmentPipeline(object):
    def process_item(self, item, spider):
        if item['title']:
            # 不讓title爲空
            item['title'] = ''
        return item

再定義一個過濾重複數據的管道DuplicatesPipeline.py,當link重複,則丟棄

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):
    def __init__(self):
        self.links = set()

    def process_item(self, item, spider):
        if item['link'] in self.links:
            # 跑出DropItem表示丟掉數據
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.links.add(item['link'])
            return item

最後能夠定義一個保存數據的管道,能夠把數據保存到數據庫中

from scrapy.exceptions import DropItem
from Database import Database

class DatabasePipeline(object):
    def __init__(self):
        self.db = Database

    def process_item(self, item, spider):
        if self.db.item_exists(item['id']):
            self.db.update_item(item)
        else:
            self.db.insert_item(item)

定義好管道以後咱們須要配置到爬蟲上,咱們在settings.py模塊中配置,後面的數字表示管道的順序

ITEM_PIPELINES = {
    'pipelines.DuplicatesPipeline.DuplicatesPipeline': 1,
    'pipelines.PretreatmentPipeline.PretreatmentPipeline': 2,
}

咱們也能夠爲spider配置單獨的pipeline

class TestSpider(Spider):
    # 自定義配置
    custom_settings = {
        # item處理管道
        'ITEM_PIPELINES': {
            'tutorial.pipelines.FangDetailPipeline.FangDetailPipeline': 1,
        },
    }
    ...

除了process_item方法外,pipeline還有open_spiderspider_closed兩個方法,在爬蟲啓動和關閉的時候調用

7、Rule

爬蟲的一般須要在一個網頁裏面爬去其餘的連接,而後一層一層往下爬,scrapy提供了LinkExtractor類用於對網頁連接的提取,使用LinkExtractor須要使用CrawlSpider爬蟲類中,CrawlSpiderSpider相比主要是多了rules,能夠添加一些規則,先看下面這個例子,爬取鏈家網的連接

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

class LianjiaSpider(CrawlSpider):
    name = "lianjia"

    allowed_domains = ["lianjia.com"]

    start_urls = [
        "http://bj.lianjia.com/ershoufang/"
    ]

    rules = [
        # 匹配正則表達式,處理下一頁
        Rule(LinkExtractor(allow=(r'http://bj.lianjia.com/ershoufang/pg\s+$',)), callback='parse_item'),

        # 匹配正則表達式,結果加到url列表中,設置請求預處理函數
        # Rule(FangLinkExtractor(allow=('http://www.lianjia.com/client/', )), follow=True, process_request='add_cookie')
    ]

    def parse_item(self, response):
        # 這裏與以前的parse方法同樣,處理
        pass

1. Rule對象

Role對象有下面參數

  • link_extractor:連接提取規則

  • callback:link_extractor提取的連接的請求結果的回調

  • cb_kwargs:附加參數,能夠在回調函數中獲取到

  • follow:表示提取的連接請求完成後是否還要應用當前規則(boolean),若是爲False則不會對提取出來的網頁進行進一步提取,默認爲False

  • process_links:處理全部的連接的回調,用於處理從response提取的links,一般用於過濾(參數爲link列表)

  • process_request:連接請求預處理(添加header或cookie等)

2. LinkExtractor

LinkExtractor經常使用的參數有:

  • allow:提取知足正則表達式的連接

  • deny:排除正則表達式匹配的連接(優先級高於allow

  • allow_domains:容許的域名(能夠是strlist

  • deny_domains:排除的域名(能夠是strlist

  • restrict_xpaths:提取知足XPath選擇條件的連接(能夠是strlist

  • restrict_css:提取知足css選擇條件的連接(能夠是strlist

  • tags:提取指定標籤下的連接,默認從aarea中提取(能夠是strlist

  • attrs:提取知足擁有屬性的連接,默認爲href(類型爲list

  • unique:連接是否去重(類型爲boolean

  • process_value:值處理函數(優先級大於allow

關於LinkExtractor的詳細參數介紹見官網

注意:若是使用rules規則,請不要覆蓋或重寫CrawlSpiderparse方法,不然規則會失效,可使用parse_start_urls方法

8、Middleware

從最開始的流程圖能夠看到,爬去一個資源連接的流程,首先咱們配置spider相關的爬取信息,在啓動爬取實例後,scrapy_engine從Spider取出Request(通過SpiderMiddleware),而後丟給Scheduler(通過SchedulerMiddleware),Scheduler接着把請求丟給Downloader(通過DownloadMiddlware),Downloader把請求結果丟還給Spider,而後Spider把分析好的結構化數據丟給Pipeline,Pipeline進行分析保存或丟棄,這裏面有4個角色

scrapy有下面三種middlewares

  • SpiderMiddleware:一般用於配置爬蟲相關的屬性,引用連接設置,Url長度限制,成功狀態碼設置,爬取深度設置,爬去優先級設置等

  • DownloadMiddlware:一般用於處理下載以前的預處理,如請求Header(Cookie,User-Agent),登陸驗證處理,重定向處理,代理服務器處理,超時處理,重試處理等

  • SchedulerMiddleware(已經廢棄):爲了簡化框架,調度器中間件已經被廢棄,使用另外兩個中間件已經夠用了

1. SpiderMiddleware

爬蟲中間件有下面幾個方法

  • process_spider_input:當response經過spider的時候被調用,返回None(繼續給其餘中間件處理)或拋出異常(不會給其餘中間件處理,當成異常處理)

  • process_spider_output:當spider有item或Request輸出的時候調動

  • process_spider_exception:處理出現異常時調用

  • process_start_requests:spider當開始請求Request的時候調用

下面是scrapy自帶的一些中間件(在scrapy.spidermiddlewares命名空間下)

  • UrlLengthMiddleware

  • RefererMiddleware

  • OffsiteMiddleware

  • HttpErrorMiddleware

  • DepthMiddleware

咱們本身實現一個SpiderMiddleware

TODO

參考連接:http://doc.scrapy.org/en/latest/topics/spider-middleware.html

2. DownloaderMiddleware

下載中間件有下面幾個方法

  • process_request:請求經過下載器的時候調用

  • process_response:請求完成後調用

  • process_exception:請求發生異常時調用

  • from_crawler:從crawler構造的時候調用

  • from_settings:從settings構造的時候調用

  • ``

更多詳細的參數解釋見這裏

在爬取網頁的時候,使用不一樣的User-Agent能夠提升請求的隨機性,定義一個隨機設置User-Agent的中間件RandomUserAgentMiddleware

import random

class RandomUserAgentMiddleware(object):
    """Randomly rotate user agents based on a list of predefined ones"""

    def __init__(self, agents):
        self.agents = agents

    # 從crawler構造,USER_AGENTS定義在crawler的配置的設置中
    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.getlist('USER_AGENTS'))

    # 從settings構造,USER_AGENTS定義在settings.py中
    @classmethod
    def from_settings(cls, settings):
        return cls(settings.getlist('USER_AGENTS'))

    def process_request(self, request, spider):
        # 設置隨機的User-Agent
        request.headers.setdefault('User-Agent', random.choice(self.agents))

settings.py設置USER_AGENTS參數

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",
]

配置爬蟲中間件的方式與pipeline相似,第二個參數表示優先級

# 配置爬蟲中間件
SPIDER_MIDDLEWARES = {
    'myproject.middlewares.CustomSpiderMiddleware': 543,
    # 若是想禁用默認的中間件的話,能夠設置其優先級爲None
    'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': None,
}

# 配置下載中間件
DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.RandomUserAgentMiddleware': 543,
    'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': None,
}

3. 代理服務器

爬蟲最怕的就是封ip,這時候就須要代理服務器來爬取,scrapy設置代理服務器很是簡單,只須要在請求前設置Request對象的meta屬性,添加proxy值便可,一般咱們能夠經過中間件來作

class ProxyMiddleware(object):
    def process_request(self, request, spider):
        proxy = 'https://178.33.6.236:3128'     # 代理服務器
        request.meta['proxy'] = proxy

9、緩存

scrapy默認已經自帶了緩存的功能,一般咱們只須要配置便可,打開settings.py

# 打開緩存
HTTPCACHE_ENABLED = True

# 設置緩存過時時間(單位:秒)
#HTTPCACHE_EXPIRATION_SECS = 0

# 緩存路徑(默認爲:.scrapy/httpcache)
HTTPCACHE_DIR = 'httpcache'

# 忽略的狀態碼
HTTPCACHE_IGNORE_HTTP_CODES = []

# 緩存模式(文件緩存)
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

更多參數參見這裏

10、多線程

scrapy網絡請求是基於Twisted,而Twisted默認支持多線程,並且scrapy默認也是經過多線程請求的,而且支持多核CPU的併發,一般只須要配置一些參數便可

# 默認Item併發數:100
CONCURRENT_ITEMS = 100

# 默認Request併發數:16
CONCURRENT_REQUESTS = 16

# 默認每一個域名的併發數:8
CONCURRENT_REQUESTS_PER_DOMAIN = 8

# 每一個IP的最大併發數:0表示忽略
CONCURRENT_REQUESTS_PER_IP = 0

更多參數參見這裏

11、常見問題

1. 項目名稱問題

在使用的時候遇到過一個問題,在初始化scrapy startproject tutorial的時候,若是使用了一些特殊的名字,如:test, fang等單詞的話,經過get_project_settings方法獲取配置的時候會出錯,改爲tutorial或一些複雜的名字的時候不會

ImportError: No module named tutorial.settings

這是一個bug,在github上有提到:https://github.com/scrapy/scrapy/issues/428,但貌似沒有徹底修復,修改一下名字就行了(固然scrapy.cfgsettings.py裏面也須要修改)

2. 爲每一個pipeline配置spider

上面咱們是在settings.py裏面配置pipeline,這裏的配置的pipeline會做用於全部的spider,咱們能夠爲每個spider配置不一樣的pipeline,設置Spidercustom_settings對象

class LianjiaSpider(CrawlSpider):
    ...
    # 自定義配置
    custom_settings = {
        'ITEM_PIPELINES': {
            'tutorial.pipelines.TestPipeline.TestPipeline': 1,
        }
    }

3. 獲取提取連接的節點信息

經過LinkExtractor提取的scrapy.Link默認不帶節點信息,有時候咱們須要節點的其餘attribute屬性,scrapy.Link有個text屬性保存從節點提取的text值,咱們能夠經過修改lxmlhtml._collect_string_content變量爲etree.tostring,這樣能夠在提取節點值就變味渲染節點scrapy.Link.text,而後根據scrapy.Link.text屬性拿到節點的html,最後提取出咱們須要的值

from lxml import etree
import scrapy.linkextractors.lxmlhtml
scrapy.linkextractors.lxmlhtml._collect_string_content = etree.tostring

4. 從數據庫中讀取urls

有時候咱們已經把urls下載到數據庫了,而不是在start_urls裏配置,這時候能夠重載spider的start_requests方法

def start_requests(self):
    for u in self.db.session.query(User.link):
        yield Request(u.link)

咱們還能夠在Request添加元數據,而後在response中訪問

def start_requests(self):
    for u in self.db.session.query(User):
        yield Request(u.link, meta={'name': u.name})

def parse(self, response):
    print response.url, response.meta['name']

5. 如何進行循環爬取

有時候咱們須要爬取的一些常常更新的頁面,例如:間隔時間爲2s,爬去一個列表前10頁的數據,從第一頁開始爬,爬完成後從新回到第一頁

目前的思路是,經過parse方法迭代返回Request進行增量爬取,因爲scrapy默認由緩存機制,須要修改

6. 關於去重

scrapy默認有本身的去重機制,默認使用scrapy.dupefilters.RFPDupeFilter類進行去重,主要邏輯以下

if include_headers:
    include_headers = tuple(to_bytes(h.lower())
                             for h in sorted(include_headers))
cache = _fingerprint_cache.setdefault(request, {})
if include_headers not in cache:
    fp = hashlib.sha1()
    fp.update(to_bytes(request.method))
    fp.update(to_bytes(canonicalize_url(request.url)))
    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[include_headers] = fp.hexdigest()
return cache[include_headers]

默認的去重指紋是sha1(method + url + body + header),這種方式並不能過濾不少,例若有一些請求會加上時間戳的,基本每次都會不一樣,這時候咱們須要自定義過濾規則

from scrapy.dupefilter import RFPDupeFilter

class CustomURLFilter(RFPDupeFilter):
    """ 只根據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 True
        else:
            self.urls_seen.add(request.url)

配置setting

DUPEFILTER_CLASS = 'tutorial.custom_filters.CustomURLFilter'

7. 如何在Pipeline中處理不一樣的Item

scrapy全部的迭代出來的的Item都會通過全部的Pipeline,若是須要處理不一樣的Item,只能經過isinstance()方法進行類型判斷,而後分別進行處理,暫時沒有更好的方案

8. url按順序執行

咱們能夠經過Request的priority控制url的請求的執行順序,但因爲網絡請求的不肯定性,不能保證返回也是按照順序進行的,若是須要進行逐個url請求的話,吧url列表放在meta對象裏面,在response的時候迭代返回下一個Request對象到調度器,達到順序執行的目的,暫時沒有更好的方案

12、總結

scrapy雖然是最有名的python爬蟲框架,可是仍是有不少不足,例如,item不能單獨配置給制定的pipeline,每個爬取的全部item都會走遍全部的管道,須要在管道里面去判斷不一樣類型的item,若是在pipelines和items比較多的項目,將會讓項目變得很是臃腫

若有問題歡迎到個人博客留言

十3、參考連接

最後安利一下本身的博客:http://zhengbomo.github.com

相關文章
相關標籤/搜索