Scrapy 通用爬虫

1.CrawlSpider

  • CrawlSpider是Scrapy提供的一个通用Spider。在Spider里,我们可以指定一些爬取规则来实现页面的提取,这些爬取规则有一个专门的数据结构Rule表示。Rule里面包含提取和跟进页面的配置,Spider会根据Rule来确定当前页面中的哪些链接需要继续爬取,哪些页面的爬取规则结果用哪个方法解析。
  • CrawlSpider继承自Spider类。它有一个非常重要的属性和方法。
    rules :爬取规则,是包含一个或多个Rule对象的列表,每个Rule对爬取网站的动作都做了定义,CrawlSpider会读取rules里面的每一个Rule并进行解析
    parse_start_url():是一个可以重写的方法,当strat_urls 里对应的Request得到Response的时候,该方法会被调用,它会分析Response并 必须返回Item对象或者Request对象。

Rule的定义和参数如下:

class scrapy.contrib.spider.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)

参数解释:

  • link_extractor: 是Link Extractor 对象。通过它可以知道从爬取的网页里提取哪些链接。提取出来的链接会自动生成Request。
    LxmlLinkExtractor(
    allow=(), deny=(), allow_domains=(),dent_domains=(), restrict_xpaths=(),restrict_css())
    -- 主要参数解释:
    -- allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
    -- deny:与allow 相反。
    -- allow_domains:会被提取的链接的domains,符合要求的域名的链接才会被跟进-生成新的Request。
    -- deny_domains:与allow_domains相反。
    -- restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。定义了从当前页面中Xpath匹配的区域提取链接。

  • callback:回调函数。每次从link_extractor中获取到链接时,该函数会被调用,返回一个包含Item或者Request对象的列表。(注意:避免使用parse()作为回调函数,由于CrawlSpiser使用parse()方法来实现其逻辑,如果parse()方法被覆盖,将运行失败)

  • cb_kwargs:字典,包含传递给回调函数的参数

  • follow:Trur or False . 表示是否跟进提取到的链接。如果callback为None 默认为True

  • process_links:指定出来函数,提取到链接时被调用,只要用于过滤链接

  • process_request:该Rule提取到Request时被调用,对Request进行处理。该函数必须返回Request或者None。

2.Item Loader

Rule定义了页面的爬取逻辑,但是没有对Item的提取方式做规则定义,这就需要借助Item Loader来实现。

class scrapy.loader.ItemLoader([item, selector, response,] **kwargs)

参数解释:
item: 它是一个Item对象,可以调用add_xpath(),add_css(),add_value() 等方法来填充Item。
selector: Selector 对象,用来提取填充数据的选择器
response: Response对象,用于使用构造选择器的Response
下面是常规提取方式与Item Loader 配置化提取方式的对比:
常规提取方式:

# 常规处理手段
        item = ChinanewsItem()
        item['title'] = response.xpath("//h1[@id='chan_newsTitle']/text()").extract_first()
        item['url'] = response.url
        item['text'] = "".join(response.xpath("//div[@id='chan_newsDetail']//text()").extract()).strip()
        item['datetime'] = response.xpath("//div[@id='chan_newsInfo']/text()").re_first('\d+-\d+-\d+\s\d+:\d+:\d+')
        item['source'] = response.xpath("//div[@id='chan_newsInfo']/text()").re_first('来源:(.*)')
        item['website'] = '中华网'
        yield item

配置化提取方式:

# 配置化提取方法
        loader = ChinaLoader(item=ChinanewsItem(),response=response)
        loader.add_xpath('title',"//h1[@id='chan_newsTitle']/text()")
        loader.add_value('url',response.url)
        loader.add_xpath('text',"//div[@id='chan_newsDetail']//text()")
        loader.add_xpath('datetime',"//div[@id='chan_newsInfo']/text()",re='\d+-\d+-\d+\s\d+:\d+:\d+')
        loader.add_xpath('source',"//div[@id='chan_newsInfo']/text()",re='来源:(.*)')
        loader.add_value('website','中华网')
        yield loader.load_item()

首先声明一个ItemLoader对象,用Item对象和Response对象实例化这个ItemLoader,ChinanewsItem()对象为items.py 里的Item对象,这里定义了Item字段。


item.py

ChinaLoder()对象为loader.py 里定义的类对象。ChinaLoader继承了NewsLoader,NewsLoader继承了ItemLoader,所以,这个类为ItemLoader的子类。其实现如下图所示。


loader.py

然后把根据xpath匹配出来的数据分配给title,url,text,datetime,source,website属性,即用不同方法给属性赋值。最后调用loader_item()方法实现Item解析。这个方式比较规则化,我们可以把一些参数和规则单独提取出来做成配置文件,即可实现可配置化。
另外,ItemLoader每个字段都包含一个Input Processor 和 Output Processor。Input Processor收到数据时立刻提取数据,Input Processor的结果被收集起来并保存在ItemLoader内,但是不分配给Item。收集到所有数据之后,Output Processor被调用来处理收集到的数据,然后load_item()方法再被调用来填充生成Item对象,存入Item中。这样就生成了Item。

内置的Processor:
  • Identity
    不进行任何处理,直接返回原来的数据
  • TakeFirst
    返回列表里的第一个非空值。与extract_first()类似。常用作 Output Processor
  • Join
    与字符串的join()方法类似,把列表拼和成字符串
  • Compose
    是用给定的多个函数组合而成的Processor,每个输入值被传递到第一个函数,其输出值被传递到第二个函数,以此类推,直到最后一个函数返回整个处理器的输出。
  • MapCompose
    迭代处理一个列表输入值,被处理的是一个可迭代对象,MapCompose会将该对象遍历然后依次处理。
  • SelectJmes
    需要先安装 jmespath库才可以:
    pip3 install jmespath
    作用是查询Json,传入Key,输出Value

3. 例子:

scrapy startproject chinaNews
scrapy genspider -t crawl china tech.china.com
初始spider/china.py:

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


class ChinaSpider(CrawlSpider):
    name = 'china'
    allowed_domains = ['tech.china.com']
    start_urls = ['http://tech.china.com/']

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

    def parse_item(self, response):
        item = {}
        #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
        #item['name'] = response.xpath('//div[@id="name"]').get()
        #item['description'] = response.xpath('//div[@id="description"]').get()
        return item

Spider内容多了rules的定义,解析方法的名称为parse_item()

3.1 定义Rule

修改start_urls为起始链接。
之后,Spider爬取start_urls里的每一个链接。得到Response之后,Spider就会根据每一个Rule来提取这个页面的超链接,去生成Request。

Rule首先定义一个新闻详情页的提取规则,使用restrct_xpaths()限制提取的域,并且指定回调函数为parse_item(),不跟进处理。
接着定义翻页的提取规则,不同页的链接只有index后面的数字不一样,所以使用响应的正则表达式匹配出页面链接,没有回调函数并且继续跟进。


image.png

3.2 定义Item(图items.py)

3.3 定义ItemLoader(图loader.py)

3.4 定义解析方法(配置化提取)

3.5 定义pipelines

3.6 定义middlewares

3.7 定义settings

china.py:
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
# 导入Item
from chinaNews.items import ChinanewsItem
# 导入ItemLoader
from chinaNews.loaders import ChinaLoader

class ChinaSpider(CrawlSpider):
    name = 'china'
    allowed_domains = ['tech.china.com']
    start_urls = ['https://tech.china.com/articles/index.html']

    rules = (
        Rule(LinkExtractor(allow=(r'article/.*.html'),restrict_xpaths='//div[@class="item_con"]'), callback='parse_item', follow=False),
        Rule(LinkExtractor(allow=(r"articles/index_\d+.html"),restrict_xpaths='//div[@class="pages"]'),follow=True)
    )

    def parse_item(self, response):
        """
        # 常规处理手段
        item = ChinanewsItem()
        item['title'] = response.xpath("//h1[@id='chan_newsTitle']/text()").extract_first()
        item['url'] = response.url
        item['text'] = "".join(response.xpath("//div[@id='chan_newsDetail']//text()").extract()).strip()
        item['datetime'] = response.xpath("//div[@id='chan_newsInfo']/text()").re_first('\d+-\d+-\d+\s\d+:\d+:\d+')
        item['source'] = response.xpath("//div[@id='chan_newsInfo']/text()").re_first('来源:(.*)')
        item['website'] = '中华网'
        yield item
        """
        # 配置化提取方法
        loader = ChinaLoader(item=ChinanewsItem(),response=response)
        loader.add_xpath('title',"//h1[@id='chan_newsTitle']/text()")
        loader.add_value('url',response.url)
        loader.add_xpath('text',"//div[@id='chan_newsDetail']//text()")
        loader.add_xpath('datetime',"//div[@id='chan_newsInfo']/text()",re='\d+-\d+-\d+\s\d+:\d+:\d+')
        loader.add_xpath('source',"//div[@id='chan_newsInfo']/text()",re='来源:(.*)')
        loader.add_value('website','中华网')

        yield loader.load_item()
结构.png

以上实现爬虫的半通用化配置。
全通用化配置还需要实现通用配置抽取(变量、属性抽取)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容