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实现监控程序执行时间并将其写入日志的方法
Jun 30 Python
详解Django框架中用context来解析模板的方法
Jul 20 Python
python用post访问restful服务接口的方法
Dec 07 Python
详解用Python练习画个美队盾牌
Mar 23 Python
python实现抽奖小程序
Apr 15 Python
Python搭建Spark分布式集群环境
Jul 05 Python
python实现输入任意一个大写字母生成金字塔的示例
Oct 27 Python
python——全排列数的生成方式
Feb 26 Python
windows上彻底删除jupyter notebook的实现
Apr 13 Python
python的链表基础知识点
Sep 13 Python
分享PyCharm最新激活码(真永久激活方法)不用每月找安装参数或最新激活码了
Dec 27 Python
详解Python+OpenCV绘制灰度直方图
Mar 22 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
PHP调用Twitter的RSS的实现代码
2010/03/10 PHP
php检索或者复制远程文件的方法
2015/03/13 PHP
PHP使用正则表达式实现过滤非法字符串功能示例
2018/06/04 PHP
javascript 进阶篇2 CSS XML学习
2012/03/14 Javascript
javascript实现点击后变换按钮显示文字的方法
2015/05/13 Javascript
Nodejs的express使用教程
2015/11/23 NodeJs
jQuery实现简单的DIV拖动效果
2016/02/19 Javascript
微信小程序-图片、录音、音频播放、音乐播放、视频、文件代码实例
2016/11/22 Javascript
js鼠标跟随运动效果
2017/03/11 Javascript
webpack下实现动态引入文件方法
2018/02/22 Javascript
Vue 中使用vue2-highcharts实现top功能的示例
2018/03/05 Javascript
Vue.js的动态组件模板的实现
2018/11/26 Javascript
利用Vue构造器创建Form组件的通用解决方法
2018/12/03 Javascript
使用taro开发微信小程序遇到的坑总结
2019/04/08 Javascript
node.js中Buffer缓冲器的原理与使用方法分析
2019/11/23 Javascript
javascript实现贪吃蛇小练习
2020/07/05 Javascript
python处理json数据中的中文
2014/03/06 Python
python 实时遍历日志文件
2016/04/12 Python
基于python中theano库的线性回归
2018/08/31 Python
python爬取网易云音乐评论
2018/11/16 Python
Django之无名分组和有名分组的实现
2019/04/16 Python
解决pyqt5中QToolButton无法使用的问题
2019/06/21 Python
pycharm中显示CSS提示的知识点总结
2019/07/29 Python
基于K.image_data_format() == 'channels_first' 的理解
2020/06/29 Python
html5唤起app的方法
2017/11/30 HTML / CSS
美国校园市场:OCM
2017/06/08 全球购物
Feelunique德国官方网站:欧洲最大的在线美容零售商
2019/07/20 全球购物
简述DNS进行域名解析的过程
2013/12/02 面试题
大一学生职业生涯规划
2014/03/11 职场文书
乡镇领导干部个人对照检查材料思想汇报
2014/09/23 职场文书
2014年公务员转正工作总结
2014/11/07 职场文书
2015医德医风个人工作总结
2015/04/02 职场文书
小型企业的绩效考核制度模板
2019/11/21 职场文书
MySQL中的布尔值,怎么存储false或true
2021/06/04 MySQL
浅析Redis Sentinel 与 Redis Cluster
2021/06/24 Redis
Win11 引入 Windows 365 云操作系统,适应疫情期间混合办公模式:启动时直接登录、模
2022/04/06 数码科技