课程笔记1:Scrapy框架的基础用法

简介:Scrapy是一个基于Twisted的异步处理框架,是纯Python实现的爬虫框架。

优势:架构清晰、模块之间耦合度程度低,可扩展性强,可以灵活完成各种需求。只需要定制开发几个模块就能轻松实现一个爬虫。

架构介绍:

Engine(引擎),处理整个系统的数据流处理、触发事务,是整个框架的核心。

Item(项目),定义爬取结果的数据结构,爬取的数据会被赋值成该Item的对象。

Scheduler(调度器),接受引擎发过来的请求并将其加入到队列中,在引擎再次请求的时候,将请求提供给引擎。

Downloader(下载器),下载网页内容,并将网页内容返回给蜘蛛。

Spiders(爬虫),定义爬取的逻辑和网页的解析规则,负责解析响应并生成提取结果和新的请求。

Item Pipeline(项目管道),负责处理由蜘蛛从网页中抽取的项目(数据的清洗、验证和存储)。

Downloader Middlewares(下载器中间件),位于引擎和下载器之间的钩子框架,负责处理引擎和下载器之间的请求和响应。

Spider Middlewares(爬虫中间件),位于引擎和蜘蛛之间的钩子框架,负责处理向蜘蛛输入的响应和输出的结果及新的请求。

数据流:

  1. 【引擎】打开网站,找到处理该网站的【爬虫】并向该【爬虫】请求第一个要爬取的URL。
  2. 【引擎】拿到第一个要爬取的URL,并通过【调度器】以Request的形式调度(【调度器】会将【引擎】发过来的请求加入到队列中)。
  3. 【引擎】向【调度器】请求下一个要爬取的URL。
  4. 【引擎】将下一个要爬取的URL,通过【下载器中间件】转发给【下载器】下载。
  5. 【下载器】下载好页面后,会生成该页面的Response,然后通过【下载器中间件】发送给【引擎】。
  6. 【引擎】拿到Response后,会通过【爬虫中间件】转发给【爬虫】处理。
  7. 【爬虫】处理完Response后,会将提取到的Item和新的Request发送给【引擎】。
  8. 【引擎】会将拿到的Item转发给【项目管道】,将新的Request转发给【调度器】。
  9. 重复执行步骤2~8,直至【调度器】中没有更多的Request,【引擎】将关闭该网站,结束爬取作业。

示例:

目标:爬取网站(Quotes to Scrape),试用命令行和MongoDB数据库两种形式保存数据。

环境需求:Scrapy框架、PyMongo库和MongoDB服务。

在命令行中创建项目框架:

scrapy startproject quotes_2022

ps:quotes_2022是自定义的项目名

在命令行中创建Spider:

cd quotes_2022

scrapy genspider quotes quotes.toscrape.com​​​​​

ps:quotes是自定义的爬虫名;quotes.toscrape.com​​​​​是目标网站的域名

在PyCharm中查看项目的文件结构

 在PyCharm中查看自定义的Spider文件(quotes.py)

import scrapy

class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        pass

其中包含了一个刚刚自定义的类(QuotesSpider),这个类继承自Scrapy提供的Spider类(scrapy.Spider)。Scrapy用它来从网页抓取内容,并解析抓取的结果。

在类下面包含了三个属性(name、allowed_domains、start_urls)和一个方法(parse)。

name:用来区分不同的Spider,在同一个项目里面不能重复

allowed_domains:设定允许爬取的域名,如果初始或者后续的请求链接不是这个域名下的,则请求链接会被过滤掉

start_urls:用来定义初始请求,包含Spider在启动时爬取的url列表

parse:它是Spider的一个方法,负责解析返回的响应、提取数据或者进一步生成要处理的请求。(默认情况下被调用时,start_urls里面的链接构成的请求在完成下载执行后,返回的响应会作为唯一的参数传递给parse函数。)

创建Item

在PyCharm中查看items.py文件

import scrapy


class Quotes2022Item(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass

其中包含了一个自定义的Item(Quotes2022Item),这个Item继承自scrapy.Item类Item是保存爬取数据的容器,使用方法和字典类似。相比字典,Item多了额外的保护机制,可以避免拼写错误或者定义字段错误。

此时观察网站,确定需要获取的目标为text、author、tags。

 此时将item.py修改一下,添加上需要的字段(类名太挫了,顺手修改下)

import scrapy

class QuotesItem(scrapy.Item):
    text = scrapy.Field()
    author = scrapy.Field()
    tags = scrapy.Field()

解析response

在PyCharm中查看自定义的Spider文件

在Spider中可以看到,parse方法的参数response时start_urls里面的链接爬取后的结果。所以在parse方法中,可以直接对response变量包含的内容进行解析。

此时查看网页结构,可以发现每一页都有多个class为quote的区块,同时每个区块内都包含text、author、tags。那么就简单了,找出所有的quote,然后再提取每一个quote里面的内容。

提取的方式可以是CSS选择器或者XPath选择器(或者两者其一再配合上正则表达式)。 

使用CSS选择器的话,parse()改写如下:

    def parse(self, response):
        quotes = response.css('.quote')
        for quote in quotes:
            text = quote.css('.text::text').get()
            author = quote.css('.author::text').get()
            tags = quote.css('.tags .tag::text').getall()

如果想要获取的是author的主页链接,可以用:

author_url = quote.css('span a::attr(href)').get()
print(author_url)

输出结果:'/author/Albert-Einstein'


使用XPath选择器的话,parse改写如下:

    def parse(self, response):
        quotes = response.xpath("//div[@class='quote']")
        for quote in quotes:
            text = quote.xpath("./span[@class='text']/text()").get()
            author = quote.xpath("./span/small[@class='author']/text()").get()
            tags = quote.xpath("./div/a[@class='tag']/text()").getall()

如果想要获取的是author的主页链接,可以用:

author_url = quotes.xpath("./span/a/@href").get()
print(author_url)

输出结果:'/author/Albert-Einstein'

ps:

get()可以用extract_first()替换,选取首个元素;

getall()可以用extract()替换,选取全部元素。

使用Item

在上上一步中,已经定义好了Item,现在就要使用了。依次用刚才解析到的结果赋值Item中的每一个字段,最后将Item返回即可。

ps:需要先导入QuoteItem函数并实例化

import scrapy
from quotes_2022.items import QuotesItem

class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/']

    def parse(self, response):
        quotes = response.xpath("//div[@class='quote']")
        for quote in quotes:
            item = QuotesItem()
            item['text'] = quote.xpath("./span[@class='text']/text()").get()
            item['author'] = quote.xpath("./span/small[@class='author']/text()").get()
            item['tags'] = quote.xpath("./div/a[@class='tag']/text()").getall()
            yield item

后续Request

这一步需要从当前页面找到信息来生成下一个请求,然后在下一个请求的页面里找到信息再构造下一个请求,如此循环往复,实现整个网站的爬取。

拉网网站底部,找到【下一页】按钮。检查源代码,链接为/page/2,完整链接就是http://quotes.toscrape.com/page/2/,通过这个链接我们就可以构造下一个请求。

构造请求时需要用到scrapy.Request,并且这里需要传递两个参数:url和callback。

url:请求链接

callback:回调函数。(当指定了该回调函数的请求完成之后,获取到响应,引擎会将该响应作为参数传递给这个回调函数。回调函数进行解析或生成下一个请求。)

由于新旧页面的结果是一样的,所以可以继续用parse方法来做页面解析。

在parse方法后追加上如下代码:

        next = response.css('.next a::attr(href)').get()
        url = response.urljoin(next)
        yield scrapy.Request(url=url,callback=self.parse)

在命令行中试运行&保存到文件

scrapy crawl quotes

ps:quotes是自定义的爬虫名

截至目前,数据都只能在控制台查看。但其实只要Scrapy提供的Feed Exports可以轻松将抓取结果输出到本地文件中。

例如,如果想将上面的结果保存成JSON文件,可以执行以下命令:

scrapy crawl quotes -o quotes.json

# 保存到一行

scrapy crawl quotes -o quotes.jsonlines / scrapy crawl quotes -o quotes.jl

# 每一个Item输出一行JSON

 输出格式还支持很多种:

scrapy crawl quotes -o quotes.csv

scrapy crawl quotes -o quotes.xml

scrapy crawl quotes -o quotes.pickle

scrapy crawl quotes -o quotes.marshal

scrapy crawl quotes -o ftp://user:pass@ftp.example.com/path/to/quotes.csv

对于小型项目来讲,这可能就已经足够了,但是想要更复杂的输出,比如输出到数据库,那就需要通过Item Pipeline来完成了。

使用Item Pipeline导出到MogonDB数据库

Item Pipeline(项目管道),当Item生成后,会被自动送到Item Pipeline进行处理。Item Pipeline的基本功能:

  • 清理HTML数据
  • 验证爬取数据,检查爬取字段
  • 查重并丢弃重复内容
  • 将爬取结果保存到数据库

要想使用Item Pipeline需要定义好一个类及其下的process_item方法(process_item方法必须返回包含数据的字典或Item对象,或者抛出DropItem异常)。

在PyCharm中查看pipelines.py文件

class Quotes2022Pipeline:
    def process_item(self, item, spider):
        return item

可以看到,process_item方法有两个参数,一个是item(每次Spider生成的Item都会作为参数传递过来),另一个是spider(就是Spider实例)。

修改一下代码,实现:将item中的text字段的长度限制在50个字符以内,超长的部分用“...”代替。

from scrapy.exceptions import DropItem

class TextPipeline:
    def __init__(self):
        self.limit = 50
    def process_item(self, item, spider):
        if item['text']:
            if len(item['text']) > self.limit:
                item['text'] = item['text'][0:self.limit].rstrip()+'...'
            return item
        else:
            return DropItem('Missing Text')
  1. 导入DropItem方法
  2. 创建构造方法,定义好长度限制为50
  3. 先判断item的text属性是否存在,如果不存在,抛出DropTrem异常
  4. 再判断长度是否大于50,如果是,那就保留50以内的字符然后加上“...”
  5. 最后将item返回

定义一个新类,实现:将处理后的item存入MongoDB。

import pymongo

class MongoPipeline:
    def __init__(self,mongo_uri,mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @ classmethod
    def from_crawler(cls,crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DB')
        )
    def open_spider(self,spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def process_item(self,item,spider):
        name = item.__class__.__name__
        self.db[name].insert_one(dict(item))
        return item

    def close_spider(self,spider):
        self.client.close()

__init__:定义构造函数,完成连接MongoDB时需要用到的地址链接和数据库名称两个对象的初始化。

from_crawler:通过crawler拿到全局配置(settings.py)中的各项配置信息。(需要在全局配置中定义好【MONGO_URI】和【MONGO_DB】)

open_spider:当Spider开启时,调用这个方法,完成一些初始化操作

process_item:执行数据插入操作

close_spider:当Spider关闭时,调用这个方法,关闭数据库的连接

在PyCharm中查看settings.py文件

最后还需要在全局配置文件里面补充上MongoDB的链接信息以及激活前面定义的两个Pipeline类。

ITEM_PIPELINES = {
   'quotes_2022.pipelines.TextPipeline': 300,
    'quotes_2022.pipelines.MongoPipeline': 400,
}
MONGO_URI = 'localhost'
MONGO_DB = 'quotes_2022'

在ITEM_PIPELINES字典中,键名就是Pipeline类的名字,键值代表的事调用优先级(数字越小越早调用)。

在命令行中正式运行

scrapy crawl quotes

等爬取结束后,在MongoDB Compass中刷新一下,可以查看到爬取到的数据了。


目录

架构介绍:

数据流:

示例:

在命令行中创建项目框架:

在命令行中创建Spider:

在PyCharm中查看项目的文件结构

 在PyCharm中查看自定义的Spider文件(quotes.py)

创建Item

在PyCharm中查看items.py文件

解析response

在PyCharm中查看自定义的Spider文件

使用Item

后续Request

在命令行中试运行&保存到文件

使用Item Pipeline导出到MogonDB数据库

在PyCharm中查看pipelines.py文件

在PyCharm中查看settings.py文件

在命令行中正式运行


<完>

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值