概览
在具体的学习scrapy之前,我们先对scrapy的架构做一个简单的了解,之后所有的内容都是基于此架构实现的,在初学阶段只需要简单的了解即可,之后的学习中,你会对此架构有更深的理解。 下面是scrapy官网给出的最新的架构图示。

基本组件
-
引擎(Engine) 引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。
详细内容查看下面的数据流(Data Flow)部分。 -
调度器(Scheduler) 调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。
-
下载器(Downloader) 下载器负责获取页面数据并提供给引擎,而后提供给spider。 并去除处理完的请求。
-
爬虫(Spiders) Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。
-
管道(Item Pipeline) Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、验证及持久化(例如存取到数据库中)。
-
下载器中间件(Downloader middlewares) 下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
-
Spider中间件(Spider middlewares) Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。
数据流向
Scrapy的数据流由执行引擎(Engine)控制,其基本过程如下:
引擎从Spider中获取到初始Requests。 引擎将该Requests放入调度器,并请求下一个要爬取的Requests。 调度器返回下一个要爬取的Requests给引擎 引擎将Requests通过下载器中间件转发给下载器(Downloader)。 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。 Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。 引擎将(Spider返回的)爬取到的Item交给ItemPipeline处理,将(Spider返回的)Request交给调度器,并请求下一个Requests(如果存在的话)。 (从第一步)重复直到调度器中没有更多地Request。
安装
pip install scrapy
创建项目
在自己的项目目录下创建项目:
scrapy startproject cnblog
命令来在当前目录下创建一个新的项目。 下面我们创建一个爬取博客园('https://www.cnblogs.com/')文章信息的项目
cmd:

然后使用pycharm打开创建的项目文件:

打开后的项目目录:

下面简单的讲解一下各目录/文件的作用:
- spiders/ 在这个文件夹下面,编写你自定义的spider。
- items.py 定义你所要抓取的字段
- middlewares.py 中间件,主要是对功能的拓展,你可以添加一些自定义的功能,比如添加随机user-agent, 添加proxy。
- pipelines.py 管道文件,当spider抓取到内容(item)以后,会被送到这里,这些信息(item)在这里会被清洗,去重,保存到文件或者数据库。
- settings.py 设置文件,用来设置爬虫的默认信息,相关功能开启与否,比如是否遵循robots协议,设置默认的headers,设置文件的路径,中间件的执行顺序等等。
- scrapy.cfg 项目的配置文件,带有这个文件的那个目录作为scrapy项目的根目录
项目配置
在settings.py中配置以下内容:
#爬虫项目名
BOT_NAME = 'cnblog'
#爬虫模块的位置
SPIDER_MODULES = ['cnblog.spiders']
NEWSPIDER_MODULE = 'cnblog.spiders'
# 不遵循robots协议
ROBOTSTXT_OBEY = False
# 爬取url之间的时间间隔
DOWNLOAD_DELAY = 1
# 是否支持cookie
COOKIES_ENABLED = True
#默认请求头
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
# user-agent是新添加的
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
}
编写爬虫
在terminal中创建爬虫文件:
scrapy genspider blog cnblogs.com
该文件在spiders目录下。
在该py文件下自动生成的代码:
import scrapy
class BlogSpider(scrapy.Spider):
#自定义爬虫名
name = 'blog'
#限定网站域
allowed_domains = ['cnblogs.com']
#起始url集合
start_urls = ['http://cnblogs.com/']
#爬取方法
def parse(self, response):
pass
分析:
导入scrapy模块 定义一个spider类,继承自scrapy.Spider父类。 下面是三个重要的内容:
-
name: 用于区别Spider。 该名字必须是唯一的,不可以为不同的Spider设定相同的名字。这一点很重要。
-
start_urls: 包含了Spider在启动时进行爬取的url列表。第一个被获取到的页面将是其中之一。即这是爬虫链接的起点,爬虫项目启动,便开始从这个链接爬取,后续的URL则从初始的URL获取到的数据中提取。
-
parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的 Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据(生成item)以及生成需要进一步处理的URL的 Request 对象。 修改settings.py文件 将settings.py文件里面的下列内容修改如下,其余的内容不动。
运行我们的爬虫项目 至此,项目必要的信息已经全部完成了,下面就是运行我们的爬虫项目 进入带有scrapy.cfg文件的那个目录,前面已经说过,这是项目的根目录,执行下面的命令:
scrapy crawl blog
blog是spiders/blog.py文件里面我们定义的那个具有唯一性的name 你会发现打印出了博客园首页的文章标题列表和文章的url信息,如下所示:
在blog.py中的parse()中编写爬虫:
#爬取方法
def parse(self, response:HtmlResponse):
# print(type(response)) HtmlResponse对象
# print(response)
# print(f'内容:{response.text}') #打印response文本
# xpath:提供基于xpath路径方式解析 extract():类型转换,返回list类型
title = response.xpath('//a[@class="titlelnk"]/text()').extract()
title_url = response.xpath('//a[@class="titlelnk"]/@href').extract()
print(f'类型:{type(title)},内容:{title}')
print(f'类型:{type(title_url)},内容:{title_url}')
print('*'*50)
# css:提供基于css选择器解析
title = response.css('a.titlelnk').extract()
print(f'类型:{type(title)},内容:{title}')
解析方式:
- xpath:提供基于xpath路径方式解析
response.xpath('xpath语法').extract()
- css:提供基于css选择器解析
response.css('css选择器').extract()
,css没有会直接获取信息的方法,要进一步解析才能获取。 - response.text:打印响应的代码
- extract():类型转换
可将<class 'scrapy.selector.unified.SelectorList'>
为<class 'list'>
类型
结果:

数据存储
我们简单的实现了一个博客首页信息的爬取,并在控制台输出,但是,爬下来的信息自然是需要保存下来的。这一节主要是实现信息的存储,我们以将信息保存到文件为例,学习数据的存储,依然是以博客首页信息为例。
items.py:
import scrapy
class CnblogItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
href = scrapy.Field()
爬虫文件blog.py:
import scrapy
from scrapy.http import HtmlResponse
from cnblog.items import CnblogItem
class BlogSpider(scrapy.Spider):
#自定义爬虫名
name = 'blog'
#限定网站域
allowed_domains = ['cnblogs.com']
#起始url集合
start_urls = ['http://www.cnblogs.com/']
def parse(self, response):
# 获取并解析数据,类型为list
title = response.xpath('//a[@class="titlelnk"]/text()').extract()
title_url = response.xpath('//a[@class="titlelnk"]/@href').extract()
#依次封装数据
for i in range(len(title)):
#获取item对象
it = CnblogItem()
it['title'] = title[i]
it['href'] = title_url[i]
# print(it)
# 将item对象传给管道pipelines
yield it
在最后每当yield一次。管道就接受一次item数据。
管道pipelines.py:
class CnblogPipeline:
def __init__(self):
#创建文件
self.wf = open('cnblog.txt','w',encoding='utf-8')
def process_item(self, item, spider):
# 获取item对象的数据
s = f'标题:{item["title"]},href:{item["href"]}'
#写入文件
self.wf.write(s+"\n")
self.wf.flush()
#返回给引擎
return item
这时管道并没有允许使用。
settings.py:
把注释去掉即可。
# 管道允许使用
ITEM_PIPELINES = {
'cnblog.pipelines.CnblogPipeline': 300,
}
运行
在terminal中输入scrapy crawl blog

在项目目录下生成cnblog.txt:

网友评论