scrapy-使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/iFan138/article/details/88425699

该文章涉及方面比较多,后面会将该文章拆开,每个方面都会进行详细的说明和使用,但是该文件的内容不变。

Scrapy 爬虫框架的使用 手册

基础介绍

安装

pip install Twisted-18.9.0-cp36-cp36m-win_amd64.whl
pip install Scrapy

错误

ModuleNotFoundError: No module named ‘win32api’

pip install pypiwin32

创建项目

scrapy startproject myscrapy

目录介绍

  • scrapy.cfg : 项目的配置文件
  • myscrapy:项目
  • myscrapy/items.py:项目使用的item文件
  • myscrapy/pipelines.py: 项目中的pipelines文件.
  • myscrapy/settings.py: 项目的设置文件.
  • myscrapy/spiders/: 放置spider代码的目录.

创建一个爬虫的应用

# 创建一个名称叫danke 网址后缀为danke.com的爬虫应用
scrapy genspider danke danke.com
# 此时在myscrapy/spiders目录下面有了一个danke.py的文件 打开

修改items.py

import scrapy
class CrawlsHouseItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass
class DanKe(CrawlsHouseItem):
     # 房子名称
    host_name = scrapy.Field()
    # 租金
    price = scrapy.Field()
    # room list
    room_list = scrapy.Field()

修改danke.py

# -*- coding: utf-8 -*-
import scrapy
from crawls_house.items import DanKe


class DankeSpider(scrapy.Spider):
    # 应用的名称
    name = 'danke'
    # 只有是该域名的连接才会被调度
    allowed_domains = ['www.danke.com']
    # 起始页面的url
    start_urls = ['https://www.danke.com/room/bj']

    def parse(self, response):
        # 获取需要继续跟进的url
        for href in response.xpath("//div[@class='r_lbx_cena']//a//@href").extract():
            # 将需要跟进的url交给调度器处理并指定回调方法为parse_item
            yield scrapy.Request(href, self.parse_item)
        # 获取下一页的url 自动会进行去重
        for next_href in response.xpath("//div[@class='page']//a//@href").extract():
            yield scrapy.Request(next_href)

    def parse_item(self, response):
        # 处理详情页面的信息
        danke = DanKe()
        # 房子的名称
        danke['host_name'] = response.xpath('//h1//text()').extract()[0]
        # 租金
        price = response.xpath('//div[@class="room-price-sale"]//text()').extract()[0]
        danke['price'] = price.replace(" ", "").replace("\n", "")
        # room list
        room_list = []
        for room in response.xpath('//div[@class="room-list"]//text()').extract():
            room = room.replace(" ", "").replace("\n", "")
            if len(room) != 0:
                room_list.append(room)
        danke["room_list"] = ",".join(room_list)
        # 返回 Item 交给 pipline 处理
        return danke

修改 settings.py

# 设置 User-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 是否遵循 robots.txt 协议
ROBOTSTXT_OBEY = False
# 是否关闭 cookies (默认启用)
COOKIES_ENABLED = False
# 使用 request headers:
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}
#item piplines
ITEM_PIPELINES = {
    'crawls_house.pipelines.CrawlsHousePipeline': 300,
}

修改pipelines.py

class CrawlsHousePipeline(object):
    def __init__(self):
        super().__init__()
        self.file = open("danke.json", "a", encoding="utf-8")

    def __del__(self):
        self.file.close()

    def process_item(self, item, spider):
        print(item, spider)
        self.file.write("{}|{}|{}\n".format(item["host_name"], item["price"], item["room_list"]))
        return item

启动项目

scrapy crawl danke

命令行使用

创建项目

scrapy startproject myproject

创建一个spider

# scrapy genspider [-t template] <name> <domain>
scrapy genspider myspider myspider.com
# 查看可以使用的template
scrapy genspider -l
# 查看template 的内容
scrapy genspider -d basic

运行项目

scrapy crawl myspider

检查spider

scrapy check -l
scrapy check

列出当前可用的spider

scrapy list

查看页面返回结果

scrapy fetch --nolog --headers https://www.danke.com/room/bj

用浏览器打开页面

scrapy view https://www.danke.com/room/bj

命令行执行scrapy

scrapy shell https://www.danke.com/room/bj

对url进行分析

scrapy parse <url> [options]
  • --spider=SPIDER: 跳过自动检测spider并强制使用特定的spider

  • --a NAME=VALUE: 设置spider的参数(可能被重复)

  • --callback or -c: spider中用于解析返回(response)的回调函数

  • --pipelines: 在pipeline中处理item

  • --rules or -r: 使用 CrawlSpider 规则来发现用来解析返回(response)的回调函数

  • --noitems: 不显示爬取到的item

  • --nolinks: 不显示提取到的链接

  • --nocolour: 避免使用pygments对输出着色

  • --depth or -d: 指定跟进链接请求的层次数(默认: 1)

  • --verbose or -v: 显示每个请求的详细信息

Item Pipline

当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item的处理。

每个item pipeline组件(有时称之为“Item Pipeline”)是实现了简单方法的Python类。他们接收到Item并通过它执行一些行为,同时也决定此Item是否继续通过pipeline,或是被丢弃而不再进行处理。

以下是item pipeline的一些典型应用:

  • 清理HTML数据
  • 验证爬取的数据(检查item包含某些字段)
  • 查重(并丢弃)
  • 将爬取结果保存到数据库中

编写自己的item pipline

  1. 重写以下方法
import json
class MyItemPipline(object):
    def __init__(self, file_name):
        self.filename = file_name 
    def open_spider(self, spider):
        # spider 开始时被调用
        sefl.file = open(self.filename, "a", encoding="utf-8")
        pass
    def close_spider(self, spider):
        # spider 结束时被调用
        self.file.close()
        pass
    def process_item(self, item, spider)
        # 每个item pipeline组件都需要调用该方法,这个方法必须返回一个 Item (或任何继承类)对象, 或是抛出 DropItem 异常,被丢弃的item将不会被之后的pipeline组件所处理。
        if item['id'] == 1:
            raise DropItem("item is get")
        else:
           self.file.write("%s\n" % json.dumps(dict(item))) 
            return item
   @classmethod
   def from_crawler(cls, crawler):
        # 从crawler的配置文件中获取配置信息 需要设置其为类方法
       return cls(
            file_name = crawler.settings.get("file_name")
       )
  1. 在配置文件最后那个添加该组件

    分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。

    ITEM_PIPELINES = {
        'crawls_house.pipelines.CrawlsHousePipeline': 300,
        'crawls_house.pipelines.CrawlsHousePipeline2': 400,
    }
    

Logging

  1. 使用日志

    from scrapy import log
    log.msg("logging warning", level=log.WARNING)
    
  2. 将日志导出到文件

    scrapy crawl danke --logfile=danke.log
    

Telent

使用telent终端访问scrapy

# 默认监听本地的6023端口
telnet localhost 6023
快捷名称 描述
crawler() Scrapy Crawler (scrapy.crawler.Crawler 对象)
engine() Crawler.engine属性
spider() 当前激活的爬虫(spider)
slot() the engine slot
extensions() 扩展管理器(manager) (Crawler.extensions属性)
stats() 状态收集器 (Crawler.stats属性)
settings() Scrapy设置(setting)对象 (Crawler.settings属性)
est() 打印引擎状态的报告
prefs() 针对内存调试 (参考 调试内存溢出)
p() pprint.pprint 函数的简写
hpy() 针对内存调试
# 暂停爬虫
telnet localhost 6023
>>> engine.pause()
# 恢复爬虫
>>> engine.unpause()
# 停止爬虫
>>> engine.stop()

Setting 配置

# 设置 telnet 的端口
TELNETCONSOLE_PORT = [6023, 6073]
# 监听的地址
TELNETCONSOLE_HOST = '127.0.0.1'

下载中间件

动态随机User-Agent和使用代理

  1. settings.py设置

    DOWNLOADER_MIDDLEWARES = {
        'crawls_house.middlewares.CrawlsHouseDownloaderMiddleware': 543,
    }
    
  2. middlewares.pyCrawlsHouseDownloaderMiddleware类中修改

    其他的方法不要改变

    import random
    from crawls_house.settings import USER_AGENTS, PROXIES
    class CrawlsHouseDownloaderMiddleware(object):
        def process_response(self, request, response, spider):
            # 设置 user-agent
            request.headers["User-Agent"] = random.choice(USER_AGENTS)
            # 设置 proxy
            request.meta["proxy"] = "http://%s" % random.choice(PROXIES)
            return response
    

Spider中间件

图片下载

pip install Pillow
  1. 在item.py文件的需要进行图片下载的类中添加属性

    image_urls = scrapy.Field()
    image_paths = scrapy.Field()
    
  2. 在settings.py文件中添加

    ITEM_PIPELINES = {
        'crawls_house.pipelines.XMLExportItem': 300,
        # 这是自定义的
        "crawls_house.pipelines.MyImagesPipeline": 299,
        # 这是基本的 不可和自定义的同时设置
        "scrapy.contrib.pipeline.images.ImagesPipeline", 299
    }
    # 图片的下载地址
    IMAGES_STORE = r'D:\workspace\crawls_house\imgs'
    # 图片的有效时间
    IMAGES_EXPIRES = 90
    # 图片缩略图生成
    IMAGES_THUMBS = {
        'small': (50, 50),
        'big': (270, 270),
    }
    
  3. 如果使用了自定义 则需要在pipeline.py文件中添加

    from scrapy.contrib.pipeline.images import ImagesPipeline
    from scrapy.exceptions import DropItem
    class MyImagesPipeline(ImagesPipeline):
        def item_completed(self, results, item, info):
            image_paths = [x['path'] for ok, x in results if ok]
            if not image_paths:
                raise DropItem("Item contains no images")
            item['image_paths'] = image_paths
            return item
    

自动限速

# 是否启用限速扩展
AUTOTHROTTLE_ENABLED = True
# 初始限速速度(单位秒)。
AUTOTHROTTLE_START_DELAY = 5.0
# 在高延迟情况下最大的下载延迟(单位秒)。
AUTOTHROTTLE_MAX_DELAY = 60.0
# 起用AutoThrottle调试(debug)模式,展示每个接收到的response。 您可以通过此来查看限速参数是如何实时被调整的。
AUTOTHROTTLE_DEBUG = True

Jobs 暂停和恢复爬虫

  1. settings.py文件中指定

    # 爬虫的job恢复路径
    JOBDIR = "crawl_job/crawls-1"
    
  2. 在命令行指定

    scrapy crawl danke -s JOBDIR=crawls_job/danke-1
    

同一时间启动多个Spider

  1. crawls_house下创建crawlall.py文件

    from scrapy.commands import ScrapyCommand
    from scrapy.utils.project import get_project_settings
    
    class Command(ScrapyCommand):
        requires_project = True
    
        def syntax(self):
            return '[options]'
    
        def short_desc(self):
            return 'Runs all of the spiders'
    
        def run(self, args, opts):
            spider_list = self.crawler_process.spiders.list()
            for name in spider_list:
                self.crawler_process.crawl(name, **opts.__dict__)
            self.crawler_process.start()
    
    
  2. settings.py文件中添加

    COMMANDS_MODULE = 'crawls_house'
    
  3. 启动

    scrapy crawlall
    

分布式爬虫

  1. 安装scrapy-redis

    pip install scrapy-redis
    
  2. 修改settings.py文件

    # 启用Redis调度存储请求队列.
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 确保所有的爬虫通过Redis去重
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    # Default requests serializer is pickle, but it can be changed to any module
    # with loads and dumps functions. Note that pickle is not compatible between
    # python versions.
    # Caveat: In python 3.x, the serializer must return strings keys and support
    # bytes as values. Because of this reason the json or msgpack module will not
    # work by default. In python 2.x there is no such issue and you can use
    # 'json' or 'msgpack' as serializers.
    # 默认请求序列化使用的是pickle 但是我们可以更改为其他类似的。PS:这玩意儿2.X的可以用。3.X的不能用
    # SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
    
    # 不清除Redis队列、这样可以暂停/恢复 爬取
    # SCHEDULER_PERSIST = True
    
    # 使用优先级调度请求队列 (默认使用)
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
    
    # 可选用的其它队列
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
    
    # 最大空闲时间防止分布式爬虫因为等待而关闭
    # 这只有当上面设置的队列类是SpiderQueue或SpiderStack时才有效
    # 并且当您的蜘蛛首次启动时,也可能会阻止同一时间启动(由于队列为空)
    # SCHEDULER_IDLE_BEFORE_CLOSE = 10
    
    # 将清除的项目在redis进行处理
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 1,
        'crawls_house.pipelines.XMLExportItem': 300,
        "crawls_house.pipelines.MyImagesPipeline": 299
    }
    
    # 序列化项目管道作为redis Key存储
    # REDIS_ITEMS_KEY = '%(spider)s:items'
    
    # 默认使用ScrapyJSONEncoder进行项目序列化
    # REDIS_ITEMS_SERIALIZER = 'json.dumps'
    
    # 指定连接到redis时使用的端口和地址(可选)
    REDIS_HOST = '192.168.1.203'
    REDIS_PORT = 60790
    
    # 指定用于连接redis的URL(可选)
    # 如果设置此项,则此项优先级高于设置的REDIS_HOST 和 REDIS_PORT
    # REDIS_URL = 'redis://user:pass@hostname:9001'
    
    # 自定义的redis参数(连接超时之类的)
    # REDIS_PARAMS  = {}
    # 自定义redis客户端类
    # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'
    
    # 如果为True,则使用redis的'spop'进行操作。
    # #如果需要避免起始网址列表出现重复,这个选项非常有用。开启此选项urls必须通过sadd添加,否则会出现类型错误。
    # REDIS_START_URLS_AS_SET = False
    
    # RedisSpider和RedisCrawlSpider默认 start_usls 键
    #REDIS_START_URLS_KEY = '%(name)s:start_urls'
    
    # 设置redis使用utf-8之外的编码
    # REDIS_ENCODING = 'latin1'
    
  3. 修改***tongcheng.py***文件
    只修改必要部分,其余部分不变

     # 引入 redisSpider类
     from scrapy_redis.spiders import RedisSpider
     # 继承该类
     class TongchengSpider(RedisSpider):
         name = 'tongcheng'
         allowed_domains = ['58.com']
         # redis中是的key
         redis_key = "tongcheng:start_urls"
    
  4. 启动爬虫,此时爬虫并没有开始爬取

    scrapy runspider spider/tongcheng.py
    
  5. 向redis中添加键,爬虫开始运行

    lpush tongcheng:start_urls https://bj.58.com/chuzu/?PGTID=0d3090a7-0000-126a-c26e-b315d60fe251&ClickID=1
    

架构概览

scrapy_architecture

组件

Scrapy Engine

引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。 详细内容查看下面的数据流(Data Flow)部分。

调度器(Scheduler)

调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader)

下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders

Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。 更多内容请看 Spiders

Item Pipeline

Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。 更多内容查看 Item Pipeline

下载器中间件(Downloader middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。更多内容请看 下载器中间件(Downloader Middleware)

Spider中间件(Spider middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。更多内容请看 Spider中间件(Middleware)

数据流(Data flow)

Scrapy中的数据流由执行引擎控制,其过程如下:

  1. 引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
  2. 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
  3. 引擎向调度器请求下一个要爬取的URL。
  4. 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
  5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
  6. 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
  7. Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
  8. 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
  9. (从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。

事件驱动网络(Event-driven networking)

Scrapy基于事件驱动网络框架 Twisted 编写。因此,Scrapy基于并发性考虑由非阻塞(即异步)的实现。

关于异步编程及Twisted更多的内容请查看下列链接:

猜你喜欢

转载自blog.csdn.net/iFan138/article/details/88425699