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程序设计入门(2)变量类型简介
Jun 16 Python
Python命名空间详解
Aug 18 Python
Python+OpenCV人脸检测原理及示例详解
Oct 19 Python
python3+PyQt5重新实现QT事件处理程序
Apr 19 Python
Python使用pandas处理CSV文件的实例讲解
Jun 22 Python
基于pandas将类别属性转化为数值属性的方法
Jul 25 Python
python  创建一个保留重复值的列表的补码
Oct 15 Python
Python中一般处理中文的几种方法
Mar 06 Python
Django框架用户注销功能实现方法分析
May 28 Python
python框架flask入门之路由及简单实现方法
Jun 07 Python
用python给csv里的数据排序的具体代码
Jul 17 Python
一文带你了解Python 四种常见基础爬虫方法介绍
Dec 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
PHP+Javascript实现在线拍照功能实例
2015/07/18 PHP
[原创]CI(CodeIgniter)简单统计访问人数实现方法
2016/01/19 PHP
CodeIgniter表单验证方法实例详解
2016/03/03 PHP
PHP中抽象类和抽象方法概念与用法分析
2016/05/24 PHP
PHP针对伪静态的注入总结【附asp与Python相关代码】
2017/08/01 PHP
JQuery Ajax 跨域访问的解决方案
2010/03/12 Javascript
js获取html文件的思路及示例
2013/09/17 Javascript
IE8下String的Trim()方法失效的解决方法
2013/11/08 Javascript
Javascript高级技巧分享
2014/02/25 Javascript
js实现浏览本地文件并显示扩展名的方法
2015/08/17 Javascript
AngularJS基础 ng-csp 指令详解
2016/08/01 Javascript
js 判断数据类型的几种方法
2017/01/13 Javascript
使用Bootstrap打造特色进度条效果
2017/05/02 Javascript
JS对象与JSON互转换、New Function()、 forEach()、DOM事件流等js开发基础小结
2017/08/10 Javascript
Angularjs使用过滤器完成排序功能
2017/09/20 Javascript
vue通过滚动行为实现从列表到详情,返回列表原位置的方法
2018/08/31 Javascript
详解Vue 如何监听Array的变化
2019/06/06 Javascript
通过循环优化 JavaScript 程序
2019/06/24 Javascript
vue项目中在可编辑div光标位置插入内容的实现代码
2020/01/07 Javascript
vue实现简单跑马灯效果
2020/05/25 Javascript
[02:51]DOTA2英雄基础教程 风暴之灵
2013/12/23 DOTA
[07:52]2014DOTA2 TI逗比武士游V社解说背后的故事
2014/07/10 DOTA
[02:23]1个至宝=115个英雄特效 最“绿”至宝拉比克“魔导师密钥”登场
2018/12/29 DOTA
python支持多线程的爬虫实例
2019/12/21 Python
python dataframe NaN处理方式
2019/12/26 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
2020/12/04 Python
匡威西班牙官网:Converse西班牙
2019/10/01 全球购物
简单租房协议书范本
2014/08/20 职场文书
银行贷款委托书范本
2014/10/11 职场文书
个人整改措施落实情况汇报
2014/10/29 职场文书
教师群众路线教育实践活动学习笔记
2014/11/05 职场文书
合作意向协议书
2015/01/29 职场文书
学校捐书活动总结
2015/05/08 职场文书
研究生毕业登记表的自我鉴定范文
2019/07/15 职场文书
SQLServer RANK() 排名函数的使用
2022/03/23 SQL Server
python中urllib包的网络请求教程
2022/04/19 Python