Scrapy——可配置的爬虫

本文介绍Scrapy爬虫框架的基本使用方法,包括安装、项目创建、定义数据结构、编写爬虫逻辑等步骤,并通过实例演示如何抓取豆瓣图书信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:


http://bbs.cnpameng.com/bbs/forum.php?mod=viewthread&tid=12&extra=page%3D1

Scrapy——可配置的爬虫

简介

        Scrapy是一个爬虫程序的框架,用来爬取网页内容和结构化内容的提取。所谓框架,就是可以自己定制一个针对特定网站的爬虫,定制的方法仅仅是需要添加一些简单的规则即可。很多大部分爬虫任务的公共部分是内部写好的,不需要开发人员重新写,减少重复开发。


安装

        Scrapy的官方网站: http://scrapy.org/
        Scrapy的技术文档: http://doc.scrapy.org/en/0.20/
        Scrapy的Github项目: https://github.com/scrapy/scrapy


        安装的话,提供两种最简单的方法:
        To installusing pip:
                pip installScrapy                                 
        To install using easy_install:
                easy_installScrapy      


例子:Step by Step

1.      创建项目





        安装好Scrapy后,最想做的肯定是找个HelloWorld项目来尝试一下。体验一下Scrapy的强大之处。
        首先我们得先了解Scrapy的项目目录结构,这样才能方便我们去了解Scrapy的工作原理。Scrapy的目录结构如下图所示:

        tutorial/
                scrapy.cfg
                tutorial/
                        __init__.py
                        items.py
                        pipelines.py
                        settings.py
                        spiders/
                                __init__.py
                                ...
        简单来说:
                •   scrapy.cfg: 项目配置文件。
                •   tutorial/: 项目的代码模块目录。
                •   tutorial/items.py: Item文件,主要用来存储结构化数据的结构信息。
                •   tutorial/pipelines.py: Pipline文件,负责持久化的输出格式。
                •   tutorial/settings.py: 爬虫的属性配置。
                •   tutorial/spiders/: 存放主要的爬虫代码的目录。
        这么复杂的目录结构,但是仅仅需要你的一条命令就能搞定,follow me:
                scrapystartproject tutorial                     
        tutorial是你自己设定的项目名称。
2.      定义Item格式化结构


        我们来试试爬去豆瓣图书上面的一些图书资源,所以我们定义我们的结构化数据结构如下:
         from scrapy .item import Item , Field
         class DoubanItem(Item ):
               url= Field ()        #网页网址
                 title = Field ()      #书名
                 author = Field ()     #作者
                imgurl = Field()       #图片地址
        这个结构类将用来存储我们在豆瓣图书上面爬去的数据。
3.      第一个爬虫Spider


        我们自己写的爬虫都要继承自基础类scrapy.spider.BaseSpider,这个类实现了大量爬虫共有的特性,我们只需要提供网址和对应的元素的定位就能爬取结构化信息。这个类的子类需要继承并重写的方法和属性如下:
        • name: identifies theSpider. It must be unique, that is, you can’t set the same name for differentSpiders.
        • start_urls: is a list ofURLs where the Spider will begin to crawl from. So, the first pages downloaded willbe those listed here. The subsequent URLs will be generated successively fromdata contained in the start URLs.
        • parse() is a method of thespider, which will be called with the downloaded Response object of each start URL.The response is passed to the method as the first and only argument.
        针对我们的豆瓣读书,我们可以简单实现一个爬虫代码如下:
         from scrapy .spider import BaseSpider   
         from scrapy .selector import Selector
         class DoubanSpider(BaseSpider ):
                name = 'douban'
                allowed_domains = ['douban.com' ]
                start_urls = [' http://read.douban.com/ebook/2004774/' ]


         def parse (self , response ):
                sel = Selector (response )
                douban = DoubanItem ()
                douban ['url' ] = response .url
                douban ['title' ] = sel .xpath ("//h1[@class='article-title']/text()" ).extract ()
                douban ['author' ] = sel .xpath ("//p[@class='author']/span[2]/span/text()" ).extract ()
                douban ['author' ] += sel .xpath ("//p[@class='author']/span[2]/a/text()" ).extract ()
                douban ['imgurl' ] = sel .xpath ("//div[@class='cover']/img/@src" ).extract ()
                return douban
        好了,这样我们就可以方便的将这个豆瓣页面上的信息格式化成我们的douban对象了。


4.      XPath定位信息


        不少人看到上面的parse函数里面的参数肯定很迷惑,这些是啥玩意,很神奇就能定位到了我们需要的信息。这里用到了XPath的Html文档的元素定位方式。
        详细的文档介绍在: http://www.w3.org/TR/xpath/
        中文的教程在: http://www.w3school.com.cn/xpath/
        举几个简单的例子:
        1.      By looking at the page HTML source we can seethat the file name is contained inside a <h1> tag:
                <h1>Home[2009][Eng]XviD-ovd </h1>
                AnXPath expression to extract the name could be:
                //h1/text()



        2.      And the description is contained inside a <div> tag with id="description":
                <h2>Description: </h2>
                <div id="description" >
                "HOME" - adocumentary film by Yann Arthus-Bertrand
                <br/>
                <br/>
                ***
                <br/>
                <br/>
                "Weare living in exceptional times. Scientists tell us that we have 10 years tochange the way we live, ...
                AnXPath expression to select the description could be:
                //div[@id=’description’]



        3.      Finally, the file size is contained in thesecond <p>taginside the <div>tagwith id=specifications:
                <div id="specifications" >
                <p>
                <strong>Category: </strong>
                <a href="/cat/4" >Movies </a> > <a href="/sub/35" >Documentary </a>
                </p>
                <p>
                <strong>Total size: </strong>
                699.79megabyte </p>
                AnXPath expression to select the file size could be:
                //div[@id=’specifications’]/p[2]/text()[2]
5.      运行爬虫


        只需要在我们项目的根目录下运行下面命令即可:
                scrapy crawldouban -o douban.json -t json        
        爬到的结构化数据将存在JSON格式的douban.json文件中。
6.      如此简单的爬虫。。。更复杂的需求。。。To be continue
递归爬取页面信息——Not only in just one page
6.      多网页爬取

       前面提到的爬虫太弱了,仅仅只能爬取一个页面的内容,而且一个页面中也就一本书的信息,这还需要爬虫吗?打开页面复制粘贴不就好了。于是需求来了,我们需要爬取多个页面的信息。多,可以是成百上千个吧。

        方法一:
       我们可以用到的最直接的方法可以这样来:
       start_urls = [' http://read.douban.com/ebook/2004774/',
              ' http://read.douban.com/ebook/2004775/',
              ' http://read.douban.com/ebook/2004776/',
              ' http://read.douban.com/ebook/2004777/'... ]
       那么我们就会分别爬取各个start_urls里面的网页的内容,这种只能说在你知道需要爬取的所有页面的地址的时候才能使用。

       方法二:
       如果有这样一个页面,他里面含有所有我们需要信息的一个url的列表,我们需要进去每一个url去爬取信息,这个时候想直接给出start_urls就不可能了。我们需要的是让爬虫自己去收集需要爬取的url。有一个方法是官方文档给出的,利用Rule类指定这类url列表,然后定义这类url的固定的parse方案即可。
       class MininovaSpider (CrawlSpider):
              name = ’mininova’
              allowed_domains = [’mininova.org’]
              start_urls = [’ http://www.mininova.org/today’]
              rules = [Rule(SgmlLinkExtractor(allow=[’/tor/\d+’]), ’parse_torrent’)]
               def parse_torrent(self, response):
                     sel = Selector(response)
                     torrent = TorrentItem()
                     torrent[’url’] = response.url
                     torrent[’name’] = sel.xpath("//h1/text()").extract()
                     torrent[’description’]= sel.xpath("//div[@id=’description’]").extract()
                     torrent[’size’] = sel.xpath("//div[@id=’info-left’]/p[2]/text()[2]").extract()
                      return torrent
       这段代码就是在爬取页面 http://www.mininova.org/today中的所有满足规则/tor/\d+的url。并移交给parse_torrent函数做后续处理。

       方法三:
       方法二也只能是有一个页面包含所有待爬连接url的时候,才能实现爬取,不过这样的页面毕竟是少数。在现在这种大规模的数据时代,数据的呈现总是分块儿的,每次页面也只会显示一部分数据。所以我们的爬虫也需要适应这种方式,需要边爬取页面,变获取之后需要爬取的页面url。这看上去是一个广度搜索的问题,而方法二是一层的广度搜索。
       于是研究一下parse函数,发现它的返回值,不仅仅可以是一个Item,也可以是一个Request。所谓Item就是一个数据项,用来存储格式化数据的;而Request则是一个url,表示待爬取的新地址。如果parse函数返回Item,那么它就会被写入到永久数据文件里面,如果parse返回的是Request,则会进一步去请求Request指向的页面,并在得到Response的时候跳转到对应的Callback函数进行处理。Callback函数可以是parse自己本身,也可以是一个其他和parse有相同签名的函数。
       针对豆瓣图书我写了如下程序,可以从它推荐的书继续扩展爬取,最后能够爬取到豆瓣图书的一个子集(可能是一类,或者相近的图书,这个和种子url有关)。
class DoubanSpider ( BaseSpider ):
    name = 'douban'
    allowed_domains = [ 'douban.com' ]
    start_urls = [ 'http://book.douban.com/' ]
    book_set = set ()         
    def parse ( self , response ):
         print '###Get Links###'
        sel = Selector ( response )
        newurls = sel . xpath ( "//div[@class='cover']/a/@href" ). extract ()
         print newurls
         for url in newurls :
             if url . find ( 'http' )==- 1 :
                url = 'http://read.douban.com' + url ;
             if url . find ( 'http://read.douban.com' )==- 1 :
                continue
            
            num = int ( url . replace ( 'http://read.douban.com/ebook/' , '' )[ 0 : 6 ])
             if num not in self . book_set :
                yield Request ( url , callback = self . parse_content , dont_filter =True)
                yield Request ( url , callback = self . parse , dont_filter =True)
                self . book_set . add ( num )
            
        
    def parse_content ( self , response ):
         print '###Get Content###'
        sel = Selector ( response )
        douban = DoubanItem ()
        douban [ 'url' ] = response . url
        douban [ 'title' ] = sel . xpath ( "//h1[@class='article-title']/text()" ). extract ()
        douban [ 'author' ] = sel . xpath ( "//p[@class='author']/span[2]/span/text()" ). extract ()
        douban [ 'author' ] += sel . xpath ( "//p[@class='author']/span[2]/a/text()" ). extract ()
        douban [ 'imgurl' ] = sel . xpath ( "//div[@class='cover']/img/@src" ). extract ()
         return douban
       这里定义了一个book_set,用于对已经爬过的书去重,防止重复爬取一个页面。在parse函数中真对一个页面进行了信息提取——跳转到parse_content函数,和新目标发现——跳转到自己。这样就能不间断的将豆瓣图书的一个集合自动爬取下来。
<think>好的,我需要回答用户关于在分布式Scrapy-Redis中使用`scrapy runspider`命令进行测试的问题。首先,我需要回顾Scrapy-Redis的基本原理,然后理解`runspider`和`crawl`的区别,特别是它们在分布式环境中的表现。 用户之前已经询问过`scrapy crawl`和`scrapy runspider`的区别,现在想知道在使用Scrapy-Redis搭建分布式爬虫时,是否可以使用`runspider`来测试单个爬虫脚本。这意味着用户可能正在尝试搭建分布式爬虫,但希望在开发阶段快速测试某个爬虫文件,而不想每次都通过完整的项目结构来运行。 首先,Scrapy-Redis的核心在于利用Redis作为请求调度和去重的中间件,允许多个爬虫实例共享同一个请求队列。这需要爬虫类继承自`RedisSpider`或`RedisCrawlSpider`,并配置相关的Redis连接参数。通常,这些配置是在项目的`settings.py`中设置的,比如`SCHEDULER`, `DUPEFILTER_CLASS`, `REDIS_URL`等。 接下来,`scrapy runspider`允许直接运行一个爬虫脚本而不需要完整的项目结构,但默认情况下不会加载项目配置。如果用户想用`runspider`来测试分布式爬虫,需要考虑以下几点: 1. **配置加载**:Scrapy-Redis的相关设置(如Redis连接、调度器、去重等)是否能在没有`settings.py`的情况下被正确加载。如果使用`runspider`,可能需要通过命令行参数或在脚本内部硬编码这些配置,这可能会带来不便,尤其是在需要频繁修改配置时。 2. **爬虫类继承**:分布式爬虫通常继承自`RedisSpider`,而普通的Scrapy爬虫继承自`scrapy.Spider`。使用`runspider`运行继承自`RedisSpider`的类时,需要确保所有依赖的Scrapy-Redis组件都已正确安装,并且在运行时能够被正确调用。 3. **Redis连接**:如果配置正确,即使通过`runspider`运行,爬虫也应该能够连接到Redis服务器并参与分布式任务调度。但需要确认在脚本中或通过命令行参数正确传递了Redis的配置信息,如`REDIS_URL`。 4. **去重和调度**:Scrapy-Redis使用Redis进行请求的去重和调度。如果`runspider`没有加载必要的中间件和扩展,可能导致去重失效或调度异常。需要确保这些组件在运行`runspider`时被正确启用。 接下来,我需要验证用户是否可以在不创建完整项目的情况下,仅通过`runspider`来测试分布式爬虫。例如,用户可能有一个独立的爬虫文件,其中包含了所有必要的Scrapy-Redis配置,或者通过命令行参数传递这些配置。 可能的解决方案包括: - 在爬虫脚本内部直接设置Scrapy-Redis所需的配置,如`custom_settings`。 - 使用`-s`参数在命令行中传递配置参数,例如`scrapy runspider myspider.py -s REDIS_URL=redis://localhost:6379`。 此外,需要考虑是否在分布式环境中运行多个`runspider`实例时,它们能够正确协作,共享同一个Redis队列,并且不会出现冲突。例如,每个实例都需要使用相同的Redis配置和相同的爬虫名称,以确保它们属于同一个分布式任务。 还需要提醒用户注意,虽然`runspider`可以用于测试,但在生产环境中,通常还是推荐使用`scrapy crawl`,因为项目结构能够更好地管理配置、中间件和管道,尤其是在复杂的分布式爬虫场景中。使用`runspider`可能更适合快速调试或临时任务,但缺乏项目结构的支持可能会增加维护的复杂性。 总结来说,回答需要确认用户可以在某些条件下使用`runspider`测试Scrapy-Redis爬虫,但需要注意配置的正确加载,以及可能遇到的一些限制。同时,要比较`runspider`和`crawl`在分布式环境中的适用性,帮助用户根据实际需求做出选择。</think>--- ### **Scrapy-Redis 环境下使用 `runspider` 的可行性分析** 在分布式 Scrapy-Redis 架构中,可以通过 `scrapy runspider` 命令测试爬虫脚本,但需要手动处理项目配置依赖。以下是具体说明和操作指南: --- #### **1. 核心条件** **必须满足以下前提才能成功运行:** - 爬虫类需继承 `RedisSpider` 或 `RedisCrawlSpider` - 显式配置 Redis 相关参数(去重、调度器、连接等) - 确保 Redis 服务已启动并可达 --- #### **2. 实现方式** ##### **(1) 在爬虫脚本内直接定义配置** 通过 `custom_settings` 属性硬编码关键参数: ```python # redis_spider.py import scrapy from scrapy_redis.spiders import RedisSpider class MyDistributedSpider(RedisSpider): name = "redis_spider" custom_settings = { 'SCHEDULER': 'scrapy_redis.scheduler.Scheduler', 'DUPEFILTER_CLASS': 'scrapy_redis.dupefilter.RFPDupeFilter', 'REDIS_URL': 'redis://:password@localhost:6379/0', 'ITEM_PIPELINES': { 'scrapy_redis.pipelines.RedisPipeline': 300 } } # 从Redis队列读取起始URL redis_key = 'my_spider:start_urls' def parse(self, response): # 解析逻辑 yield {'url': response.url} ``` ##### **(2) 通过命令行动态注入配置** 使用 `-s` 参数传递关键设置: ```bash scrapy runspider redis_spider.py \ -s SCHEDULER=scrapy_redis.scheduler.Scheduler \ -s DUPEFILTER_CLASS=scrapy_redis.dupefilter.RFPDupeFilter \ -s REDIS_URL=redis://:password@localhost:6379/0 ``` --- #### **3. 操作验证步骤** 1. **启动 Redis 服务** ```bash redis-server ``` 2. **向 Redis 队列推送种子 URL** ```bash redis-cli lpush my_spider:start_urls "https://example.com/page1" ``` 3. **运行爬虫** ```bash scrapy runspider redis_spider.py ``` 4. **多节点测试** 重复步骤3,在不同终端或服务器上启动多个实例,观察是否协同工作。 --- #### **4. 与 `scrapy crawl` 的对比** | 特性 | `scrapy crawl` + 项目结构 | `scrapy runspider` | |---------------------|----------------------------------|----------------------------------| | **配置管理** | 集中式 (`settings.py`) | 需手动编码或命令行传递 | | **依赖加载** | 自动加载中间件/管道 | 需显式声明所有必要组件 | | **维护成本** | 适合长期维护 | 适合临时测试 | | **团队协作** | 标准化配置易于共享 | 脚本独立性高但配置分散 | | **生产部署** | 推荐方式 | 不建议 | --- #### **5. 典型问题排查** - **连接拒绝错误** 检查 `REDIS_URL` 格式是否正确(包含密码、端口),确保防火墙放行。 - **调度不生效** 确认 `redis_key` 名称是否与 Redis 队列名称一致。 - **去重失效** 检查 `DUPEFILTER_CLASS` 是否正确定义,观察 Redis 中是否生成 `dupefilter` 相关键。 --- #### **6. 使用建议** - **快速验证场景** 使用 `runspider` + 内嵌配置,快速测试爬虫基础逻辑。 - **长期开发场景** 创建标准 Scrapy 项目,通过 `crawl` 命令运行,利用 `settings.py` 统一管理配置。 - **混合调试技巧** 开发阶段可先用 `runspider` 调试解析逻辑,再迁移到项目结构中完善分布式功能。 --- ### **总结** 在 Scrapy-Redis 分布式架构中,`scrapy runspider` 可用于临时测试,但需手动处理配置加载问题。建议在复杂项目中使用标准项目结构配合 `scrapy crawl`,以确保配置管理和扩展性的最优实践。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值