likes
comments
collection
share

Scrapy框架学习和实践

作者站长头像
站长
· 阅读数 3

一、前言

近期工作遇到了一些和网络爬虫相关的内容,于是抽时间了解一下爬虫系统。这边文章是学习和使用Scrapy框架进行信息爬取的一篇学习笔记。

二、Scrapy的基本介绍

简介

Scrapy(/ˈskreɪpaɪ/)是一个用于爬取网站并提取结构化数据的应用程序框架,可用于各种有用的应用程序,例如数据挖掘、信息处理或历史档案。尽管Scrapy最初是为了网络抓取而设计的,但也可以用于使用API(如亚马逊联盟网络服务)提取数据或作为通用网络爬虫。

(摘自Scrapy官方文档:docs.scrapy.org/en/latest/i…

组件介绍

  • Scrapy Engine(引擎) : 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。
  • Scheduler(调度器) : 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
  • Downloader(下载器) :负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,
  • Spider(爬虫) :它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器).
  • Item Pipeline(管道) :它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。
  • Downloader Middlewares(下载中间件) :你可以当作是一个可以自定义扩展下载功能的组件。
  • Spider Middlewares(Spider中间件) :你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests)

数据流控制

Scrapy框架学习和实践 在Scrapy中,数据流由执行引擎控制,流程如下:

  1. 引擎从Spider获取要爬取的初始请求。
  2. 引擎将请求安排到调度器中,并请求下一个要爬取的请求。
  3. 调度器将下一个要爬取的请求返回给引擎。
  4. 引擎将请求发送给下载器,经过下载器中间件。
  5. 页面下载完成后,下载器生成一个带有该页面的响应,并将其发送给引擎,此过程会经过下载器中间件。
  6. 引擎从下载器接收响应,并将其经过Spider中间件发送给Spider进行处理。
  7. Spider处理响应,经过Spider中间件返回抓取的数据项和新的请求。
  8. 引擎将处理后的数据项发送到数据管道,然后将处理后的请求发送给调度器,并请求下一个要爬取的请求。
  9. 重复上述过程(从步骤3)直到调度器没有更多的请求为止。

三、Scrapy实践

目标

从Foresight News网站上爬取Web3投融资信息并持久化存储

执行步骤

简单总结爬取数据的步骤,可以分为以下三步:

  • 爬取目标网站
  • 结构化数据选取
  • 存储数据

爬取目标网站

首先,创建爬虫项目,可以使用如下命令

scrapy startproject foresight

其次,创建爬虫(genspider),进入foresight/spiders目录,执行命令,生成对应的爬虫程序代码fundrasing.py

scrapy genspider fundraising "https://foresightnews.pro/wiki/fundraising"

命令行会自动生成一个包含spider基本属性和方法的模板,先对parse函数进行简单修改:

def parse(self, response):
	filename = "fr.html"
	open(filename, 'wb').write(response.body)

然后运行爬虫程序,验证一下是否可以爬取到数据,运行命令如下:

scrapy crawl fundraising

运行后,在当前目录下生成了"fr.html"文件。

结构化数据选取

首先,定义一个想要提取的结构化数据类型,它需要包含:公司(项目)名称、融资金额,融资轮次和融资时间。

class ForesightItem(scrapy.Item):
    # define the fields for your item here like:
    name=scrapy.Field()
    amount=scrapy.Field()
    amount_unit=scrapy.Field()
    amount_round=scrapy.Field()
    date=scrapy.Field()

然后从爬取中的页面中,提取这些信息。修改刚才写好的fundrasing.py,把简单的保存整个HTML的逻辑改为从页面中提取相应的元素。

import scrapy
from foresight.items import ForesightItem

class FundraisingSpider(scrapy.Spider):
    name = "fundraising"
    allowed_domains = ["foresightnews.pro"]
    start_urls = ["https://foresightnews.pro/wiki/fundraising"]

    def parse(self, response):
        elements=response.xpath('//*[@class="invest InvestCord"]')
        for element in elements:
            item = ForesightItem()
            item_name = element.xpath('./div/div/span[@class="name"]/text()').extract()
            item_amount=element.xpath('./div[1]/div[2]/div[1]/text()').extract()[0].strip()
            item_amount_unit=element.xpath('./div[1]/div[2]/div[1]/span/text()').extract()
            item_amount_round=element.xpath('./div[1]/div[2]/div[2]/text()').extract()
            item_date=element.xpath('./div[1]/div[3]/text()').extract()
            # print(f"名字:{item_name}, 融资金额: {item_amount},{item_amount_unit}融资轮次: {item_amount_round},日期: {item_date}")

            item['name'] = item_name
            item['amount'] = item_amount
            item['amount_unit']=item_amount_unit
            item['amount_round'] = item_amount_round
            item['date'] = item_date

            # return or yield
            yield item

关于return和yield的区别:

Scrapy框架学习和实践

注意:如果想要使用Scrapy的pipeline处理item数据,在parse()函数结尾处需要用yield关键字

数据持久化

有两种方式可以完成的数据持久化,简单的办法是通过命令行存储文件,支持的文件格式有:JSON、JSON lines、CSV和XML。另一种发放可以通过Pipeline实现数据的存储逻辑。

1.命令行存储文件:

scrapy crawl fundraising -O foresight.csv

可以看到在当前目录下,出现了foresight.csv文件,文件中存储了之前定义好的几项信息。

2.通过Pipeline存储数据

另一种存储数据的方式是通过pipeline接受每一个item数据,随后可以通过写入文件或者数据库等存储媒介。

以写入文件为例,自定义一个pipeline:

class ForesightPipeline:
    def __init__(self):
        self.file = open('./foresight2.csv', 'wb')
        self.exporter = CsvItemExporter(self.file,fields_to_export=["name","amount","amount_unit","amount_round","date"])
        self.exporter.start_exporting()

    def process_item(self, item, spider):
        if isinstance(item, ForesightItem):
            self.exporter.export_item(item)        
        return item
    
    def close_spider(self, spider):
        self.exporter.finish_exporting()
        self.file.close()
        print(f"文件已保存至:{self.file.name}")

这段代码里使用了框架提供的CsvItemExporter类,使用时仅需要通过传入文件名和CSV首行名称,即可按顺序逐行写入文件。

另外,如果要pipeline生效,需要在此项目的setting文件里加入pipeline的配置:

ITEM_PIPELINES = {
   "foresight.pipelines.ForesightPipeline": 300,
}

在这个设置中需要给不同的pipeline赋予整数值,这个整数在习惯上通常定义在0-1000的范围内,整数值决定pipeline的运行顺序,按照从小到大的顺序依次执行。

配置完成后,再执行

scrapy crawl fundraising

可以在代码目录下生成一个名为”foresight2.csv“的文件,里面存储了爬取的数据信息:

Scrapy框架学习和实践

除了写文件之外,还经常通过pipeline进行数据去重、简单的数据处理、写入数据库等操作。

至此,一个简单的网页爬虫就完成了,可以在此基础上增加更多目标地址,或者根据当前页面的url进行递归爬取。另外Scrapy还提供了一套插件管理体系,支持开发人员扩展系统配置,增加额外功能,关于Scrapy的更多使用方式和技巧,还是需要在实践中逐渐探索和加深理解的。