超详细的Scrapy框架的基本使用教程

Scrapy的介绍

scrapy的工作流程(重点!!!)

如下图所示:

爬虫

  1. 负责向引擎提供要爬取网页的URL,引擎会把这个URL封装成request对象并传递给调度器,
  2. 把引擎传递过来的response对象进行数据解析。数据解析有两种结果:
    1. 解析出具体的数据,那么通过引擎把这个具体的数据传递给管道,然后存入文件、数据库等
    2. 解析出一个新的URL,那么过程同作用1

管道:负责把引擎传递过来的数据进行存储,存入文件、数据库等。管道可以有多个,比如MySQL的管道,某个文件的管道,mango的管道等。

调度器:可以把调度器的存储结构看成一个优先队列,不同的request对象可能优先级不一样,按优先级的高低进行调度

  1. 把引擎传递过来的request对象放入队列进行排队,调度器可以实现去重的效果,即对两个相同的URL,只存储一个
  2. 向引擎提供队头的request对象(即优先级高的request对象),引擎把这个request对象传递给下载器进行请求

下载器:把引擎传递过来的request对象发送给服务器请求数据,并把服务器返回的内容封装成response对象, 然后把这个response对象传递给引擎,引擎再把这个response对象传递给爬虫进行数据解析

引擎:从上面的流程中可以看到,引擎负责控制数据流在所有组件流动,并在相应动作时触发事件,相当于爬虫的大脑

注意,在实际的代码编写过程中,我们只需要关注爬虫和管道部分的代码编写,而引擎、调度器、下载器都不需要我们实现   

scrapy的安装

在终端输入以下命令(包有点大,建议切换成国内的镜像源,如清华源等,据说安装可能会有很多问题,但是可能我人品比较好,没遇到,嘿嘿。如果有问题的百度一下吧)

pip install scrapy 

scrapy的基本使用

以爬取4399游戏网站的游戏名称为例,scrapy有以下几个步骤:

1、创建scrapy项目

首先在某个文件夹下打开终端,输入以下命令创建scrapy项目

scrapy startproject 项目名称

2、创建一个爬虫程序 

首先进入项目文件夹下,然后输入命令: 

scrapy genspider 爬虫程序的名称  要爬取网站的域名

3、编写爬虫程序

在game_4399.py文件中编写爬虫代码,代码如下

import scrapyclass Game4399Spider(scrapy.Spider):name = "game_4399"  # 爬虫程序的名称allowed_domains = ["4399.com"]  # 允许爬取的域名# 默认情况下是:https://4399.com# 但是我们不从首页开始爬取,所以改一下URLstart_urls = ["https://4399.com/flash/"]  # 一开始爬取的URLdef parse(self, response):  # 该方法用于对response对象进行数据解析# print(response)  # <200 http://www.4399.com/flash/># print(response.text)  # 打印页面源代码# response.xpath()  # 通过xpath解析数据# response.css()  # 通过css解析数据# 获取4399小游戏的游戏名称# txt = response.xpath('//ul[@class="n-game cf"]/li/a/b/text()')# txt 列表中的每一项是一个Selector:# <Selector query='//ul[@class="n-game cf"]/li/a/b/text()' data='逃离克莱蒙特城堡'>]# 要通过extract()方法拿到data中的内容# print(txt)# txt = response.xpath('//ul[@class="n-game cf"]/li/a/b/text()').extract()# print(txt)  # 此时列表中的元素才是游戏的名字# 也可以先拿到每个li,然后再提取名字lis = response.xpath('//ul[@class="n-game cf"]/li')for li in lis:# name = li.xpath('./a/b/text()').extract()# # name 是一个列表# print(name)  # ['王城霸业']# 一般我们都会这么写:li.xpath('./a/b/text()').extract()[0]# 但是这样如果列表为空就会报错,所以换另一种写法# extract_first方法取列表中的第一个,如果列表为空,返回Nonename = li.xpath('./a/b/text()').extract_first()print(name)  # 王城霸业category = li.xpath('./em/a/text()').extract_first()  # 游戏类别date = li.xpath('./em/text()').extract_first()  # 日期print(category, date)# 通过yield向管道传输数据dic = {'name': name,'category': category,'date': date}# 可以认为这里是把数据返回给了管道pipeline,# 但是实际上是先给引擎,然后引擎再给管道,只是这个过程不用我们关心,scrapy会自动完成# 这里的数据会在管道程序中接收到yield dic

4、运行scrapy爬虫程序

在终端输入命令,就可以看到爬虫程序运行结果。

scrapy crawl 爬虫程序名称

5、总结scrapy的基本使用

关于第6、7步,在下面的scrapy管道中会说到。

Scrapy中的管道

基本介绍

我们接着看上述4399中创建的scrapy项目,管道的默认情况如下:

管道程序默认是不生效的,需要在settings文件进行配置,如下:

pipelines.py文件中的代码

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html# useful for handling different item types with a single interface
from itemadapter import ItemAdapter# 默认情况下管道是不开启的,需要在settings文件中进行设置
class GamePipeline:def process_item(self, item, spider):"""接收爬虫通过引擎传递过来的数据:param item: 具体的数据内容:param spider: 对应传递数据的爬虫程序:return:"""print(item)  # {'name': '武器升级', 'category': '休闲类', 'date': '2024-01-06'}print(spider)  # <Game4399Spider 'game_4399' at 0x22867dafc70>return item  # 把数据传递给下一个管道

 settings文件中关于管道的代码

ITEM_PIPELINES = {# 管道程序的所在路径:优先级# 300 表示管道的优先级,数字越小优先级越高# 优先级高的管道会比优先级低的管道先拿到数据"game.pipelines.GamePipeline": 300,
}

运行命令:scrapy crawl game_4399,运行结果如下(只截取了一部分):

上述只有一个管道,如果有多个管道,比如我们自定义一个管道,代码如下:

from itemadapter import ItemAdapter# 默认情况下管道是不开启的,需要在settings文件中进行设置
class GamePipeline:def process_item(self, item, spider):"""接收爬虫通过引擎传递过来的数据:param item: 具体的数据内容:param spider: 对应传递数据的爬虫程序:return:"""print(item)  # {'name': '武器升级', 'category': '休闲类', 'date': '2024-01-06'}print(spider)  # <Game4399Spider 'game_4399' at 0x22867dafc70>return item  # 把数据传递给下一个管道# 自定义一个管道程序
# 记得在settings文件中配置,否则不生效
class OtherPipeline:def process_item(self, item, spider):# 比如这里给传递过来的数据添加一个新的字段item['new_field'] = 'hello'return item

settings文件关于管道的代码

ITEM_PIPELINES = {# 管道程序的所在路径:优先级# 300 表示管道的优先级,数字越小优先级越高# 优先级高的管道会比优先级低的管道先拿到数据"game.pipelines.GamePipeline": 300,# 优先级比GamePipeline高,可以通过运行结果看出"game.pipelines.OtherPipeline": 299
}

 运行结果如下,可见多了一个字段

 scrapy中的数据格式item

在上述的项目中,我们在爬虫程序里解析出来的数据组装成了字典然后使用yield传递给了管道,但是这实际上是不符合scrapy的规范的。在scrapy中,数据用item表示。

还是以上面的4399为例。game_4399.py中的代码如下:

import scrapyfrom game.items import GameItemclass Game4399Spider(scrapy.Spider):name = "game_4399"  # 爬虫程序的名称allowed_domains = ["4399.com"]  # 允许爬取的域名# 默认情况下是:https://4399.com# 但是我们不从首页开始爬取,所以改一下URLstart_urls = ["https://4399.com/flash/"]  # 一开始爬取的URLdef parse(self, response):  # 该方法用于对response对象进行数据解析# 也可以先拿到每个li,然后再提取名字lis = response.xpath('//ul[@class="n-game cf"]/li')for li in lis:name = li.xpath('./a/b/text()').extract_first()category = li.xpath('./em/a/text()').extract_first()  # 游戏类别date = li.xpath('./em/text()').extract_first()  # 日期# 通过yield向管道传输数据# dic = {#     'name': name,#     'category': category,#     'date': date# }# 可以认为这里是把数据返回给了管道pipeline,# 但是实际上是先给引擎,然后引擎再给管道,只是这个过程不用我们关心,scrapy会自动完成# 如果只有一个数据,可以通过return返回,但是在scrapy中没人使用return,都是用yield的# 另外,在scrapy中,只希望yield返回三个类型之一的数据:item、request、None# 这里可以yield dic 返回字典,但是实际上并不希望这么干# 而且如果换成了 yield [] 返回雷暴,就会报错:# ERROR: Spider must return request, item, or None, got 'list'# yield dic# 我们现在不返回字典,而是返回真正推荐我们返回的格式之一:item# 先导入GameItem类:from game.items import GameItem# 然后创建它的实例,使用起来和字典类似# 区别就是GameItem类里没有定义的字段,就不能使用,比如不能item['某个没有定义的字段']item = GameItem()# item['xxx'] 里的xxx要在类GameItem里定义有,否则就会报错item['name'] = nameitem['category'] = categoryitem['date'] = dateyield item

 items.py中的代码

import scrapyclass GameItem(scrapy.Item):# 这里定义了三个字段,分别表示游戏的名称、类别和日期name = scrapy.Field()category = scrapy.Field()date = scrapy.Field()# 可以定义其他字段来表示不同的信息
class OtherItem(scrapy.Item):pass

管道程序pipelines.py中的代码

from itemadapter import ItemAdapter# 默认情况下管道是不开启的,需要在settings文件中进行设置
class GamePipeline:def process_item(self, item, spider):"""接收爬虫通过引擎传递过来的数据:param item: 具体的数据内容:param spider: 对应传递数据的爬虫程序:return:"""print(item)  # {'name': '武器升级', 'category': '休闲类', 'date': '2024-01-06'}print(spider)  # <Game4399Spider 'game_4399' at 0x22867dafc70>return item  # 把数据传递给下一个管道# 自定义一个管道程序
# 记得在settings文件中配置,否则不生效
class OtherPipeline:def process_item(self, item, spider):# 比如这里给传递过来的数据添加一个新的字段# 此时传递过来的item不再是字典,而是GameItem类对象# 由于GameItem类里没有定义字段new_field,所以不能使用,否则报错# item['new_field'] = 'hello'return item

数据存储

我们一直说通过管道存储数据,但是上面的例子一直未涉及,现在来讲解怎么把数据进行持久化存储。在上面的例子中,我们已经在管道程序里拿到了引擎传递过来的数据,现在就可以把这些数据存储起来。

首先,先来说一说数据存储的几种方案:

  • 存入.csv文件,这类数据一般用于数据分析
  • 存入MySQL数据库
  • 存入mangodb数据库
  • 写入文件,如图片、视频、文字等数据

下面以存入csv文件为例(存入MySQL的也列举了一个模板,mango数据库的操作和MySQL基本一致,但是由于我对mango不熟悉,所以不写了,需要的可以百度一下)

settings文件中的代码

ITEM_PIPELINES = {# 管道程序的所在路径:优先级# 300 表示管道的优先级,数字越小优先级越高# 优先级高的管道会比优先级低的管道先拿到数据"game.pipelines.GamePipeline": 300,"game.pipelines.GameMySqlPipeline": 300,# 优先级比GamePipeline高,可以通过运行结果看出"game.pipelines.OtherPipeline": 299
}
# 配置MySQL
MYSQL = {"host": "localhost",  # 主机"port": 3306,  # 端口"user": "xxx",  # 用户名"password": "xxx",  # 密码"database": "xxx"  # 数据库名称
}

pipelines.py的代码:

import pymysql
# 导入MySQL配置
from game.settings import MYSQL# 默认情况下管道是不开启的,需要在settings文件中进行设置
class GamePipeline:def __init__(self):self.f = Nonedef open_spider(self, spider):""""""print('爬虫开始了...')self.f = open('./game_data.csv', mode='a', encoding='utf-8')def process_item(self, item, spider):"""接收爬虫通过引擎传递过来的数据:param item: 具体的数据内容:param spider: 对应传递数据的爬虫程序:return:"""print('爬虫进行中...')# 把数据写入文件# 写入模式是mode='a',表示在文件里追加,不能是w,否则文件原本的内容会被覆盖# 以下的这种方式效率不高,因为每传递一次数据,就要进行一次文件的打开的关闭操作# with open('./game_data.csv', mode='a', encoding='utf-8') as f:#     f.write(f'{item["category"]}, {item["name"]}, {item["date"]}\n')# 采取另一种方式# scrapy 提供了两个方法open_spider()、close_spider(),分别会在爬虫开始时和爬虫结束后调用self.f.write(f'{item["category"]}, {item["name"]}, {item["date"]}\n')return item  # 把数据传递给下一个管道def close_spider(self, spider):print('爬虫结束了...')if self.f:self.f.close()# 默认情况下管道是不开启的,需要在settings文件中进行设置
class GameMySqlPipeline:def __init__(self):self.conn = Nonedef open_spider(self, spider):print('爬虫开始了...')# self.conn = pymysql.connect(  # 创建数据库连接#     host='localhost',  # 主机#     port=3306,  # 端口#     user='xxx',  # 用户名#     password='xxx',  # 密码#     database='xxx'  # 数据库名称# )# 可以向上面那样写,但是更好的办法是写在settings文件中# 然后从settings文件中导入:from game.settings import MYSQLself.conn = pymysql.connect(  # 创建数据库连接host=MYSQL['host'],  # 主机port=MYSQL['port'],  # 端口user=MYSQL['user'],  # 用户名password=MYSQL['password'],  # 密码database=MYSQL['database']  # 数据库名称)def process_item(self, item, spider):"""接收爬虫通过引擎传递过来的数据:param item: 具体的数据内容:param spider: 对应传递数据的爬虫程序:return:"""print('爬虫进行中...')# 把数据写入mysql数据库# 下载数据库包并导入:pip install pymysql# 确定自己的数据库中准备好了相应的数据表try:cursor = self.conn.cursor()# 插入的sql语句# (%s, %s, %s) 对应相应的字段类型,%s表示字符串类型insert_sql = 'insert into 数据库表名 (字段1, 字段2, 字段3, ...) values (%s, %s, %s)'# execute()的第二个参数是一个元祖,里面的每一个元素对应sql语句中的字段值cursor.execute(insert_sql, (item['category'], item['name'], item['date']))self.conn.commit()  # 提交事务except:self.conn.rollback()  # 出现异常,执行回滚操作finally:if cursor:cursor.close()return item  # 把数据传递给下一个管道def close_spider(self, spider):print('爬虫结束了...')if self.conn:self.conn.close()# 自定义一个管道程序
# 记得在settings文件中配置,否则不生效
class OtherPipeline:def process_item(self, item, spider):# 比如这里给传递过来的数据添加一个新的字段# 此时传递过来的item不再是字典,而是GameItem类对象# 由于GameItem类里没有定义字段new_field,所以不能使用,否则报错# item['new_field'] = 'hello'return item

结尾

关于scrapy的基本使用,好像还有中间件这个内容,但是我看的那个视频教程这个部分好像漏掉了,反正就是没有笔记,需要了解的自行百度一下吧,或者看官方文档也行。

本人也是初学者,所以文章中有什么错误的地方,欢迎指正。

有一个对应的综合练习:scrapy框架爬取图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/722026.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安卓app软件开发的费用

我们公司总结的开发价格根据安卓APP&#xff0c;苹果APP行业的报价&#xff0c;开发的APP软件费用主要受到两个方面的影响。安卓和苹果APP软件应用的复杂性&#xff0c;第二个是定制开发的APP软件&#xff0c;开发成本和人员的投入成本&#xff0c;以下就是不同的报价费用是怎么…

面试经典150题【51-60】

文章目录 面试经典150题【51-60】71.简化路径155.最小栈150.逆波兰表达式求值224.基本计算器141.环形链表2.两数相加21.合并两个有序链表138.随机链表的复制19.删除链表的倒数第N个节点82.删除链表中的重复元素II 面试经典150题【51-60】 71.简化路径 先用split(“/”)分开。然…

四平方和 刷题笔记

/* 四平方和 直接暴力搜索 可能会超时 使用二分辅助搜索 先枚举出 c*cd*d并存入数组 用式子算出 a*ab*b还剩下多少查找sum数组里面是否存在符合条件的数 查找方式使用二分搜索 当逼近答案后 检查一下是否为所需的数 如果是 直接输出 */ #include <cstring> #includ…

rabbitmq基础(1)

1、背景 能实现消息队列的框架软件有很多&#xff0c;kafka、rabbitmq、RocketMq、activeMq、Redis&#xff08;非专业&#xff09;&#xff0c;各有各的特点和优缺点。但是之前的公司数据需求规模并非很大&#xff0c;所以采用rabbitmq作为消息队列。 2、rabbitMq的基础架构…

C++面试宝典【配文档,全方面学习】

原word文档[链接: https://pan.baidu.com/s/1CKnm7vHDmHSDskAgxgZgKA?pwdr4wv 提取码: r4wv 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 --来自百度网盘超级会员v5的分享] 一、C / C基础 1、简述C的内存分区&#xff1f; 一个C、C程序的内存分区主要有5个…

使用html网页播放多个视频的几种方法

前言 因为项目测试需要&#xff0c;我需要可以快速知道自己推流的多路视频流质量&#xff0c;于是我想到可以使用html网页来播放视频&#xff0c;实现效果极其简单&#xff0c;方法有好几种&#xff0c;以下是几种记录&#xff1a; 注意&#xff1a;测试过&#xff0c;VLC需要使…

Ubuntu18.04运行ORB-SLAM3

ORB-SLAM3复现(ubuntu18) 文章目录 ORB-SLAM3复现(ubuntu18)1 坐标系与外参Intrinsic parameters2 内参Intrinsic parameters2.1 相机内参① 针孔模型Pinhole② KannalaBrandt8模型③ Rectified相机 2.2 IMU内参 3 VI标定—外参3.1 Visual calibration3.2 Inertial calibration…

STM32类别概述、下载程序及启动过程分析

STM32类别概述、下载程序及启动过程分析 STM32类别STM32下载程序STM32启动过程分析 STM32类别 STM32 目前总共有 5 大类&#xff0c;18 个系列 结合 STM32F1 的芯片来说&#xff0c;其 CMSIS 应用程序的简单结构框图&#xff0c;不包括实时操作系统和 中间设备等组件&#xf…

find函数-秒了道题

秒了 笑死 还是规规矩矩做吧 string类的find()函数用于在字符串中查找字符或子串&#xff0c;返回第一个匹配的位置。 class Solution { public:int strStr(string haystack, string needle) {return haystack.find(needle);} };

深入理解操作系统Operator System(1)

目录 OS概念 设计OS的目的 OS定位 操作系统对下的结构层次示意图 理解操作系统的"管理"❗❗ "管理"被管理者的数据 怎么获取被管理者的数据 获取被管理者什么"数据" 数据过多&先描述再组织❗ C/C中的体现 解释OS对硬件的"管…

RabbitMQ(控制台模拟收发消息与数据隔离)

1.RabbitMQ架构图 publisher&#xff1a;生产者&#xff0c;也就是发送消息的一方 consumer&#xff1a;消费者&#xff0c;也就是消费消息的一方 queue&#xff1a;队列&#xff0c;存储消息。生产者投递的消息会暂存在消息队列中&#xff0c;等待消费者处理 exchange&…

深度解析速卖通商品详情API:Python实战与高级技术探讨

速卖通商品详情API接口实战&#xff1a;Python代码示例 一、准备工作 在开始之前&#xff0c;请确保你已经完成了以下步骤&#xff1a; 在速卖通开放平台注册账号并创建应用&#xff0c;获取API密钥。阅读速卖通商品详情API接口的文档&#xff0c;了解接口的使用方法和参数要…

什么是物联网?物联网如何工作?

物联网到底是什么&#xff1f; 物联网(Internet of Things&#xff0c;IoT)的概念最早于1999年被提出&#xff0c;官方解释为“万物相连的互联网”&#xff0c;是在互联网基础上延伸和扩展&#xff0c;将各种信息传感设备与网络结合起来而形成的一个巨大网络&#xff0c;可以实…

[SpringCloud] OpenFeign核心架构原理 (一)

Feign的本质: 动态代理 七大核心组件 Feign底层是基于JDK动态代理来的, Feign.builder()最终构造的是一个代理对象, Feign在构建对象的时候会解析方法上的注解和参数, 获取Http请求需要用到基本参数以及和这些参数和方法参数的对应关系。然后发送Http请求, 获取响应, 再根据响…

Python Web开发记录 Day6:MySQL(关系型数据库)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 六、MySQL1、MySQL-概述和引入①MySQL是什么&am…

liunx安装jdk、redis、nginx

jdk安装 下载jdk,解压。 sudo tar -zxvf /usr/local/jdk-8u321-linux-x64.tar.gz -C /usr/local/ 在/etc/profile文件中的&#xff0c;我们只需要编辑一下&#xff0c;在文件的最后加上java变量的有关配置&#xff08;其他内容不要动&#xff09;。 export JAVA_HOME/usr/l…

docker部署aria2-pro

前言 我平时有一些下载视频和一些资源文件的需求&#xff0c;有时候需要离线下载&#xff0c;也要速度比较快的方式 之前我是用家里的玩客云绝育之后不再写盘当下载机用的&#xff0c;但是限制很多 我发现了aria2 这个下载器非常适合我&#xff0c;而有个大佬又在原来的基础…

10 OpenCV 形态学的应用

文章目录 算子形态学提取直线示例 算子 adaptiveThreshold 二值化算子 adaptiveThreshold(src, dstNone,maxValue, adaptiveMethod, thresholdType, blockSize, C, ) /* *src&#xff1a;灰度化的图片 *dst&#xff1a;输出图像&#xff0c;可选 *maxValue&#xff1a;满足条件…

C#中对象的相等性与同一性的判断方法总结

C#对象的相等性与同一性 1. 概述与准备1.1 概述1.2 准备 2. Equals(Object)2.1 功能&#xff1a;2.2 实例&#xff1a;2.3 扩展&#xff1a;2.4 重写此方法 3. Equals(Object, Object)3.1 功能3.2 实例 4. ReferenceEquals(Object, Object)4.1 功能4.2 使用场景&#xff1a;4.3…

数据结构学习(四)高级数据结构

高级数据结构 1. 概念 之所以称它们为高级的数据结构&#xff0c;是因为它们的实现要比那些常用的数据结构要复杂很多&#xff0c;能够让我们在处理复杂问题的过程中&#xff0c; 多拥有一把利器&#xff0c;同时掌握好它们的性质&#xff0c;以及所适应的场合&#xff0c;在…