Scrapy框架CrawlSpiders的介绍以及使用详解


Posted in Python onNovember 29, 2017

在Scrapy基础——Spider中,我简要地说了一下Spider类。Spider基本上能做很多事情了,但是如果你想爬取知乎或者是简书全站的话,你可能需要一个更强大的武器。CrawlSpider基于Spider,但是可以说是为全站爬取而生。

CrawlSpiders是Spider的派生类,Spider类的设计原则是只爬取start_url列表中的网页,而CrawlSpider类定义了一些规则(rule)来提供跟进link的方便的机制,从爬取的网页中获取link并继续爬取的工作更适合。

一、我们先来分析一下CrawlSpiders源码

源码解析

class CrawlSpider(Spider):
  rules = ()
  def __init__(self, *a, **kw):
    super(CrawlSpider, self).__init__(*a, **kw)
    self._compile_rules()

  # 首先调用parse()来处理start_urls中返回的response对象
  # parse()则将这些response对象传递给了_parse_response()函数处理,并设置回调函数为parse_start_url()
  # 设置了跟进标志位True
  # parse将返回item和跟进了的Request对象  
  def parse(self, response):
    return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)

  # 处理start_url中返回的response,需要重写
  def parse_start_url(self, response):
    return []

  def process_results(self, response, results):
    return results

  # 从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回
  def _requests_to_follow(self, response):
    if not isinstance(response, HtmlResponse):
      return
    seen = set()
    # 抽取之内的所有链接,只要通过任意一个'规则',即表示合法
    for n, rule in enumerate(self._rules):
      links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
      # 使用用户指定的process_links处理每个连接
      if links and rule.process_links:
        links = rule.process_links(links)
      # 将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded()
      for link in links:
        seen.add(link)
        # 构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数
        r = Request(url=link.url, callback=self._response_downloaded)
        r.meta.update(rule=n, link_text=link.text)
        # 对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request.
        yield rule.process_request(r)

  # 处理通过rule提取出的连接,并返回item以及request
  def _response_downloaded(self, response):
    rule = self._rules[response.meta['rule']]
    return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)

  # 解析response对象,会用callback解析处理他,并返回request或Item对象
  def _parse_response(self, response, callback, cb_kwargs, follow=True):
    # 首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数)
    # 如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象,
    # 然后再交给process_results处理。返回cb_res的一个列表
    if callback:
      #如果是parse调用的,则会解析成Request对象
      #如果是rule callback,则会解析成Item
      cb_res = callback(response, **cb_kwargs) or ()
      cb_res = self.process_results(response, cb_res)
      for requests_or_item in iterate_spider_output(cb_res):
        yield requests_or_item

    # 如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
    if follow and self._follow_links:
      #返回每个Request对象
      for request_or_item in self._requests_to_follow(response):
        yield request_or_item

  def _compile_rules(self):
    def get_method(method):
      if callable(method):
        return method
      elif isinstance(method, basestring):
        return getattr(self, method, None)

    self._rules = [copy.copy(r) for r in self.rules]
    for rule in self._rules:
      rule.callback = get_method(rule.callback)
      rule.process_links = get_method(rule.process_links)
      rule.process_request = get_method(rule.process_request)

  def set_crawler(self, crawler):
    super(CrawlSpider, self).set_crawler(crawler)
    self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)

二、 CrawlSpider爬虫文件字段的介绍

1、 CrawlSpider继承于Spider类,除了继承过来的属性外(name、allow_domains),还提供了新的属性和方法:class scrapy.linkextractors.LinkExtractorLink Extractors 的目的很简单: 提取链接。每个LinkExtractor有唯一的公共方法是 extract_links(),它接收一个 Response 对象,并返回一个 scrapy.link.Link 对象。

Link Extractors要实例化一次,并且 extract_links 方法会根据不同的 response 调用多次提取链接。

class scrapy.linkextractors.LinkExtractor(
  allow = (),
  deny = (),
  allow_domains = (),
  deny_domains = (),
  deny_extensions = None,
  restrict_xpaths = (),
  tags = ('a','area'),
  attrs = ('href'),
  canonicalize = True,
  unique = True,
  process_value = None
)

主要参数:

① allow:满足括号中“正则表达式”的值会被提取,如果为空,则全部匹配。
② deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取。
③ allow_domains:会被提取的链接的domains。
④ deny_domains:一定不会被提取链接的domains。
⑤ restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。

2、 在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了特定操作。如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用。

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

① link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。

② callback: 从link_extractor中每获取到链接时,参数所指定的值作为回调函数,该回调函数接受一个response作为其第一个参数。

注意:当编写爬虫规则时,避免使用parse作为回调函数。由于CrawlSpider使用parse方法来实现其逻辑,如果覆盖了 parse方法,crawl spider将会运行失败。

③ follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。 如果callback为None,follow 默认设置为True ,否则默认为False。

④ process_links:指定该spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。

⑤ process_request:指定该spider中哪个的函数将会被调用, 该规则提取到每个request时都会调用该函数。 (用来过滤request)

3、Scrapy提供了log功能,可以通过 logging 模块使用。可以修改配置文件settings.py,任意位置添加下面两行,效果会清爽很多。

LOG_FILE = "TencentSpider.log"
LOG_LEVEL = "INFO"

Scrapy提供5层logging级别:

① CRITICAL - 严重错误(critical)
② ERROR - 一般错误(regular errors)
③ WARNING - 警告信息(warning messages)
④ INFO - 一般信息(informational messages)
⑤ DEBUG - 调试信息(debugging messages)

通过在setting.py中进行以下设置可以被用来配置logging:

① LOG_ENABLED 默认: True,启用logging
② LOG_ENCODING 默认: 'utf-8',logging使用的编码
③ LOG_FILE 默认: None,在当前目录里创建logging输出文件的文件名
④ LOG_LEVEL 默认: 'DEBUG',log的最低级别
⑤ LOG_STDOUT 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中。例如,执行 print "hello" ,其将会在Scrapy log中显示。

三、 CrawlSpider爬虫案例分析

1、创建项目:scrapy startproject CrawlYouYuan

2、创建爬虫文件:scrapy genspider -t crawl youyuan youyuan.com

3、项目文件分析

items.py

模型类
import scrapy
class CrawlyouyuanItem(scrapy.Item):
  # 用户名
  username = scrapy.Field()
  # 年龄
  age = scrapy.Field()
  # 头像图片的链接
  header_url = scrapy.Field()
  # 相册图片的链接
  images_url = scrapy.Field()
  # 内心独白
  content = scrapy.Field()
  # 籍贯
  place_from = scrapy.Field()
  # 学历
  education = scrapy.Field()
  # 兴趣爱好
  hobby = scrapy.Field()
  # 个人主页
  source_url = scrapy.Field()
  # 数据来源网站
  sourec = scrapy.Field()
  # utc 时间
  time = scrapy.Field()
  # 爬虫名
  spidername = scrapy.Field()

youyuan.py

爬虫文件
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from CrawlYouYuan.items import CrawlyouyuanItem
import re
class YouyuanSpider(CrawlSpider):
  name = 'youyuan'
  allowed_domains = ['youyuan.com']
  start_urls = ['http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/']
  # 自动生成的文件不需要改东西,只需要添加rules文件里面Rule角色就可以
  # 每一页匹配规则
  page_links = LinkExtractor(allow=(r"youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p\d+/"))
  # 每个人个人主页匹配规则
  profile_links = LinkExtractor(allow=(r"youyuan.com/\d+-profile/"))
  rules = (
    # 没有回调函数,说明follow是True
    Rule(page_links),
    # 有回调函数,说明follow是False
    Rule(profile_links, callback='parse_item', follow=True),
  )

  def parse_item(self, response):
    item = CrawlyouyuanItem()

    item['username'] = self.get_username(response)
    # 年龄
    item['age'] = self.get_age(response)
    # 头像图片的链接
    item['header_url'] = self.get_header_url(response)
    # 相册图片的链接
    item['images_url'] = self.get_images_url(response)
    # 内心独白
    item['content'] = self.get_content(response)
    # 籍贯
    item['place_from'] = self.get_place_from(response)
    # 学历
    item['education'] = self.get_education(response)
    # 兴趣爱好
    item['hobby'] = self.get_hobby(response)
    # 个人主页
    item['source_url'] = response.url
    # 数据来源网站
    item['sourec'] = "youyuan"

    yield item

  def get_username(self, response):
    username = response.xpath("//dl[@class='personal_cen']//div[@class='main']/strong/text()").extract()
    if len(username):
      username = username[0]
    else:
      username = "NULL"
    return username.strip()

  def get_age(self, response):
    age = response.xpath("//dl[@class='personal_cen']//dd/p/text()").extract()
    if len(age):
      age = re.findall(u"\d+岁", age[0])[0]
    else:
      age = "NULL"
    return age.strip()

  def get_header_url(self, response):
    header_url = response.xpath("//dl[@class='personal_cen']/dt/img/@src").extract()
    if len(header_url):
      header_url = header_url[0]
    else:
      header_url = "NULL"
    return header_url.strip()

  def get_images_url(self, response):
    images_url = response.xpath("//div[@class='ph_show']/ul/li/a/img/@src").extract()
    if len(images_url):
      images_url = ", ".join(images_url)
    else:
      images_url = "NULL"
    return images_url

  def get_content(self, response):
    content = response.xpath("//div[@class='pre_data']/ul/li/p/text()").extract()
    if len(content):
      content = content[0]
    else:
      content = "NULL"
    return content.strip()

  def get_place_from(self, response):
    place_from = response.xpath("//div[@class='pre_data']/ul/li[2]//ol[1]/li[1]/span/text()").extract()
    if len(place_from):
      place_from = place_from[0]
    else:
      place_from = "NULL"
    return place_from.strip()

  def get_education(self, response):
    education = response.xpath("//div[@class='pre_data']/ul/li[3]//ol[2]/li[2]/span/text()").extract()
    if len(education):
      education = education[0]
    else:
      education = "NULL"
    return education.strip()

  def get_hobby(self, response):
    hobby = response.xpath("//dl[@class='personal_cen']//ol/li/text()").extract()
    if len(hobby):
      hobby = ",".join(hobby).replace(" ", "")
    else:
      hobby = "NULL"
    return hobby.strip()

pipelines.py

管道文件
import json
import codecs
class CrawlyouyuanPipeline(object):

  def __init__(self):
    self.filename = codecs.open('content.json', 'w', encoding='utf-8')

  def process_item(self, item, spider):
    html = json.dumps(dict(item), ensure_ascii=False)
    self.filename.write(html + '\n')
    return item

  def spider_closed(self, spider):
    self.filename.close()

settings.py

BOT_NAME = 'CrawlYouYuan'
SPIDER_MODULES = ['CrawlYouYuan.spiders']
NEWSPIDER_MODULE = 'CrawlYouYuan.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0)'
# Obey robots.txt rules
ROBOTSTXT_OBEY = True
ITEM_PIPELINES = {
  'CrawlYouYuan.pipelines.CrawlyouyuanPipeline': 300,
}

begin.py

from scrapy import cmdline
cmdline.execute('scrapy crawl youyuan'.split())

在运行程序之前需要使Scrapy版本和Twisted版本相吻合,设置如下

Scrapy框架CrawlSpiders的介绍以及使用详解

这次分享详细介绍了使用Scrapy框架爬虫的具体步骤,并同时编写爬虫案例进行分析,很好的诠释了Scrapy框架爬取数据的方便性和易懂性,下篇文章我会分享下Scrapy分布式爬取网站,让我们一起学习,一起探讨爬虫技术。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现带声音的摩斯码翻译实现方法
May 20 Python
Python Web框架Tornado运行和部署
Oct 19 Python
Linux 发邮件磁盘空间监控(python)
Apr 23 Python
python安装numpy&安装matplotlib& scipy的教程
Nov 02 Python
详解利用Python scipy.signal.filtfilt() 实现信号滤波
Jun 05 Python
快速解决vue.js 模板和jinja 模板冲突的问题
Jul 26 Python
简单了解python 生成器 列表推导式 生成器表达式
Aug 22 Python
python3 map函数和filter函数详解
Aug 26 Python
python动态视频下载器的实现方法
Sep 16 Python
Python程序暂停的正常处理方法
Nov 07 Python
Python获取、格式化当前时间日期的方法
Feb 10 Python
详解Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
Aug 04 Python
pycharm下打开、执行并调试scrapy爬虫程序的方法
Nov 29 #Python
Python快速排序算法实例分析
Nov 29 #Python
Python3学习urllib的使用方法示例
Nov 29 #Python
Python实现的选择排序算法示例
Nov 29 #Python
Python实现的桶排序算法示例
Nov 29 #Python
[原创]教女朋友学Python(一)运行环境搭建
Nov 29 #Python
对变量赋值的理解--Pyton中让两个值互换的实现方法
Nov 29 #Python
You might like
ThinkPHP自动填充实现无限级分类的方法
2014/08/22 PHP
PHP与MYSQL中UTF8 中文排序示例代码
2014/10/23 PHP
CodeIgniter框架钩子机制实现方法【hooks类】
2018/08/21 PHP
一段好玩的JavaScript代码
2006/12/01 Javascript
js 面向对象的技术创建高级 Web 应用程序
2010/02/25 Javascript
jquery中获取id值方法小结
2013/09/22 Javascript
js Date概念详细介绍
2013/11/22 Javascript
nodejs爬虫抓取数据之编码问题
2015/07/03 NodeJs
基于jquery实现左右按钮点击的图片切换效果
2021/01/27 Javascript
jQuery使用serialize()表单序列化时出现中文乱码问题的解决办法
2016/07/27 Javascript
JS实现的表格行上下移动操作示例
2016/08/03 Javascript
Vue.js路由组件vue-router使用方法详解
2016/12/02 Javascript
Webpack常见静态资源处理-模块加载器(Loaders)+ExtractTextPlugin插件
2017/06/29 Javascript
AngularJS实用基础知识_入门必备篇(推荐)
2017/07/10 Javascript
ionic grid(栅格)九宫格制作详解
2018/06/30 Javascript
JS温故而知新之变量提升和时间死区
2019/01/27 Javascript
python访问系统环境变量的方法
2015/04/29 Python
解析Python编程中的包结构
2015/10/25 Python
python实现简易学生信息管理系统
2020/04/05 Python
Python 进程操作之进程间通过队列共享数据,队列Queue简单示例
2019/10/11 Python
Python3 Click模块的使用方法详解
2020/02/12 Python
Python更换pip源方法过程解析
2020/05/19 Python
阿迪达斯印尼官方网站:adidas印尼
2020/02/10 全球购物
限量版运动鞋和街头服饰:TheDrop
2020/09/06 全球购物
枚举和一组预处理的#define有什么不同
2016/09/21 面试题
办公室驾驶员岗位职责
2013/11/15 职场文书
超市5.1促销活动
2014/01/15 职场文书
社区清明节活动总结
2014/07/04 职场文书
书法兴趣小组活动总结
2014/07/07 职场文书
2015学生会文艺部工作总结
2015/04/03 职场文书
2015年乡镇残联工作总结
2015/05/13 职场文书
忠犬八公的故事观后感
2015/06/05 职场文书
2016干部作风整顿心得体会
2016/01/22 职场文书
幽默导游词应该怎么写?
2019/08/26 职场文书
Python借助with语句实现代码段只执行有限次
2022/03/23 Python
JS中forEach()、map()、every()、some()和filter()的用法
2022/05/11 Javascript