在之前 Scrapy 的基本使用當中,spider 如果要重新發送請求的話,就需要自己解析頁面,然後發送請求。而 CrawlSpider 則可以通過設置 url 條件自動發送請求。
CrawlSpider 是 Spider 的一個派生類,相對於 Spider 來說,功能進行了更新,使用也更加方便。
CrawlSpider
創建 CrawlSpider
和之前創建 spider 一樣,雖然可以在創建 Scrapy 項目之後手動構造 spider,但是 Scrapy 也給出了在終端下創建 CrawlSpider 的指令:
scrapy genspider -t crawl spidername domainname
在終端中使用上邊的指令就能夠使用 Scrapy 中的模板創建 CrawlSpider。
LinkExtractors
CrawlSpider 與 spider 不同的是就在於下一次請求的 url 不需要自己手動解析,而這一點則是通過 LinkExtractors 實現的。LinkExtractors 原型爲:
class LxmlLinkExtractor(FilteringLinkExtractor):
def __init__(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(),
tags=('a', 'area'), attrs=('href',), canonicalize=False,
unique=True, process_value=None, deny_extensions=None, restrict_css=(),
strip=True, restrict_text=None):
tags, attrs = set(arg_to_iter(tags)), set(arg_to_iter(attrs))
lx = LxmlParserLinkExtractor(
tag=lambda x: x in tags,
attr=lambda x: x in attrs,
unique=unique,
process=process_value,
strip=strip,
canonicalized=canonicalize
)
super(LxmlLinkExtractor, self).__init__(lx, allow=allow, deny=deny,
allow_domains=allow_domains, deny_domains=deny_domains,
restrict_xpaths=restrict_xpaths, restrict_css=restrict_css,
canonicalize=canonicalize, deny_extensions=deny_extensions,
restrict_text=restrict_text)
其中的參數爲:
- allow:允許的 url。所有滿足這個正則表達式的 url 都會被提取
- deny:禁止的 url。所有滿足這個正則表達式的 url 都不會被提取
- allow_domains:允許的域名。只有在這個裏面指定的域名的 url 纔會被提取
- deny_domains:禁止的域名。所有在這個裏面指定的域名的 url 都不會被提取
- restrict_xpaths:嚴格的 xpath。和 allow 共同過濾鏈接
Rule
LinkExtractors 需要傳遞到 Rule 類對象中才能發揮作用。Rule 類爲:
class Rule:
def __init__(self, link_extractor=None, callback=None, cb_kwargs=None, follow=None,
process_links=None, process_request=None, errback=None):
self.link_extractor = link_extractor or _default_link_extractor
self.callback = callback
self.errback = errback
self.cb_kwargs = cb_kwargs or {}
self.process_links = process_links or _identity
self.process_request = process_request or _identity_process_request
self.process_request_argcount = None
self.follow = follow if follow is not None else not callback
常見的參數爲:
- link_extractor:LinkExtractor 對象,用於定義爬取規則
- callback:對於滿足該規則的 url 所要執行的回掉函數,類似於之前提到的 scrapy.Request() 中的callback。而 CrawlSpider 使用了 parse 作爲回調函數,因此不要覆蓋 parse 作爲回調函數自己的回調函數
- follow:從 response 中提取的鏈接是否需要跟進
- process_links:從 link_extractor 中獲取到鏈接後會傳遞給這個函數,用來過濾不需要爬取的鏈接
除了上述的這些差別,Crawlspider 和 spider 基本沒有什麼差別了。
settings.py
仍舊需要設置:
- ROBOTSTXT_OBEY:設置爲 False,否則爲 True。True 表示遵守機器協議,此時爬蟲會首先找 robots.txt 文件,如果找不到則會停止
- DEFAULT_REQUEST_HEADERS:默認請求頭,可以在其中添加 User-Agent,表示該請求是從瀏覽器發出的,而不是爬蟲
- DOWNLOAD_DELAY:表示下載的延遲,防止過快
- ITEM_PIPELINES:啓用 pipelines.py
items.py
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class StepItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field()
author = scrapy.Field()
pub_time = scrapy.Field()
content = scrapy.Field()
spider
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from step.items import StepItem
class WechatSpider(CrawlSpider):
name = 'wechat'
allowed_domains = ['www.wxapp-union.com']
start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1']
rules = (
# 該 Rule 沒有 callback 參數,說明不需要對符合該條件的 url 執行回調操作
Rule(LinkExtractor(allow=r'.+page=\d'), follow=True),
# 該 Rule 存在 callback 參數,說明需要對符合該條件的 url 執行回調操作
Rule(LinkExtractor(allow=r'.+article-.+\.html'),callback='parse_item',follow=False)
)
def parse_item(self, response):
title = response.xpath("//h1[@class='ph']/text()").get()
author =response.xpath("//p[@class='authors']/a/text()").get()
pub_time = response.xpath("//p[@class='authors']/span/text()").get()
content = response.xpath("//div[@class='content_middle cl']//text()").getall()
content = ''.join(content).strip()
item = StepItem(title=title,author=author,pub_time=pub_time,content=content)
yield item
pipelines.py
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy.exporters import JsonLinesItemExporter
class StepPipeline:
def __init__(self):
self.fp = open('wechat.json','wb')
self.export = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
def open_spider(self,spider):
print('spider begin.')
def process_item(self, item, spider):
self.export.export_item(item)
return item
def close_spider(self,spider):
self.fp.close()
print('spider over.')
在 CrawlSpider 中需要注意的就是 spider 的寫法,別的和之前差不多。