scrapy框架入门
一、Scrapy框架介绍
1.框架简介
- Scrapy是纯Python开发的一个高效,结构化的网页抓取框架
- Scrapy使用了Twisted 异步网络库来处理网络通讯
- Scrapy是为了爬取网站数据,提取结构性数据而编写的应用框架
- Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试
2.模块安装
scrapy支持Python2.7和python3.4以上版本
python包可以用全局安装(也称为系统范围),也可以安装在用户空间中
(1).Windows
pip install scrapy
换源:
pip install scrapy -i https://pypi.doubanio.com/simple
如果安装途中报错,则按照以下方法安装
- 在
https://www.lfd.uci.edu/~gohlke/pythonlibs/
下载对应的Twisted的版本文件 - 在命令行进入到Twisted的目录,执行
pip install Twisted的文件名
- 执行
pip install scrapy
(2).Ubuntu 14.04或以上
scrapy目前正在使用最新版的lxml,twisted和pyOpenSSL进行测试,并且与最近的Ubuntu发行版兼容,但它也支持旧版本的Ubuntu,比如Ubuntu14.04,尽管可能存在TLS连接问题
Ubuntu安装不要使用 python-scrapyUbuntu提供的软件包,它们通常太旧而且速度慢,无法赶上最新的Scrapy
-
在Ubuntu(或基于Ubuntu)系统上安装scrapy,需要安装这些依赖项:
sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
-
如果想在python3上安装scrapy,还需要Python3的开发头文件:
sudo apt-get install python3-dev
-
在virtualenv中,可以使用pip安装Scrapy:
pip install scrapy
3.架构介绍
-
Engine:引擎。负责控制系统所有组件之间的数据流,并在发生某些操作时触发事件,是整个框架的核心
-
Spiders:蜘蛛。由用户编写的自定义的类,用于解析响应,从中提取数据,或其它要抓取的请求
-
Scheduler:调度器。接收来自引擎的请求并将其排入队列,在引擎再次请求时将请求提供给引擎
-
Downloader:下载器。获取网页并将其提供给引擎,引擎再将网页内容提供给spider
-
Item Pipeline:项目管道。负责由爬虫提取的数据的后续处理,典型的任务包括清理、验证和存储数据
-
Spider Middlewares:蜘蛛中间件。位于引擎和爬虫之间的特定的钩子,负责处理向蜘蛛输入的响应和输出的结果及新的请求。以下情况使用爬虫中间件:
- 处理爬虫回调之后的请求或item
- 处理start_requests
- 处理爬虫异常
- 根据响应内容调用errback而不是回调请求
-
Downloader Middlewares:下载中间件。位于引擎和下载器之间的特定的钩子,负责处理从引擎传递到下载器的请求,以及下载器传递到引擎的响应。以下情况使用下载中间件:
- 在请求发送到下载程序之前处理请求(即在scrapy将请求发送到网站之前)
- 在响应发送给爬虫之前
- 直接发送新的请求,而不是将收到的响应传递给蜘蛛
- 将响应传递给爬行器而不获取web页面;
- 默默的放弃一些请求
-
Items:项目。定义了爬取结果的数据结构,爬取的数据会被赋值成该Item对象
-
Internet:事件驱动的网络。scrapy 是用Twisted编写的,Twisted是一个流行的事件驱动的Python网络框架,它使用非阻塞(也成为异步)代码实现并发
4.运行流程
Scrapy中的数据流由执行引擎控制,流程如下:
- Engine首先向Spiders请求第一个要爬取的URL,URL通过Spiders到达Engine之后,Engine将URL转发给Scheduler以Request的形式调度
- Engine从Scheduler请求下一个要爬取的Request,Engine获得Request之后,通过下载中间件转发给Downloader,Downloader连接互联网并爬取相关的网页形成Response,并将其通过下载中间件发送给Engine,再由Engine转发到Spiders
- Spiders处理接收到的Response,然后解析出Item和生成新的Request,并发送给Engine,Engine将处理好的Item发送给Item Pipeline,将生成好的新的Request发送给调度器
5.项目结构
scrapy.cfg # Scrapy项目的配置文件,定义了项目配置文件路径、部署相关信息等内容
project/ # 项目模块
__init__.py
items.py # 定义Item的数据结构
pipelines.py # 定义Item Pipeline的实现
settings.py # 定义项目的全局配置
middlewares.py # 定义爬虫中间件和下载中间件的实现
spiders/ # 防止spider的文件夹
__init.py
spider1.py # spider1文件
spider2.py # spider2文件
...
二、Scrapy入门(爬取腾讯课堂所有python课程信息)
1.创建项目
进入你想要存放项目的目录下运行一下命令:
scrapy startproject 项目名
例如:scrapy startproject tencent
该命令会在当前目录创建包含一下文件的名为tz_spider的目录
2.创建Spider
Spider是自己定义的类,Scrapy用它从网页里抓取内容并解析抓取的结果。创建一个Spider的类,它必须必须继承Scrapy提供的Spider类scrapy.Spider,并且需要定义三个属性:
- name:用于区别Spider,该名字必须是唯一的
- start_urls:包含了Spider在启动时进行爬取的url列表 ,初始请求由它定义
- parse(self, response) 方法:Spider的一个方法,每个初始url完成之后被调用。这个函数要完成两个功能:
- 解析响应,封装成item对象并返回这个对象
- 提取新的需要下载的url,创建新的request,并返回它
可以使用命令行创建一个spider:
cd 项目文件
scrapy genspider <name> <domain>
例如:
cd tencent
scrapy genspider ke ke.qq.com
会在spiders文件下创建ke.py文件,文件内容如下:
# -*- coding: utf-8 -*-
import scrapy
class KeSpider(scrapy.Spider):
name = 'ke'
allowed_domains = ['ke.qq.com']
start_urls = ['http://ke.qq.com/']
def parse(self, response):
pass
- allowed_domains:允许爬取的域名,如果初始或后续的请求链接不是这个域名下的,则会被过滤掉,一般将其注释掉
3.定义Items文件
Item是保存爬取数据的容器,使用方法类似于字典,比字典多了额外的保护机制,避免拼写错误或者定义字段错误。创建Item类需要继承scrapy.Item类,并且定义类型为scrapy.Field的字段
items.py文件,内容如下:
import scrapy
class KeItem(scrapy.Item):
title = scrapy.Field()
link_url = scrapy.Field()
img_url = scrapy.Field(
4.解析Response
parse()方法的参数response是start_urls里面的链接爬取后的结果,所以可以在parse()方法中直接对response进行解析,extract()方法把selectlist对象序列成字符串
# -*- coding: utf-8 -*-
import scrapy
class KeSpider(scrapy.Spider):
name = 'ke'
# allowed_domains = ['ke.qq.com']
start_urls = ['https://ke.qq.com/course/list']
def parse(self, response):
title_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/h4/a/text()').extract()
link_url_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/a/@href').extract()
img_url_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/a/img/@src').extract()
5.使用Item
Item可以理解为一个字典,但在声明的时候需要实例化,因此首先导入该类,接下来进行实例化,然后依次用刚才解析的结果赋值给Item的每个字段,最后将Item通过yield返回即可。这样首页的所有内容都被解析出来,并且复制成一个个的KeItem()
# -*- coding: utf-8 -*-
import scrapy
from ..items import KeItem
class KeSpider(scrapy.Spider):
name = 'ke'
# allowed_domains = ['ke.qq.com']
start_urls = ['https://ke.qq.com/course/list']
def parse(self, response):
title_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/h4/a/text()').extract()
link_url_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/a/@href').extract()
img_url_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/a/img/@src').extract()
for title, link_url, img_url in zip(title_list, link_url_list, img_url_list):
item = KeItem()
item['title'] = title
item['link_url'] = link_url
item['img_url'] = img_url
yield item
6.后续Request
上面的爬虫实现了从初始页面抓取内容,而我们需要爬取所有页面,直到所有的信息都被下载。因此需要从页面中提取链接,或者根据规则构建(小技巧:开发者工具下点击下一页那个标识)。构造请求时候需要用到scrapy.Request(),传入两个参数:
- url:请求链接
- callback:回调函数。当指定了该回调函数的请求完成后,获取到响应,引擎会将该响应作为参数传递给此回调函数,回调函数进行解析或生成下一个请求
# -*- coding: utf-8 -*-
import scrapy
from ..items import KeItem
class KeSpider(scrapy.Spider):
name = 'ke'
# allowed_domains = ['ke.qq.com']
start_urls = ['https://ke.qq.com/course/list']
def parse(self, response):
title_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/h4/a/text()').extract()
link_url_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/a/@href').extract()
img_url_list = response.xpath('//ul/li[@class="course-card-item--v3 js-course-card-item "]/a/img/@src').extract()
for title, link_url, img_url in zip(title_list, link_url_list, img_url_list):
item = KeItem()
item['title'] = title
item['link_url'] = link_url
item['img_url'] = img_url
yield item
next_url = response.xpath('//a[@class="page-next-btn icon-font i-v-right"]/@href').extract_first() # 此处需要调用extract_first()方法获取获取内容
if next_url: # next_url为空时停止,否则报错
yield scrapy.Request(url=next_url, callback=self.parse) # 回调函数依然使用parse()方法,不写默认也是parse()方法
7.运行
进入项目根目录(tencent)下,在命令行运行命令:
查看可运行的爬虫文件:
scrapy list
运行爬虫:
scrapy crawl <name>
注意:此处的name是创建Spider时的name属性,而不是.py文件的文件名
例如:scrapy crawl ke
8.保存到文件
以下命令对应的输出分别为json、csv、xml、pickle、marshal格式
scrapy crawl ke -o ke.json
scrapy crawl ke -o ke.csv
scrapy crawl ke -o ke.xml
scrapy crawl ke -o ke.pickle
scrapy crawl ke -o ke.marshal
9.定义Item Pipeline
如果想进行更复杂的操作(将结果保存到数据库,或者筛选信息),则需要定义Item Pipeline来实现
Item Pipeline的主要用途是:
- 清理HTML数据
- 验证爬取数据,检查爬取字段
- 查重并删除重复内容
- 将爬取结果保存到数据库
要激活这个管道组件,必须将其添加到ITEM_PIPELINES设置中,在settings文件中设置如下:
ITEM_PIPELINES = {
'tencent.pipelines.TencentPipeline': 300,
}
编写一个用来存储课程信息到ke.json文件中的管道类,修改项目下piplines.py文件
import json
class TencentPipeline(object):
def open_spider(self, spider):
# 在爬虫启动时打开文件
# 打开文件句柄
self.f = open('ke.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
# 处理传递过来的item
self.f.write(json.dumps(dict(item), ensure_ascii=False, indent=4) + "\n")
return item
def close_spider(self, spider):
# 在爬虫关闭时关闭文件
self.f.close()
柄
self.f = open(‘ke.json’, ‘w’, encoding=‘utf-8’)
def process_item(self, item, spider):
# 处理传递过来的item
self.f.write(json.dumps(dict(item), ensure_ascii=False, indent=4) + "\n")
return item
def close_spider(self, spider):
# 在爬虫关闭时关闭文件
self.f.close()