总字符数: 21.88K

代码: 18.29K, 文本: 1.74K

预计阅读时间: 1.45 小时

scrapy的基本使用

  • 创建一个工程 scrapy startproject filename
  • 必须在spiders这个目录下创建一个爬虫文件
    • cd proName
    • scrapy genspider spiderName www.xxx.com
  • 执行工程:scrapy crawl spiderName

settings.py

  • 不遵从rebots协议
  • ROBOTSTXT_OBEY = False
  • 进行UA伪装
  • USER_AGENT = 'UA'
  • 进行日志等级设定
    • LOG_LEVEL = 'ERROR'

scrapy解析

1
2
3
4
5
6
7
8
9
10
11
12
13
def parse(self, response):
# 解析:作者的名称+段子的内容
div_list = response.xpath('//div[@class="col1 old-style-col1"]/div')
for div in div_list:
# xpath返回的是列表, 但是列表元素一定是Selector类型的对象想要取得内容可以直接.extract()
# .extract可以将Selector对象中的data参数存储的字符串提取出来
# 只要能保证返回的列表里只有一个内容就可以使用.extract_first否则还是使用[下标]取出
# .extract_first将列表中的第0个取出
author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
# 列表调用了.extract之后, 则表示将列表中每一个Selector对象中的data对应的字符串提取了出来
content = div.xpath('./a[1]/div/span//text()').extract()
content = ''.join(content) # 将列表转换为字符串
print(author, content)

scrapy持久化存储

  • 基于终端指令:

    • 要求:只可以将parse方法的返回值存储到本地的文本文件中
    • scrapy crawl qiubai -o ./qiubai.csv
    • 注意:持久化存储对应的文本文件类型只可以为:'json','jsonlines','jl','csv','xml','marshal','pickle'
    • 好处:简洁高效便捷
    • 缺点:局限性较强(数据值可以存储到指定后缀名文件中)
  • 基于管道

    • 编码流程:

    • 数据解析

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      #qiubai.py 文件名

      import scrapy from qiubaiPro.items import QiubaiproItem#导入item类
      class QiubaiSpider(scrapy.Spider):
      name = 'qiubai'
      # allowed_domains = ['www.xxx.com']
      start_urls = ['https://www.qiushibaike.com/text/']

      def parse(self, response):
      # 解析:作者的名称+段子的内容
      div_list = response.xpath('//div[@class="col1 old-style-col1"]/div')
      for div in div_list:
      # xpath返回的是列表, 但是列表元素一定是Selector类型的对象想要取得内容可以直接.extract()
      # .extract可以将Selector对象中的data参数存储的字符串提取出来
      # 只要能保证返回的列表里只有一个内容就可以使用.extract_first否则还是使用[下标]取出
      # .extract_first将列表中的第0个取出
      # 匿名用户和正常用户的用户名不在同一个div中所以使用管道符分割,来写2个xpath表达式
      author = div.xpath('./div[1]/a[2]/h2/text() ./div[1]/span[2]/h2/text()').extract_first()
      # 列表调用了.extract之后, 则表示将列表中每一个Selector对象中的data对应的字符串提取了出来
      content = div.xpath('./a[1]/div/span//text()').extract()
      content = ''.join(content) # 将列表转换为字符串
    • item类中定义相关的属性

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      #items.py 文件名

      # Define here the models for your scraped items
      #
      # See documentation in:
      # https://docs.scrapy.org/en/latest/topics/items.html

      import scrapy


      class QiubaiproItem(scrapy.Item):
      # define the fields for your item here like:
      author = scrapy.Field()#定义author属性
      content = scrapy.Field()#定义content属性
    • 将解析的数据封装存储到item类型的对象中

      1
      2
      3
      4
      #qiubai.py 文件名
      item = QiubaiproItem()#实例化对象
      item['author'] = author#将author存储到item对象中
      item['content'] = content#将content存储到item对象中
    • item类型的对象提交给管道进行持久化存储的操作

      1
      2
      # 将item提交给了管道
      yield item
    • 在管道类的process_item中要将其接收到的item对象中存储的数据进行持久化存储操作

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      #pipelines.py 文件名

      # Define your item pipelines here
      #
      # Don't forget to add your pipeline to the ITEM_PIPELINES setting
      # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


      # useful for handling different item types with a single interface
      from itemadapter import ItemAdapter


      class QiubaiproPipeline:
      # 专门用来处理item类型对象
      # 该方法可以接收爬虫文件提交过来的item对象
      # 该方法每接收到一个item就会被调用一次
      fp = None
      # 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次

      def open_spider(self, spider):
      print('打开文件')
      self.fp = open('./qiubai.txt', 'w', encoding='utf-8')
      pass

      def process_item(self, item, spider):
      author = item['author']
      content = item['content']
      self.fp.write(author+':'+content+'\n')
      return item

      def close_spider(self, spider):
      print('关闭文件')
      self.fp.close()
    • 在配置文件中开启管道好处:

      1
      2
      3
      4
      5
      #setting.py 文件名
      #将以下内容取消注释,300代表优先级,数字越小优先级越高,一个键值对应一个管道类
      ITEM_PIPELINES = {
      'qiubaiPro.pipelines.QiubaiproPipeline': 300,
      }
    • 优点:

      • 通用性强
    • 缺点:

      • 操作繁琐
  • 测试题

    • 将爬取到的数据一份存储到本地一份存储到数据库,如何实现?
    1
    2
    3
    4
    5
    6
    #settings.py 文件名

    ITEM_PIPELINES = {
    'qiubaiPro.pipelines.QiubaiproPipeline': 300,
    'qiubaiPro.pipelines.mysqlPileLine': 301,
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #pipelines.py 文件名

    # Define your item pipelines here
    #
    # Don't forget to add your pipeline to the ITEM_PIPELINES setting
    # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


    # useful for handling different item types with a single interface
    from itemadapter import ItemAdapter
    import pymysql


    class QiubaiproPipeline:
    # 专门用来处理item类型对象
    # 该方法可以接收爬虫文件提交过来的item对象
    # 该方法每接收到一个item就会被调用一次
    fp = None
    # 重写父类的一个方法:该方法只在开始爬虫的时候被调用一次

    def open_spider(self, spider):
    print('打开文件')
    self.fp = open('./qiubai.txt', 'w', encoding='utf-8')
    pass

    def process_item(self, item, spider):
    author = item['author']
    content = item['content']
    self.fp.write(author+':'+content+'\n')
    return item # return item就会传递给下一个即将被执行的管道类

    def close_spider(self, spider):
    print('关闭文件')
    self.fp.close()
    # 管道文件中一个管道类对应将一组数据存储到一个平台或者载体中


    class mysqlPileLine:
    conn = None
    cursor = None

    def open_spider(self, spider):
    print('正在连接数据库')
    self.conn = pymysql.Connect(
    host='127.0.0.1', port=3306, user='root', password='123456789', db='qiubai', charset="utf8")

    def process_item(self, item, spider):
    print('正在写入数据')
    self.cursor = self.conn.cursor()
    try:
    self.cursor.execute('insert into qiubai values("%s","%s")' %
    (item["author"], item["content"]))
    self.conn.commit()
    except Exception as e:
    print(e)
    self.conn.rollback
    return item

    def close_spider(self, spider):
    print('正在关闭连接')
    self.cursor.close()
    self.conn.close()
    • 爬虫文件提交的item类型的对象最终会提交给哪一个管道类?
      • 爬虫文件提交的item只会给管道文件中第一个被执行的管道类接收
      • 第一个管道类中的process_item中的return item表示将item传递给下一个即将被执行的管道类

基于Spider的全站数据爬取

什么是全站数据爬取

就是将网站中某板块下的全部页码对应的页面数据爬取下来

  • 需求:爬取校花网中的照片的名称
  • 实现方式:
    • 自行手动进行请求发送(推荐)
    • 将所有页面的url添加到start_urls列表中(不推荐,如果页码有上万个呢?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#xiaohua.py

import scrapy


class XiaohuaSpider(scrapy.Spider):
name = 'xiaohua'
# allowed_domains = ['www.xxx.com']
start_urls = ['http://www.521609.com/tuku/index.html']
# 生成一个通用的url模板(不可变)
url = 'http://www.521609.com/tuku/index_%d.html'
page_num = 2

def parse(self, response):
li_list = response.xpath('//ul[@class="pbl "]/li')
for li in li_list:
img_name = li.xpath('./a/p/text()').extract_first()
print(img_name)
if self.page_num <= 51:
print(self.page_num)
new_url = format(self.url % self.page_num)
# 手动请求发送:callbak回调函数是专门用于数据解析
self.page_num += 1
yield scrapy.Request(url=new_url, callback =self.parse)

请求传参

  • 使用场景:如果爬取的解析数据不在同一张页面中.(深度爬取)

图片数据爬取之ImagesPipeline

  • 基于scrapy爬取字符串类型的数据和爬取图片类型的数据区别?

    • 字符串:只需要xpath进行解析且提交管道进行持久化存储
    • 图片:xpath解析出图片的src属性值.单独对图片地址发起请求获取图片二进制类型的数据
  • ImagesPipeline:

    • 只需要将imgsrc的属性值进行解析,将属性值封装到item并提交给管道,管道就会对图片的src进行请求发送获取图片的二进制类型的数据,且还会帮我们进行持久化存储
  • 需求:爬取站长素材中的高清图片

  • 使用流程:

    • 数据解析(地址的地址)
    • 将存储文件地址的item提交到指定的管道类
    • 在管道文件中定制一个基于ImagesPipeLine的一个管道类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #pipelines.py

    # Define your item pipelines here
    #
    # Don't forget to add your pipeline to the ITEM_PIPELINES setting
    # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


    # useful for handling different item types with a single interface
    from itemadapter import ItemAdapter
    from scrapy.pipelines.images import ImagesPipeline
    import scrapy
    # class ZhanzhangproPipeline:
    # def process_item(self, item, spider):
    # return item


    class imgsPileLine(ImagesPipeline):
    # 就是可以根据图片地址进行图片数据的请求
    def get_media_requests(self, item, info):
    yield scrapy.Request(item['src'])
    # 指定图片存储的路径

    def file_path(self, request, response=None, info=None):
    imgName = request.url.split('/')[-1]
    return imgName

    def item_completed(self, results, item, info):
    return item # 返回给下一个即将被执行的管道类
    • 修改serrings.py配置文件
    1
    2
    3
    #serrings.py
    #指定图片存储的目录
    IMAGES_STORE = './imgs'
    • 指定开启的管道类:定制的管道类
    1
    2
    3
    4
    5
    serrings.py

    ITEM_PIPELINES = {
    'zhanzhangPro.pipelines.imgsPileLine': 300,
    }

中间件

  • 下载中间件

    • 位置:引擎和下载器之间

    • 作用:批量拦截到整个工程中所有的请求和响应

    • 拦截请求

    • UA伪装

    • 代理IP

    • 拦截响应:

    • 篡改响应数据,响应对象

    • 设置UA伪装以及代理IP

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      #middlewares.py

      # Define here the models for your spider middleware
      #
      # See documentation in:
      # https://docs.scrapy.org/en/latest/topics/spider-middleware.html

      from scrapy import signals

      # useful for handling different item types with a single interface
      from itemadapter import is_item, ItemAdapter
      import random


      class QiubaiproDownloaderMiddleware:
      user_agent_list = [
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
      "(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
      "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
      "(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 "
      "(KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 "
      "(KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
      "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 "
      "(KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 "
      "(KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
      "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 "
      "(KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
      "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 "
      "(KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 "
      "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
      "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 "
      "(KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
      ]
      # 可被选用的代理IP
      PROXY_http = [
      '153.180.102.104:80',
      '195.208.131.189:56055',
      ]
      PROXY_https = [
      '120.83.49.90:9000',
      '95.189.112.214:35508',
      ]

      # 拦截请求
      def process_request(self, request, spider):
      # UA伪装
      request.headers['User-Agent'] = random.choice(self.user_agent_list)
      return None

      # 拦截所有的响应
      def process_response(self, request, response, spider):
      # Called with the response returned from the downloader.

      # Must either;
      # - return a Response object
      # - return a Request object
      # - or raise IgnoreRequest
      return response

      # 拦截发生异常的请求
      def process_exception(self, request, exception, spider):
      # 发生请求异常后设置代理
      # 对拦截到请求的url进行判断(协议头到底是http还是https)
      # request.url返回值:http://www.xxx.com
      if request.url.split(':')[0] == 'https': # 请求的协议头
      ip = random.choice(self.PROXY_https)
      request.meta['proxy'] = 'https://'+ip
      else:
      ip = random.choice(self.PROXY_http)
      request.meta['proxy'] = 'http://' + ip
      return request # 将修正之后的请求对象进行重新的请求发送
    • settings.py中开启下载中间件

    1
    2
    3
    DOWNLOADER_MIDDLEWARES = {
    'qiubaiPro.middlewares.QiubaiproDownloaderMiddleware': 543,
    }
  • 需求:爬取网易新闻中的新闻数据(标题和内容)

    • 通过网易新闻的首页解析出五大板块对应的详情页URL(没有动态加载)
    • 每一个板块对应的新闻标题都是动态加载出来的(动态加载)
    • 通过解析出每一条新闻详情页的url获取详情页的页面源码,解析出新闻内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#settings.py
#基本操作
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
#开启中间件拦截
DOWNLOADER_MIDDLEWARES = {
'wangyiPro.middlewares.WangyiproDownloaderMiddleware': 543,
}
#开启管道
ITEM_PIPELINES = {
'wangyiPro.pipelines.WangyiproPipeline': 300,
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#wangyi.py

import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem


class WangyiSpider(scrapy.Spider):
name = 'wangyi'
# allowed_domains = ['https://news.163.com/']
start_urls = ['https://news.163.com/']
models_urls = [] # 存储五个板块对应详情页的url
# 解析五大板块对应详情页的url

# 实例化一个浏览器对象
def __init__(self):
# 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的
self.bro = webdriver.Chrome(
executable_path=r'D:\Learning world\personal project\personal project\Python\爬虫\scrapy\wangyiPro\wangyiPro\chromedriver.exe')

def parse(self, response):
li_list = response.xpath('//div[@class="ns_area list"]/ul/li')
alist = [3, 4, 6, 7, 8]
for index in alist:
model_url = li_list[index].xpath('./a/@href').extract_first()
self.models_urls.append(model_url)

# 依次对每一个板块对应的页面进行请求
for url in self.models_urls: # 对每一个板块的url进行请求发送
yield scrapy.Request(url=url, callback=self.parse_model)

# 每一个板块对应的新闻标题相关的内容都是动态加载
def parse_model(self, response): # 解析每一个板块页面中对应新闻的标题和新闻详情页的url
div_list = response.xpath('//div[@class="ndi_main"]/div')
for div in div_list:
title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
new_detail_url = div.xpath(
'./div/div[1]/h3/a/@href').extract_first()
item = WangyiproItem()
item['title'] = title
# 对新闻详情页的url发起请求
yield scrapy.Request(url=new_detail_url, callback=self.parse_detail, meta={'item': item})

def parse_detail(self, response):
content = response.xpath(
'//div[@class="post_body"]/p/text()').extract()
content = ''.join(content)
item = response.meta['item']
item['content'] = content
yield item

def closed(self, spider):
self.bro.quit()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#middlewares.py


# Define here the models for your spider middleware
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html

from scrapy import signals

# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter
from scrapy.http import HtmlResponse
from time import sleep


class WangyiproDownloaderMiddleware:
def process_request(self, request, spider):
# Called for each request that goes through the downloader
# middleware.

# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None

# 通过该方法拦截五大板块对应的响应对象,进行篡改
def process_response(self, request, response, spider): # spider表示的是爬虫对象
bro = spider.bro # 获取了在爬虫类中定义的浏览器对象
# 挑选出指定的响应对象进行篡改
# 通过url指定request
# 通过request指定response
if request.url in spider.models_urls:
bro.get(request.url) # 五个板块对应的url进行请求
sleep(2)
page_text = bro.page_source # 包含了动态加载的新闻数据
# response # 五大板块对应的响应对象
# 针对定位到的response进行篡改
# 实例化新的相应对象,符合需求:包含动态加载出的新闻数据,替代原来不满足需求的响应对象
# 如何获取动态加载出的新闻数据呢?
# 基于selenium便捷的获取动态加载数据
new_response = HtmlResponse(
url=request.url, body=page_text, encoding='utf-8', request=request)
return new_response
else:
# response # 其他请求对应的响应对象
return response

def process_exception(self, request, exception, spider):
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.

# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
pass
1
2
3
4
5
6
7
8
9
#items.py

import scrapy


class WangyiproItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
content = scrapy.Field()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pipelines.py


# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class WangyiproPipeline:
def process_item(self, item, spider):
print(item)

CrawlSpider类

CrawlSpider类:Spider的一个子类

  • 全站数据爬取的方式
    • 基于Spider手动请求
    • 基于CrawlSpider
  • CrawlSpider的使用:
    • 创建一个工程
    • cd xxx
    • 创建爬虫文件(与之前不一样,基于CrawlSpider子类)
    • scrapy genspider -t crawl name www.xxx.com
    • LinkExtractor(链接提取器):
      • 作用:根据指定规则(allow="正则")进行指定链接的提取
    • Rule(规则解析器)
      • 作用:将链接提取器提取到的链接进行指定规则(callback的解析操作)

分布式爬虫

分布式爬虫的概念

我们需要搭建一个分布式的集群,让其对一组资源进行分布联合爬取

作用

提升爬取数据的效率

如何实现分布式

  • 安装scrapy-redis的组件
  • 原生的scrapy是不可以实现分布式爬虫的,必须要让scrapy结合着scrapy-redis组件一起实现分布式爬虫
  • 为什么原生的scrapy不可以实现分布式?
    • 调度器不可以被分布式集群共享
    • 管道不可以被分布式集群共享
  • scrapy-redis组件作用
    • 可以给原生的scrapy框架提供可以被共享的管道和调度器
  • 实现流程
    • 创建一个工程

    • 创建一个基于CrawlSpider的爬虫文件

    • 修改当前的爬虫文件

    • 导包

      • from scrapy_redis.spiders import RedisCrawlSpider
    • start_urlsallowed_domains进行注释

    • 添加一个新属性:与之代替的是redis_key='sun'可以被共享的调度器队列的名称

    • 编写数据解析相关的操作

    • 将当前爬虫类的父类修改成RedisCrawlSpider

    • 修改配置文件settings.py

      • 指定使用可以被共享的管道
      1
      2
      3
      ITEM_PIPELINES = {
      'scrapy_redis.pipelines.RedisPipeline':400
      }
      • 指定调度器
      1
      2
      3
      4
      5
      6
      #增加了一个去重容器类的配置,作用使用Redis的set集合来存储请求的指纹数据,从而实现请求去重的持久化
      DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
      #使用scrapy-redis组件自己的调度器
      SCHEDULER="scrapy_redis.scheduler.Scheduler"
      #配置调度器是否要持久化,也就是当爬虫结束了,要不要清空Redis中请求队列和去重指纹的set.True=保留,False=清空
      SCHEDULER_PERSIST=TRUE
      • 指定redis服务器
      1
      2
      3
      4
      #setting.py

      REDIS_HOST = 'redis服务器的ip地址'
      REDIS_PORT = 6379
    • redis相关操作配置

      • 配置`redis`的配置文件
      • linux/mac:redis.conf
      • windows:redis.windows.conf
      • 打开配置文件修改
        • bind 127.0.0.1进行删除
        • 关闭保护模式:protected_mode yes改为no
      • 结合着配置文件开启redis服务
        • redis-server配置文件
        • 启动客户端
        • redis-cli
      • 执行工程
      • scrapy runspider xxx.py(爬虫源文件名称)
      • 向调度器的队列中放入一个起始的url
      • 调度器的队列在redis的客户端中
        • lpush sun(爬虫文件中的redis_key) www.xxx.com(起始的url)