Scrapy框架的基本使用
Scrapy框架入门
一、Scrapy架构展示
1.数据处理流程
- 引擎询问蜘蛛需要处理那个网站,并让蜘蛛将第一个需要处理的
URL
交给它 - 引擎让调度器将需要处理的
URL
放在队列中 - 引擎从调度那获取接下来进行爬取的页面
- 调度器将下一个爬取的
URL
返回给引擎,引擎将它通过下载中间件发送到下载器 - 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎,如果下载失败了,引擎会通知调度器记录这个
URL
,待会再重新下载 - 蜘蛛收到下载器的响应并返回爬取到的数据条目,此外还要将需要跟进的新的
URL
发送给引擎 - 引擎将抓取到的数据条目送入数据管道,把新的
URL
发送给调度器放入队列中
上述操作的第2到第8步会一直重复知道调度器中没有需要请求的URL
,爬虫就停止工作
2.准备工作
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy
注:如果安装不成功你需要自己找其他的安装教程,当你在终端输入
scrapy version
能出来对应的版本号就说明你的Scrapy
安装成功了
二、实战案例:基于Scrapy实现一个爬取豆瓣电影 Top250 电影标题、评分和金句等信息的爬虫
创建项目
在你的工程文件下打开终端,输入下面这条命令,创建一个Scrapy
项目,项目文件可以直接用scrapy
命令生成
scrapy startproject DoubanProject
工程目录如下所示
DoubanProject
|____ DoubanProject
|________ spiders
|____________ __init__.py
|________ __init__.py
|________ items.py
|________ middlewares.py
|________ pipelines.py
|________ settings.py
|____ scrapy.cfg
1.创建你自己的 Spider
Spider
是自己定义的类,Scrapy
用它来从网页里抓取内容,并解析抓取的结果
在终端执行下面两行命令,
cd DoubanProject # 进入刚才创建的 DoubanProject 文件夹
scrapy genspider douban movie.douban.com
执行完后spider
文件夹中多了一个douban.py
文件
import scrapy
class DoubanSpider(scrapy.Spider):
# 每个项目唯一的名字,用来区分不同的Spider
name = "douban"
# 允许爬取的域名:用来限定start_urls列表中哪些 url 可以进行请求发送
allowed_domains = ["movie.douban.com"]
# 起始的 url 列表:该列表中存放的 url 会被 scrapy 自动进行请求的发送
start_urls = ["https://movie.douban.com/top250"]
# 用作于数据解析:response参数表示的就是请求成功后的响应对象
# 当 Response 没有指定回调函数时,该方法会默认被调用,它负责处理 Response 并从中提取想要的数据和下一步的请求
def parse(self, response):
pass
2.创建 Item
Item
是保存爬取数据的容器,它的使用方法和字典类似,创建Item
需要继承scrapy.Item
类,并且定义为scrapy.Field
的字段
items.py
import scrapy
class DoubanprojectItem(scrapy.Item):
# define the fields for your item here like:
rank = scrapy.Field() # 电影排名
movie_name = scrapy.Field() # 电影名称
movie_introduction = scrapy.Field() # 电影简介
picture = scrapy.Field() # 电影海报
movie_rating = scrapy.Field() # 电影评分
evaluators = scrapy.Field() # 观影人数
3.解析 Response
定义好数据结构以后,接下来我们再接着对douban.py
中的我们自己的DoubanSpider
类中的 parse
方法进行详细介绍,博主在其他文章中介绍过利用Beautiful Soup
,Xpath
以及正则表达式来提取网页数据,不过Scrapy
提供了自己的数据提取方法即基于lxml
构建的Selector(选择器)
,同样支持Xpath
选择器、CSS
选择器、正则表达式
parse()
方法的参数response
是start_url
里面的链接爬取后的结果,所以在parse()
方法中,我们可以直接对response
变量包含的内容进行解析,比如浏览请求结果的网页源代码或者进一步分析源代码内容或者找出结果中的链接而得到下一个请求,提取的方式可以是CSS选择器
或Xpath选择器
,我们在这里是用Selector
中的Xpath
来解析数据,大家对其他解析方式感兴趣可以自行了解
douban.py
import scrapy
from scrapy import Selector, Request
from ..items import DoubanprojectItem
class DoubanSpider(scrapy.Spider):
name = "douban"
allowed_domains = ["movie.douban.com"]
start_urls = ["https://movie.douban.com/top250"]
def parse(self, response):
sel = Selector(response)
movie_items = sel.xpath('//div[@class="article"]//ol[@class="grid_view"]/li')
for movie in movie_items:
item = DoubanprojectItem()
item['rank'] = movie.xpath('.//div[@class="pic"]/em/text()').extract_first() # 电影排名
item['movie_name'] = movie.xpath('.//div[@class="hd"]//span[1]/text()').extract_first() # 电影名称
item['movie_introduction'] = '无' if not movie.xpath('.//div[@class="bd"]//span[@class="inq"]/text()') \
else movie.xpath('.//div[@class="bd"]//span[@class="inq"]/text()').extract_first() # 电影简介
item['picture'] = movie.xpath('.//div[@class="pic"]/a/img/@src').extract_first() # 电影海报链接
item['movie_rating'] = movie.xpath('.//div[@class="star"]/span[2]/text()').extract_first() # 电影评分
item['evaluators'] = movie.xpath('.//div[@class="star"]/span[4]/text()').extract_first() # 评价人数
yield item
nextLink = sel.xpath('//span[@class="next"]/link/@href').extract()
if nextLink:
next_page_url = nextLink[0]
yield Request(url=response.urljoin(next_page_url), callback=self.parse)
Spider的用法
- 定义爬取网站的动作
- 分析爬取下来的网页
对Spider
类来说,整个爬取循环过程如下所示
- 以初始的
URL
初始化Request
并设置回调函数,当该Request
请求成功并返回时,Response
生成并作为参数传给该回调函数 - 在回调函数内分析返回的网页内容,一种解析到有效结果返回字典或者
Item
对象,另一种解析得到下一个连接,利用此连接构造Request
并设置新的回调函数,返回Request
等待后序调度
基础属性
常用方法
-
start_requests()
: -
parse()
:当Response
没有指定回调函数时,该方法会默认被调用负责处理Response
并从中取出想要的数据和下一步的请求,返回一个包含Request
或Item
的可迭代对象
后序Request
我们已经能够实现从初始页面中抓取内容,那么下一页的内容该如何抓取,我们需要从当前的页面中找到信息来生成下一个请求,然后在下一个请求的页面里找到信息再构造下一个请求,这样循环往复迭代从而实现整站的爬取
构造请求时需要用到scrapy.Request
,传递两个参数—url
和callback
- url:请求连接
- callback:回调函数,当指定该回调函数的请求完成之后获取到响应,引擎会将该响应作为参数传递给这个回调函数
我们在items.py
文件中定义了Item
,Item
可以理解为一个字典,在声明的时候需要实例化
4. Item Pipeline的用法
4.1 Item Pipeline
的主要功能
- 清理HTML数据
- 验证爬取数据,检查爬取字段
- 查重并丢弃重复内容
- 将爬取结果保存到数据库
4.2 基于管道持久化存储操作的流程
- 数据解析
- 在
item
类中定义相关的字段属性 - 将解析的数据封装存储到
item
类型的对象 - 将
item
类型的对象提交给管道进行持久化存储的操作 - 在管道类的
process_item
中将其接受到的item
对象中存储的数据进行持久化存储的操作 - 在配置文件
settings
中开启管道
4.3 核心方法介绍
- 必须要实现的方法:
process_item(item, spider)
功能:进行数据处理或者将数据写入到数据库或文件等操作
def process_item(self, item, spider):
"""
该方法每接收到一个item就会被调用一次
:param item: 即被处理的 Item 对象
:param spider: 生成该 Item 的 Spider 对象
:return Item对象: 此 Item 对象会被低优先级的 Item Pipeline 的 process_item()方法处理
:or return DropItem异常: 此 Item 被丢弃,不再进行处理
"""
return item
注意: 爬虫文件
douban.py
提交的item
只会传给管道文件pipelines.py
中第一个被执行的管道类接受,而pipelines.py
中可以有多个管道类,故process_item
中的return item
表示将item
传递给下一个即将被执行的管道类
-
open_spider(self, spider)
在
Spider
开启的时候被自动调用的,故可以在这个方法内做一些初始化操作(开启数据库连接等),参数spider
即被开启的Spdier
对象
-
close_spider(self, spider)
在
Spider
对象被关闭的时候自动调用的,故可以在这个方法内做一些收尾操作(关闭数据库连接等),参数spider
即被关闭的Spdier
对象
-
from_crawler(cls, crawler)
类方法,用
@classmethod
标识,是一种依赖注入的方式,通过crawler对象可以拿到Scrapy的所有核心组件,如全局配置的每个信息,然后创建一个Pipeline实例,参数cls就是Class,拿到配置信息之后返回类对象即可,这个方法的定义主要是用来获取settings.py
中的配置的
注:setting.py
:在配置文件中打开ITEM_PIPELINES
的相关配置,键名是Pipeline
的类名称,键值是调用优先级,是一个数字,数字越小对应的Pipeline
越先被调用
ITEM_PIPELINES = {
"DoubanProject.pipelines.DoubanprojectPipeline": 300, # 300表示的是优先级,数值越小优先级越高
}
4.4 实战案例代码: pipelines.py
case1: 将数据保存到excel
文件
import openpyxl
from .items import DoubanprojectItem
class DoubanprojectPipeline:
def __init__(self):
self.wb = openpyxl.Workbook()
self.sheet = self.wb.active
self.sheet.title = '豆瓣电影Top250'
self.sheet.append(('电影排名', '电影名称', '电影简介', '电影海报', '电影评分', '观影人数'))
def open_spider(self, spider):
print('开始爬虫...')
def process_item(self, item: DoubanprojectItem, spider):
"""
:param item:
:param spider:
:return:
"""
self.sheet.append((item['rank'], item['movie_name'], item['movie_introduction'], item['picture'],
item['movie_rating'], item['evaluators']))
return item
def close_spider(self, spider):
print("爬虫结束....")
self.wb.save('豆瓣电影数据.xlsx')
在你的终端命令行输入scrapy crawl douban
后按下回车,等待scrapy
执行完毕,在你的工程目录下会出现一个豆瓣电影数据.xlsx
文件,用wps
打开即可以看到下面这个效果
部分结果展示
case2: 将数据保存到MySQL
中
要将数据保存至MySQL
数据库,首先要安装MySQL,关于MySQL
的安装大家可以参考网上其他教程,能够出现下面这个图片说明安装成功了
- 在MySQL中创建数据库(确定你的MySql版本)
create database douban_movie; # mysql8.0默认就是utf8mb4;
create database douban_movie default charset=utf8mb4; # mysql8.0之前的版本
- 博主这里创建的数据库名称为
douban_movie
,切换到你创建的数据库
use douban_movie;
- 在你的数据库下建表
CREATE TABLE movie_data(
rank varchar(20),
movie_name varchar(30),
movie_introduction varchar(100),
picture varchar(100),
movie_rating varchar(100),
evaluators varchar(100)
)character set=utf8;
pipelines.py
文件中的代码
import pymysql
from .items import DoubanprojectItem
# piplines.py中的一个管道类对应将一组数据存储到一个平台或者载体中
class MysqlPipeline:
def __init__(self, host, database, user, password, port):
self.host = host
self.database = database
self.user = user
self.password = password
self.port = port
@classmethod
def from_crawler(cls, crawler):
""""""
return cls(
host=crawler.settings.get('MYSQL_HOST'),
database=crawler.settings.get('MYSQL_DATABASE'),
user=crawler.settings.get('MYSQL_USER'),
password=crawler.settings.get('MYSQL_PASSWORD'),
port=crawler.settings.get('MYSQL_PORT')
)
def open_spider(self, spider):
print('打开数据库连接....')
self.db = pymysql.connect(host=self.host, user=self.user, password=self.password, database=self.database,
port=self.port, charset='utf8')
self.cursor = self.db.cursor()
def close_spider(self, spider):
print('关闭数据库连接...')
self.db.close()
def process_item(self, item, spider):
data = dict(item)
keys = ', '.join(data.keys())
print(keys)
values = ', '.join(['%s'] * len(data))
print(values)
sql = "insert into movie_data (%s) values (%s)" % (keys, values)
self.cursor.execute(sql, tuple(data.values()))
self.db.commit()
return item # 传递给下一个即将被执行的管道类
**注:**修改settings.py
文件对项目进行配置
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
"DoubanProject.pipelines.MysqlPipeline": 301,
}
# 连接你本地MYSQL的相关配置
MYSQL_HOST = 'localhost'
MYSQL_DATABASE = 'douban_movie'
MYSQL_PORT = 3306
MYSQL_USER = 'root'
MYSQL_PASSWORD = 'your password'
保存到MySQL部分结果展示
在你的终端命令行输入scrapy crawl douban
后按下回车,等待scrapy
执行完毕,切换到你的mysql
终端,输入slect * form movie_data
即可看到下面的数据,当然你也可以用数据库管理工具MySQLWorkbench
或者Navicat
打开查看
到此,其实你可以按照博主上面介绍的步骤,复制上面的代码到对应的文件下面,修改settings.py
文件中的配置,使用命令行输入开启爬虫就能得到上面的结果,非常的酷炫哈~,不过在某些情况下你可能运行不出来,或者爬取不到数据,第一种情况是豆瓣修改了前端网页,这个时候你修改Xpath
页面解析元素定位的方式即可,第二种情况网站的反爬机制限制了你的访问,这个时候你还需要接着往下看!!!
三、Middleware的用法(扩展)有个初步了解即可
1. Downloader Middleware 的用法
Downloader Middleware 即下载中间件,它是处于Scrapy的Request和Response之间的处理模块
Scheduler
从队列中拿出一个Request
发送给Downloder
执行下载,这个过程会经过Downloader Middleware
的处理,当Downloader
将Request
下载完成得到Response
返回给Spider时会再次经过Downloader Middleware
处理
Downloader Middleware
的功能十分强大,修改User-Agent
、处理重定向、设置代理、失败重试、设置Cookie等功能都需要它来实现
案例演示一:修改User-Agent
我们知道UA
伪装的目的是让网站将我们爬虫发起的请求伪装成浏览器访问网站的请求,我们可以通过如下方式构造一个虚假的UA
,让网站把我们的请求当成是浏览器发过来的请求
middlewares.py
from faker import Faker
class doubanRandomUserAgentMiddlewares:
def __init__(self):
self.faker = Faker() # 看名字就知道是干啥的
def process_request(self, request, spider):
request.headers.setdefault('User-Agent', self.faker.user_agent())
案例演示二:设置Cookie
豆瓣网站的反爬机制对访问频率有限制,对于未登录的请求,如果访问过快,网站会返回403 Forbidden
状态码,那么你将爬不到任何数据,这个时候你需要登录豆瓣网站,然后按下F12
打开开发者工具,将你登录之后的Cookie
赋值粘贴到下面的字符串中
middlewares.py
def get_douban_cookies():
cookie_str = 'your cookies'
cookie_dict = {i.split('=')[0]: i.split('=')[1] for i in cookie_str.split('; ')}
return cookie_dict
COOKIE_DICT = get_douban_cookies() # 获取cookie,为了不每次调用函数,先将其转换成值
class DoubanDownloaderMiddleware:
def process_request(self, request, spider):
request.cookies = COOKIE_DICT # 将获取到的cookie写入请求
return None
注:
settings.py
文件中的配置如下
DOWNLOADER_MIDDLEWARES = {
"DoubanProject.middlewares.DoubanDownloaderMiddleware": 543,
"DoubanProject.middlewares.doubanRandomUserAgentMiddlewares": 544,
}
案例三:设置代理【这里可不配,只做演示】
如果频繁的访问,爬取数据,网站会对直接封掉你的访问IP,这个时候需要设置代理IP,关于代理IP的设置和获取就是另一个大篇幅了,这里不做细讲,在这个案例中爬取你稍微注意一下基本不会封掉IP,如果要设置代理IP,你需要有一批可用的IP地址,这个一般需要付费购买,当你有了IP以后,配置如下
middlewares.py
核心方法
process_request(request, spider)
Request
被Scrapy
引擎调度给Downloader
之前,process_request()
方法就会被调用,在Request从队列里被调度出来到Downloader
下载执行之前,我们都可以用process_request()
方法对Request
进行处理
def process_request(self, request, spider):
"""
"""
# Called for each request that goes through the downloader
# middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object:更低优先级的 Downloader Middleware 就不会被调用
# - or return a Request object:更低优先级的 Downloader Middleware 的 process_request() 会停止执行
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
process_response(request, response, spider)
Downloader
执行Request
下载之后会得到对应的Response
,在Scrapy
引擎将Response
发送给Spider
进行解析之前,可以用process_response()
方法来对Response
进行处理
2. Spider Middleware的用法(在这个案例中不涉及)
简单介绍,大家有兴趣可以深入了解
当Downloader生成Response之后,Response会被发送给Spider,在发送给Spider之前,Response会首先经过
- 在
Downloader
生成的Response
发送给Spider
之前对Response
进行处理 - 在
Spider
生成的Request
发送给Scheduler
之前对Request
进行处理 - 在
Spider
生成的Item
发送给Item Pipeline
之前对Item
进行处理
注:Spider Middleware的使用频率没有 Downloader Middleware高,在必要的情况下可以用来方便数据的处理
四、结语
本文给大家介绍了Scrapy
的框架以及Scrapy
的基本用法,通过一个爬取豆瓣电影Top250
的案例展示了Scrapy
的威力,相比于requests
,Scrapy
最大的方便除了在于框架化,流程化,其实最主要的还是在于Scrapy
中的请求是异步的,对于海量数据的爬取是requests
无法比拟的
上面的根据每个知识点讲解了每部分代码的功能,下面附上可以直接运行的代码,【下面只将爬取数据保存至excel
文件中】大家可以先赋值粘贴运行感受一下Scrapy
的威力与魅力再去深入了解每个部分的功能也可~
安装对应的三方库
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple openpyxl
按照第一节中的步骤创建号工程目录之后
douban.py
import scrapy
from scrapy import Selector, Request
from ..items import DoubanprojectItem
class DoubanSpider(scrapy.Spider):
name = "douban"
allowed_domains = ["movie.douban.com"]
start_urls = ["https://movie.douban.com/top250"]
def parse(self, response):
sel = Selector(response)
movie_items = sel.xpath('//div[@class="article"]//ol[@class="grid_view"]/li')
for movie in movie_items:
item = DoubanprojectItem()
item['rank'] = movie.xpath('.//div[@class="pic"]/em/text()').extract_first() # 电影排名
item['movie_name'] = movie.xpath('.//div[@class="hd"]//span[1]/text()').extract_first() # 电影名称
item['movie_introduction'] = '无' if not movie.xpath('.//div[@class="bd"]//span[@class="inq"]/text()') \
else movie.xpath('.//div[@class="bd"]//span[@class="inq"]/text()').extract_first() # 电影简介
item['picture'] = movie.xpath('.//div[@class="pic"]/a/img/@src').extract_first() # 电影海报链接
item['movie_rating'] = movie.xpath('.//div[@class="star"]/span[2]/text()').extract_first() # 电影评分
item['evaluators'] = movie.xpath('.//div[@class="star"]/span[4]/text()').extract_first() # 评价人数
yield item
nextLink = sel.xpath('//span[@class="next"]/link/@href').extract()
if nextLink:
next_page_url = nextLink[0]
yield Request(url=response.urljoin(next_page_url), callback=self.parse)
items.py
import scrapy
class DoubanprojectItem(scrapy.Item):
# define the fields for your item here like:
rank = scrapy.Field() # 电影排名
movie_name = scrapy.Field() # 电影名称
movie_introduction = scrapy.Field() # 电影简介
picture = scrapy.Field() # 电影海报
movie_rating = scrapy.Field() # 电影评分
evaluators = scrapy.Field() # 观影人数
pipelines.py
import openpyxl
from .items import DoubanprojectItem
class DoubanprojectPipeline:
def __init__(self):
self.wb = openpyxl.Workbook()
self.sheet = self.wb.active
self.sheet.title = '豆瓣电影Top250'
self.sheet.append(('电影排名', '电影名称', '电影简介', '电影海报', '电影评分', '观影人数'))
def open_spider(self, spider):
print('开始爬虫...')
def process_item(self, item: DoubanprojectItem, spider):
"""
:param item:
:param spider:
:return:
"""
self.sheet.append((item['rank'], item['movie_name'], item['movie_introduction'], item['picture'],
item['movie_rating'], item['evaluators']))
return item
def close_spider(self, spider):
print("爬虫结束....")
self.wb.save('豆瓣电影数据.xlsx')
settings.py
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
"DoubanProject.middlewares.DoubanDownloaderMiddleware": 543,
"DoubanProject.middlewares.doubanRandomUserAgentMiddlewares": 544,
}
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
"DoubanProject.pipelines.DoubanprojectPipeline": 300,
}
# 如果做了第三步在Middlewares中做了对应的伪装UA的配置,这里可以不用加
USER_AGENT = '你浏览器的User-Agent'