前两周去学习了一下Django,由于第一次接触设计模式
,对于mvc设计模式
还是很陌生的,花了大约一周的时间才大概熟悉了整个工作流程,第二周学会了写一些简单的网站应用,原本自己定好目标是要两周学会一个框架的,但是整网站要搞服务器,还要域名备案什么的,怪麻烦的,两周时间过去,还是没有做一个完整的Django项目,也就没脸跑去写博客来记录了。但还是不得不继续学习我计划中的下一个框架——Scrapy
,从3.15到今天3.21号已经正好过去一周了,我花了四到五天的时间看完了一本半的书,周五晚上熬了个夜最终把整个scrapy项目的流程写了一遍,包括spider
,item
,管道
,spider中间件
,下载器中间件
。由于笔者之前已经对爬虫有一定的经验了,因此这个框架接触的比较快。
1.Scrapy的特点
要具体去说scrapy的特点,网上故居一搜一大堆,而且也比我写的更准确具体详细,我也只是写下来作为自己的笔记。有错误的话也欢迎各位指正。
1.1 在我看来,在大概了解了scrapy框架之后,我唯一的感受就是模块化
,就像是我之前写的爬虫都是啥玩意,各种工作都糅杂在一起,一个方法写解析主页,一个方法写构造表单数据,一个方法写详情页的解析,又一个方法写数据的保存等等,很多地方都有重复性工作,比如爬取网站上的文章,主页爬一遍,详情页又爬一遍,相当于request写了两遍,但其实这个过程是一样的,request的作用不就是用来请求得到html吗,虽然请求头有所不同,但是这个过程是没必要去写两遍的。
1.2 另一个感受就是脉络清晰,scrapy将各种功能的实现都模块化了,也因此让我们不再考虑一个项目的具体实现,只需要写它的业务逻辑便是了。就那上面爬取网站文章的例子来说。首先定义它的数据结构,要保存哪些数据,比如标题
,日期
,作者
,文章标签
,文章内容
,那么首先在item.py文件里定义好字段。在网站主页上我们只能获得文章的标题
,日期
,因此对于文章的作者
,文章标签
,文章内容
我们还需要进入详情页进行分析,此时就已经简单确定好了spider
的业务逻辑。分别写parse方法和parse_xqy方法,在parse方法里获取文章的标题
,时间
,随后返回一个Request对象
给下载器,meta属性里传递item对象
,callback属性写上详情页解析方法的方法名parse_xqy,在parse_xqy里面获取文章的作者
,文章标签
,文章内容
,将数据合并成一个item对象
,返回item对象
给管道
。至此,一个项目的主体已然实现了,而剩下的就是管道
中对item对象
的处理,中间件对Request对象
,Response对象
和异常
的处理 。
1.3 速度快,scrapy的调度器采用了twisted异步框架,可以让我们的项目不受到部分任务因此网络或者其他原因而阻塞,而且scrapy的调度器里的请求队列也实现的挺好的(但具体怎么好我也不清楚,本人只是用技术而不造技术的渣渣),对于大批量的请求,我自己写的爬虫可能根本承受不了,但是scrapy却可以很轻松的承受。而且更快更稳。
2.一个小项目
废话不多说,实战开始,我先简单写个爬取一下知乎首页文章
2.1 新建scrapy项目
打开控制台,输入
scrapy startproject zhihu [zhihu]
zhihu表示项目名,方括号里后面表示文件夹名。
2.2 新建爬虫
这一步在上一步的基础上
cd zhihu
scrapy genspider zhihutext zhihu.com
当然,你也可以直接进入spiders文件夹新建爬虫文件,但是这样需要自己写一下下面的内容
import scrapy
class ZhihutextSpider(scrapy.Spider):
name = 'zhihu'
allowed_domains = ['zhihu.com']
start_urls = ['http://zhihu.com/hot']
def parse(self, response):
pass
ZhihutextSpider
继承自scrapy.Spider
,有一些属性和方法,有些属性和方法需要重写,以实现对特定网站的爬取。
2.2.1 属性
name
对一个spider来说,name属性时必须且唯一的,其定义了spider的名字,而scrapy通过spider的名字来定位并且初始化spider。
allowed_domains
该属性可选,该属性是一个列表,里面是域名,表示爬虫只会爬属于域名列表里面的网页。
start_urls
也是一个列表,当没有指定特定url时,spider将从该列表中开始获取页面数据,后续的url将从获取的数据中提取。
custom_setings
该属性可选,是一个dict。当spider启动时,会覆盖项目的设置,即setting.py文件里面的设置,由于设置必须在初始化前被更新,因此必须设定为class属性。
2.2.2方法
start_requests()方法
在爬虫启动时,scrapy会调用该方法,会从start_urls
列表里面一次获取url
并生成Request对象
传递给下载器,中间经历了spider中间件
和下载器中间件
,下载器下载返回了Response对象
,然后调用parse()方法
解析Response对象
,该方法一般可以选择不重写。
parse()方法
该方法是爬虫最重要的部分,该方法接收一个response参数,即从下载器中下载的Response对象
。该方法是一定要重写的(不过有一个例外,scrapy自带的通用爬虫CrawlSpider
是一定不能重写此方法的,原因是CrawlSpider
默认使用parse
来实现爬虫逻辑,如果重写了parse
,那么CrawlSpider
将不能继续正确执行),也是我们写爬虫最重要的一个地方,我们从Response对象
中提取数据就是在该方法里。
2.3 定义数据结构(编写items.py文件)
分析页面,主页上有排行榜排名,问题,简介和热度。
进入一个问题详情页里面,发现有标签,关注者,被浏览,问题内容,回答数,评论数。
因此我们可以在items.py里定义9个字段:
top_number
,question
,question_content
,hot
,tags
,attention
,browse
,answer_number
,comment_number
import scrapy
class ZhihuItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
top_number = scrapy.Field()
question = scrapy.Field()
question_content = scrapy.Field()
hot = scrapy.Field()
tags = scrapy.Field()
attention = scrapy.Field()
browse = scrapy.Field()
answer_number = scrapy.Field()
comment_number = scrapy.Field()
2.4 编写spider文件
import scrapy
from zhihu.items import ZhihuItem
from scrapy import Request
import json, re
class ZhihutextSpider(scrapy.Spider):
name = 'zhihu'
# allowed_domains = ['zhihu.com']
start_urls = ['http://zhihu.com/hot']
def start_requests(self):
for url in self.start_urls:
yield Request(url, callback=self.parse)
def parse(self, response):
selectors