前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据获取:认识Scrapy

数据获取:认识Scrapy

作者头像
马拉松程序员
发布2023-09-02 08:06:46
1990
发布2023-09-02 08:06:46
举报

本节介绍一个普通流程的爬虫框架——Scrapy,它提供了一个通用性的开发规范,帮助开发者做好了通用性的功能,只需要自定义发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容。在最后的实战项目中,我们将会使用Scrapy来做数据采集并进行深度的数据分析和可视化。

在Scrapy的官网上对它的介绍是:Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。Scrapy官网:https://scrapy.org/,这里可以查阅原始的官网文档。

学习一个框架,先了解框架的架构。在Scrapy官网给出了一张各个组件的数据流程图,在图中包含了Scrapy的各个核心组件。

Scrapy Engine(引擎)

Scrapy引擎负责数据在不同的模块中传递和流转,并在相应动作发生时触发事件。也可以说Engine是Scrapy的大脑。

Scheduler(调度器)

调度器是存放需要待爬取的URL。

Downloader(下载器)

从调度器中传过来的页面URL,下载器将负责获取页面数据并提供给引擎,而后把数据提供给spider。

Spiders(爬虫)

Spider是需要开发者自行编写用于分析下载器返回的内容,提取item(可以理解为每个页面中需要爬取的数据对象),并且会提起新的待爬取的url给Scheduler。每个spider负责处理一个特定网站,一个Scrapy可以有多个Spider。

Item Pipeline(实体管道)

Item Pipeline负责处理Spider中获取到的Item,并进行后期处理,比如详细分析、过滤、存储等等操作。

Downloader Middlewares(下载中间件)

一个可以自定义扩展下载功能的组件,比如你需要在爬取中配置代理,那么就可以编辑这个middleware

Spider Middlewares(Spider中间件)

Spider中间件是一个可以自定扩展和操作引擎和Spider中间通信的功能组件,比如进入Spider的Responses和从Spider出去的Requests,添加请求头的过滤或者某些属性的设置等。

在以上组件中,Scrapy Engine、Scheduler和Downloader已经由Scrapy完成,Spiders和Item Pipeline是需要开发者根据需要自行编写。

Scrapy运行流程大概如下:

1.Spiders把要处理的URL发给Engine

2.引擎把URL传给调度器进行Request请求排序入队

3.引擎从调度器中取出一个URL,封装成一个Request请求传给下载器

3.下载器把资源下载下来,并封装成应答包(Response)

4.Spiders进行解析Response

5.Spiders解析出的Item,则交给实体管道进行后续处理

6.Spiders解析出的URL,则把URL传给调度器等待抓取

创建Scrapy项目

1.安装Scrapy

Scrapy也可以使用pip来安装,也推荐使用此方式安装,安装命令

代码语言:javascript
复制
pip install Scrapy

默认是安装的最新版本,代码演示基于2.4.1版本,不同版本代码可能有差异,但是都大同小异。可以直接用最新版

2.创建项目

代码语言:javascript
复制
scrapy startproject mycrawler

注意:这是在终端里运行的命令,不是在py脚本中,下同

运行上述命令后即可创建一个scrapy的项目,运行日志:

代码语言:javascript
复制
New Scrapy project 'mycrawler', using template directory
'd:\develop\python\python39\lib\site-packages\scrapy\templates\project', created in:
E:\PycharmProjects\pythonProject\mycrawler
You can start your first spider with:
cd mycrawler
scrapy genspider example example.com

现在我们在E:\PycharmProjects\pythonProject 目录下创建了一个名叫mycrawler的scrapy项目,看看Scrapy项目下都有些什么。

scrapy.cfg: 项目的配置文件

mycrawler/: 该项目的python模块

mycrawler/items.py: 项目中的item文件,在此创建对象的容器的地方,爬取的信息分别放到不同容器里

mycrawler/middlewares.py:项目的middlewares文件,比如在这里可以设置请求代理

mycrawler/pipelines.py: 项目中的pipelines文件,这里定义数据管道,是保存还是分析

mycrawler/settings.py: 项目的设置文件,可以在此设置请求头,运行模式等等

mycrawler/spiders/: 放置spider代码的目录,这个目录下就是放爬虫的地方

接下来我们就创建爬虫,在创建项目的日志中也给出了提示:

You can start your first spider with:

cd mycrawler

scrapy genspider example example.com

3.创建爬虫

首先需要定义爬虫名称和需要爬取的域名,这里我们还是以豆瓣电影为例子,那么需要依次执行下面命令:

代码语言:javascript
复制
cd mycrawler
scrapy genspider douban douban.com

这是在mycrawler/spiders/目录下会生成一个douban.py的文件,内容如下(已经对自动生成的内容做了修改)

代码语言:javascript
复制
import scrapy
class DoubanSpider(scrapy.Spider):
# 爬虫的唯一标识,不能重复
name = 'douban'
#  限定爬取该域名下的网页
allowed_domains = ['douban.com']
#   开始爬取的链接,可以设置多个
start_urls = ['https://movie.douban.com/top250']
def parse(self, response):
# response就是返回体
print(response.status)

4.运行爬虫

这时已经算是实现了一个scrapy搭建的爬虫了,不过现在好像这个爬虫什么都不能做,我们先来启动一下,看看是否能正常启动。Scrapy启动命令, 命令中“douban”必须是在DoubanSpider类的name属性值相同。

代码语言:javascript
复制
scrapy crawl  douban

这时候启动后,部分日志内容如下:

2023-08-09 14:09:51 [scrapy.core.engine] DEBUG: Crawled (403)

<GET https://movie.douban.com/top250> (referer: None)

2023-08-09 14:09:51 [scrapy.spidermiddlewares.httperror] INFO: Ignoring response <403 https://movie.douban.com/top250>: HTTP status co

de is not handled or not allowed

2023-08-09 14:09:51 [scrapy.core.engine] INFO: Closing spider (finished)

当前爬虫运行状态是403,权限不足,这个问题在之前小节中有遇到过,具体原因也详细讲过了,不赘述。

我们需要在请求上设置请求头参数,那么在scrapy中如何设置请求头参数呢。在这里有两种方式可以设置。

1.在settings.py中有一个USER_AGENT参数,可以通过设置这个参数给请求添加请求头。设置方式更改配置文件:

代码语言:javascript
复制
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0)Gecko/20100101 Firefox/87.0'

2.在middlewares.py中间件文件中的MycrawlerDownloaderMiddleware类中有一个process_request()的方法,这里可以设置。

代码语言:javascript
复制
def process_request(self, request, spider):
    request.headers["User-Agent"] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:87.0) Gecko/20100101 Firefox/87.0'

如果使用方式2进行设置还需要在配置文件中声明,具体在下一小节中详细说明配置内容。这里先使用方式1修改,完成后重新运行启动命令,看到打印的部分日志如下

…..

2023-08-09 14:53:25 [scrapy.core.engine] DEBUG: Crawled (200)

<GET https://movie.douban.com/top250> (referer: None)

200

2023-08-09 14:53:25 [scrapy.core.engine] INFO: Closing spider (finished)

…..

这时scrapy爬虫已经成功运行。

Scrapy配置详解

BOT_NAME

在settings.py文件中是Scrapy项目的全局配置,需要根据自定义设置的参数,BOT_NAME生成时自带,也就是项目名称。

BOT_NAME = 'mycrawler'

ROBOTSTXT_OBEY

每个网站都会有robots.txt文件,关于robots.txt在4.1.4中已经说明过,这个参数的设置scrapy爬虫是否遵守robots.txt协议,默认是True

ROBOTSTXT_OBEY = True

CONCURRENT_REQUESTS

配置Scrapy最大并发请求数,默认是16。如果是大批量爬取数据,可以设置高并发数。在settings.py文件中是注释掉的,如果修改默认值,则需要解除注释,使之生效。

CONCURRENT_REQUESTS = 32

DOWNLOAD_DELAY

设置下载等待的时间,即下载同一个网站之前每个请求之前是否添加延迟,默认是0,单位是秒。

DOWNLOAD_DELAY = 3

打开此配置后Scrapy对联系同一个网站的请求之间添加3秒的延迟。

COOKIES_ENABLED

是否禁用cookie,默认是启用

DEFAULT_REQUEST_HEADERS

设置自定义请求头

DEFAULT_REQUEST_HEADERS = {

'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',

'Accept-Language': 'en',

}

SPIDER_MIDDLEWARES

启用或者禁用SPIDER中间件,启动则需要添加到下面参数值字典中

SPIDER_MIDDLEWARES = {

'mycrawler.middlewares.MycrawlerSpiderMiddleware': 543,

}

字典的key是中间件的类名,value是优先级或者说执行顺序。如果有多个执行顺序如图所示。Middleware1的value是100,Middleware2的value是200,请求从Engine到Spiders的时候,是先执行Middleware1的input(),在执行Middleware2的input(),返回的时候先执行Middleware2的output(),再执行Middleware1的output()。

DOWNLOADER_MIDDLEWARES

启用或者禁用下载中间件,启动则需要添加到下面参数值字典中。在上一小节中,如果使用中间件配置请求头,那么需要在这里设置开启,否则中间件将不生效。

DOWNLOADER_MIDDLEWARES = {

'mycrawler.middlewares.MycrawlerDownloaderMiddleware': 543,

}

ITEM_PIPELINES

配置项目管道,主要是用于后续清理、分析和保存数据。如果开启某些PIPELINES,需要在此进行声明配置。

ITEM_PIPELINES = {

'mycrawler.pipelines.MycrawlerPipeline': 300,

}

Scrapy保存数据

编写Item

在mycrawler目录下有一个items.py文件,item的定义为用于装载爬取到的数据的容器,这里需要给保存的对象以定义,用于储存爬虫抓取到的内容。在4.6.5小节中,我们已经定义了一个doubanMovie的类,这里可以直接复用doubanMovie类的属性,但是在Scrapy中与Python类的创建还有一点差别,详细参数如下。

代码语言:javascript
复制
import scrapy


class MycrawlerItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    # 电影名称
    name = scrapy.Field()
    # 上映年份
    year = scrapy.Field()
    # 导演
    directedBy = scrapy.Field()
    # 类型
    genre = scrapy.Field()
    # 评价分数
    rating_num = scrapy.Field()
    # 评价人数
    rating_people = scrapy.Field()
    # 制片国家/地区
    area = scrapy.Field()
    # 电影语言
    language = scrapy.Field()

MycrawlerItem类的属性为什么都设置为scrapy.Field()。Field对象 指明每个字段的元数据,对于接受的值没有任何限制。设置Field对象的主要目的是在一个地方定义好所有的元数据。打开源码查看下,发现Field类仅是内置字典类(dict)的一个别名,并没有提供额外的方法和属性。

class Field(dict):

"""Container of field metadata"""

完善DoubanSpider

在前面已经自动生成了的douban.py中创建了一个名为DoubanSpider的爬虫,现在我们根据之前写过的豆瓣电影的爬虫方法改造下,重写下DoubanSpider 类。

在Parse()方法中,主要是完成了两个操作:1.提取目标数据2.获取新的url。

代码语言:javascript
复制
import re
import scrapy
from bs4 import BeautifulSoup
from mycrawler.items import MycrawlerItem


class DoubanSpider(scrapy.Spider):
    # 爬虫的唯一标识,不能重复
    name = 'douban'
    # 限定爬取该域名下的网页
    allowed_domains = ['douban.com']
    # 开始爬取的链接,可以设置多个
    start_urls = ['']

    def parse(self, response):
        # response就是返回体
        for link in self.getDetailLinks(response):
            print(response.status)
            yield scrapy.Request(url=link, callback=self.getMovieDetail)

    def getDetailLinks(self, response):
        # 添加headers参数
        # 生成一个BeautifulSoup对象
        soup = BeautifulSoup(response.text, 'html.parser')
        # 搜索符合要求的标签
        links = soup.find_all('a', href=re.compile("^https://movie.douban.com/subject/"))
        linkList = set()
        for link in links:
            linkList.add(link['href'])
        linkList = list(linkList)
        return linkList

    # 爬取详情链接中信息,并且返回一个对象
    def getMovieDetail(self, response):
        # 生成一个BeautifulSoup对象
        soup = BeautifulSoup(response.text, 'html.parser')
        info = soup.select("#info")
        # 电影名称
        name = soup.find('span', attrs={"property": "v:itemreviewed"})
        name = name.string
        # 上映年份
        year = soup.select('.year')
        year = year[0].get_text().replace("(", "").replace(")", "")
        # 导演
        directedBy = soup.find('a', attrs={"rel": "v:directedBy"})
        directedBy = directedBy.string
        # 类型
        genre = soup.find('span', attrs={"property": "v:genre"})
        genre = genre.string
        # 分数
        rating_num = soup.find('strong', attrs={"property": "v:average"})
        rating_num = rating_num.string
        # 评价人数
        rating_people = soup.find('span', attrs={"property": "v:votes"})
        rating_people = rating_people.string

        infos = info[0].get_text().split("\n")
        area = ""
        language = ""
        for info in infos:
            if info.startswith("制片国家/地区"):
                area = info.replace("制片国家/地区:", "").replace(" ", "").split("/")[0]
            if info.startswith("语言"):
                language = info.replace("语言:", "").replace(" ", "").split("/")[0]
        item = MycrawlerItem()
        item['name'] = name
        item['year'] = year
        item['directedBy'] = directedBy
        item['genre'] = genre
        item['rating_num'] = rating_num
        item['rating_people'] = rating_people
        item['area'] = area
        item['language'] = language
        yield item

重写的DoubanSpider 类中getDetailLinks()和getMovieDetail()引用自之前数据获取小节中的内容。

start_urls属性值是开始爬取的url,这里是10页电影列表的url。

在parse()方法中用到了一个yield函数。scrapy框架会根据 yield 返回的实例类型来执行不同的操作。在这里通过 yield scrapy.Reques()来发起一个请求,并通过 callback 参数为这个请求添加回调函数,在请求完成之后会将响应作为参数传递给回调函数。相当于执行的操作是:从getDetailLinks()函数中获得电影详情链接,通过yield每一个详情链接发起请求,返回之后再执行callback函数。

这里的回调函数是getMovieDetail(),也是我们自定义的,用于处理电影的信息。而在这里的yield item,scrapy框架会将这个对象传递给 pipelines.py做进一步处理。

完善pipelines

在DoubanSpider类中,parse()最终会通过yield item,把每一个电影信息的对象(实际上是一个封装的字典)传递给pipelines.py中的MycrawlerPipeline类下的process_item(self, item, spider)函数进行后续处理,进行数据分析或者储存等操作。

代码语言:javascript
复制
from itemadapter import ItemAdapter


class MycrawlerPipeline:
    def process_item(self, item, spider):
        print(item)
        saveData2MySQL(item)
        return item


def saveData2MySQL(movie):
    # -- coding: utf-8 --
    # 导入pymysql模块
    import pymysql
    # 连接database
    conn = pymysql.connect(
        host="101.37.124.133",
        port=3306,
        user="root",
        password="Adong@123.",
        database="datatest",
    )
    # 得到游标
    cursor = conn.cursor()
    # 定义要执行的SQL语句
    sql = 'insert into movies(name,year,directedBy,genre, rating_num,rating_people, area,language) values (%s,%s,%s,%s,%s,%s,%s,%s)'

    value = [movie['name'], movie['year'], movie['directedBy'], movie['genre'], movie['rating_num'],
             movie['rating_people'], movie['area'], movie['language']]
    cursor.execute(sql, value)
    # 关闭光标对象
    cursor.close()
    # 提交数据,否则并没有持久化到数据库
    conn.commit()
    # 关闭数据库连接
    conn.close()

在process_item进行了一步操作就是储存到数据库中。这样在严格意义上的一个scrapy爬虫已经做好了。

再次启动scrapy爬虫,既可以进行数据抓取。

代码语言:javascript
复制
scrapy crawl  douban

本章主要是介绍了scrapy框架的基本知识和使用,可以看出scrapy还是比较容易上手的,它只是帮助开发者实现了通用的功能,在具体的页面(网站)的爬取上还是需要开发者自行编写。熟悉scrapy之后,我们将在实战运行中使用它。后面将会涉及在scrapy中如何配置代理以及如何使用shell脚本启动scrapy和监控scrapy的状态。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 马拉松程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建Scrapy项目
  • Scrapy配置详解
  • Scrapy保存数据
    • 编写Item
      • 完善DoubanSpider
        • 完善pipelines
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档