1. scrapy.CrawlSpider
scrapy框架提供了多种类型的spider,大致分为两类,一类为基本spider(scrapy.Spider),另一类为通用spider(scrapy.spiders.CrawlSpider、scrapy.spiders.XMLFeedSpider、scrapy.spiders.CSVFeedSpider、scrapy.spiders.SitemapSpider),并且值得注意的是,通用spider类型,均继承scrapy.Spider,那么我们在使用通用spider来抓取网页的时候,其解析方法名不能是parse(那样就重写了其父类Spider的parse方法,造成冲突)。
为什么可以使用CrawlSpider进行全站抓取呢? 那么可以想像一下, 我们如果不使用它如何实现全站抓取, 首先我们需要解析一个网站的首页, 解析出其所有的资源链接(ajax方式或绑定dom事件实现跳转忽略),请求该页面所有的资源链接, 再在资源链接下递归地查找子页的资源链接,最后在我们需要的资源详情页结构化数据并持久化在文件中。这里只是简单的介绍一下全站抓取的大致思路,事实上,其细节的实现,流程的控制是很复杂的,所以scrapy封装了一个CrawlSpider,使得数据的爬取变得简单高效,因为它通过定义一组规则为跟踪链接提供了便利的机制。它可能不是最适合您的特定网站或项目,但它在几种情况下足够通用,因此您可以从它开始并根据需要覆盖它以获得更多自定义功能。
2. 重要属性
A . rules:这是一个(或多个)Rule
对象的列表。每个都Rule
定义了爬网站点的特定行为。规则对象如下所述。如果多个规则匹配相同的链接,则将根据它们在此属性中定义的顺序使用第一个规则。
补充:class scrapy.spiders.
Rule
(link_extractor,callback = None,cb_kwargs = None,follow = None,process_links = None,process_request = None )
1.link_extractor
是一个Link Extractor对象,它定义如何从每个已爬网页面中提取链接,可以是正则表达式,用于在跟进的时候筛选url(*重要*)。
2.callback
是一个可调用的或一个字符串(在这种情况下,将使用来自具有该名称的spider对象的方法)为使用指定的link_extractor提取的每个链接调用。此回调接收响应作为其第一个参数,并且必须返回包含Item
和/或 Request
对象(或其任何子类)的列表(*重要*)。
3.cb_kwargs
是一个包含要传递给回调函数的关键字参数的dict。
4.follow
是一个布尔值,指定是否应该从使用此规则提取的每个响应中跟踪链接。如果callback
是follow
默认值True
,则默认为False(*重要*)
。
5.process_links
是一个可调用的,或一个字符串(在这种情况下,将使用来自具有该名称的spider对象的方法),将使用指定的每个响应从每个响应中提取的每个链接列表调用该方法link_extractor
。这主要用于过滤目的。
6.process_request
是一个可调用的,或一个字符串(在这种情况下,将使用来自具有该名称的spider对象的方法),该方法将在此规则提取的每个请求中调用,并且必须返回请求或None(以过滤掉请求)
B . ...................(Spider的allowed_domains、start_urls等
)
3. 实战需求
"""
爬取微信小程序社区所有教程(http://www.wxapp-union.com/portal.php?mod=list&catid=2),并以json格式存储在文件中
"""
4. 实现
补充:新建CrawlSpider模板的爬虫的命令 scrapy genspider -t crawl wxappcrawl wxapp-union.com
settings.py
# Obey robots.txt rules
ROBOTSTXT_OBEY = False # Override the default request headers:
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36', } DOWNLOAD_DELAY = 3 # Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'wxapp.pipelines.WxappPipeline': 300,
}
wxappcrawl .py
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from .. import items class WxappcrawlSpider(CrawlSpider):
name = 'wxappcrawl'
allowed_domains = ['wxapp-union.com']
start_urls = ['http://www.wxapp-union.com/portal.php?mod=list&catid=2&page=1'] '''
allow设置之后, 会在执行其父类(scrapy.Spider)的parse方法的时候, 提取response中符合这一正则的所有
url, 并添加到调度器下的请求队列(无重复)中
'''
rules = (
# 爬取每一页
Rule(LinkExtractor(allow=r'.+?mod=list&catid=2&page=\d'), follow=True),
# 爬取具体的文章页
Rule(LinkExtractor(allow=r'.+/article-\d+-\d+\.html'), callback='parse_article', follow=False),
) def parse_article(self, response):
'''
解析文章页,并返回实体
'''
article_title = response.xpath("//h1[@class='ph']/text()").get().strip()
article_author = response.xpath("//p[@class='authors']/a/text()").get().strip()
article_ctime = response.xpath("//p[@class='authors']/span[@class='time']/text()").get()
article_content_list = response.xpath("//td[@id='article_content']/*/text()").getall()
article_content = ''.join(article_content_list) yield items.WxappItem(
title = article_title,
author = article_author,
ctime = article_ctime,
content = article_content
)
items.py
# -*- coding: utf-8 -*- # Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html import scrapy class WxappItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field() title = scrapy.Field()
author = scrapy.Field()
ctime = scrapy.Field()
content = scrapy.Field()
pipelines.py
# -*- coding: utf-8 -*- # Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html from scrapy.exporters import JsonLinesItemExporter class WxappPipeline(object): def __init__(self):
self.file = open('./wxapp.json', 'wb')
self.exporter = JsonLinesItemExporter(file=self.file, ensure_ascii=False, encoding='utf-8') def open_spider(self, spider):
pass def process_item(self, item, spider):
self.exporter.export_item(item)
return item def close_spider(self, spider):
self.file.close()
run.py (在项目根目录下(与scrapy.cfg同级)新建启动爬虫的py文件)
from scrapy import cmdline cmdline.execute("scrapy crawl wxappcrawl".split()) # cmdline.execute(['scrapy', 'crawl', 'wxappcrawl'])
哈哈·,收工!