文章目录Scray爬虫框架1.框架架构1.1Scray框架介绍1.2Scray架构图1.3Scray框架模块功能1.4Scray的运作流程(容易理解的介绍)2.Scray入门2.1创建项目2.2项目的目录结构2.3使用Scray框架爬取糗事百科段子3.CrawlSider3.1创建CrawlSider爬虫3.2LinkExtractors链接提取器3.3Re规则类3.4微信小程序社区CrawlSider案例3.5总结
Scray爬虫框架
1.框架架构
1.1Scray框架介绍写一个爬虫,需要做很多的事情。比如:发送网络请求、数据解析、数据存储、反反爬虫机制(更换i、设置请求头等)、异步请求等。这些工作如果每次都要自己从零开始写的话,比较浪费时间。因此Scray把一些基础的东西封装好了,在他上面写爬虫可以变的更加的高效(爬取效率和开发效率)。因此真正在公司里,一些上了量的爬虫,都是使用Scray框架来解决。1.2Scray架构图1.3Scray框架模块功能ScrayEngine(引擎):负责Sider、ItemPiene、Downloader、Scheder中间的通讯,信号、数据传递等。
Scheder(调度器):它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。
Downloader(下载器):负责下载ScrayEngine(引擎)发送的所有Requests请求,并将其获取到的Resonses交还给ScrayEngine(引擎),由引擎交给Sider来处理,
Sider(爬虫):它负责处理所有Resonses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheder(调度器),
ItemPiene(管道):它负责处理Sider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方.
DownloaderMiddlewas(下载中间件):你可以当作是一个可以自定义扩展下载功能的组件。
SiderMiddlewas(Sider中间件):你可以理解为是一个可以自定扩展和操作引擎和Sider中间通信的功能组件(比如进入Sider的Resonses;和从Sider出去的Requests)1.4Scray的运作流程(容易理解的介绍)引擎:Hi!Sider,你要处理哪一个网站?
Sider:老大要我处理xxxx.com。
引擎:你把第一个需要处理的URL给我吧。
Sider:给你,第一个URL是xxxxxxx.com。
引擎:Hi!调度器,我这有quest请求你帮我排序入队一下。
调度器:好的,正在处理你等一下。
引擎:Hi!调度器,把你处理好的quest请求给我。
调度器:给你,这是我处理好的quest
引擎:Hi!下载器,你按照老大的下载中间件的设置帮我下载一下这个quest请求
下载器:好的!给你,这是下载好的东西。(如果失败:sorry,这个quest下载失败了。然后引擎告诉调度器,这个quest下载失败了,你记录一下,我们待会儿再下载)
引擎:Hi!Sider,这是下载好的东西,并且已经按照老大的下载中间件处理过了,你自己处理一下(注意!这儿sonses默认是交给defarse()这个函数处理的)
Sider:(处理完毕数据之后对于需要跟进的URL),Hi!引擎,我这里有两个结果,这个是我需要跟进的URL,还有这个是我获取到的Item数据。
引擎:Hi!管道我这儿有个item你帮我处理一下!调度器!这是需要跟进URL你帮我处理下。然后从第四步开始循环,直到获取完老大需要全部信息。
管道``调度器:好的,现在就做!2.Scray入门
2.1创建项目要使用Scray框架创建项目,需要在命令行通过命令来创建。打开cmd进入到你想把这个项目存放的目录。然后使用以下命令scraystartroject[项目名称]创建。2.2项目的目录结构
? 创建完成以后,使用ycharm打开项目。主要文件的作用:items.y:用来存放爬虫爬取下来数据的模型。
middlewas.y:用来存放各种中间件的文件。
ienes.y:用来将items的模型存储到本地磁盘中。
settings.y:本爬虫的一些配置信息(比如请求头、多久发送一次请求、i池等)。
scray.cfg:项目的配置文件。
siders包:以后所有的爬虫,都是存放到这个里面。2.3使用Scray框架爬取糗事百科段子
创建项目:scraystartrojectqsbk
进入qsbk目录中,使用命令创建一个爬虫:scraygensiderqsbk_siderqiushibaike.com
创建了一个名叫做qsbk_sider(爬虫名不能和项目名称一样)的爬虫,并且能爬取的网页只会限制在qiushibaike.com这个域名下。
爬虫代码解析:创建的qsbk_sider爬虫会在siders目录中。qsbk_sider.y
#-*-coding:utf-8-*-
imortscrayclassQsbkSiderSider(scray.Sider):
name='qsbk_sider'
allowed_domains=['qiushibaike.com']
start_urls=['htt:qiushibaike.com']defarse(self,sonse):
ass其实这些代码我们完全可以自己手动去写,而不用命令。只不过是不用命令,自己写这些代码比较麻烦。
要创建一个Sider,那么必须自定义一个类,继承自scray.Sider,然后在这个类中定义三个属性和一个方法。name:这个爬虫的名字,名字必须是唯一的。
allow_domains:允许的域名。爬虫只会爬取这个域名下的网页,其他不是这个域名下的网页会被自动忽略。
start_urls:爬虫从这个变量中的url开始爬取。
arse:引擎会把下载器下载回来的数据扔给爬虫解析,爬虫再把数据传给这个arse方法。这个是个固定的写法。这个方法的作用有两个,第一个是提取想要的数据。第二个是生成下一个请求的url。修改settings.y代码:
在做一个爬虫之前,一定要记得修改setttings.y中的设置。两个地方是强烈建议设置的。ROBOTSTXT_OBEY设置为False。默认是True。即遵守robots协议,那么在爬虫的时候,scray首先去找robots.txt文件,如果没有找到。则直接停止爬取。
DEFAULT_REQUEST_HEADERS添加User-Agent。完成的爬虫代码:
items.y
imortscrayclassQsbkItem(scray.Item):
#definethefieldsforyouritemheke:
#name=scray.Field()
author=scray.Field()
content=scray.Field()ienes.y:将数据保存到json文件中
方式1
imortjsonclassQsbkPiene:
def__init__(self):
self.f=oen('duanzi.json','w',encoding='utf-8')defoen_sider(self,sider):
assdefrocess_item(self,item,sider)
#将item对象转换为字典,在转换为json字符串
item_json=json.dums(dict(item),ensu_ascii=False)
self.f.write(item_json+'n')
#iene可能有多个,如果不返回item,其他iene将不能获得item。
turnitemdefclose_sider(self,sider):
self.f.close()保存json数据时,可以使用这两个类,让操作变得更简单。
JsonItemExorter:每次把数据添加到内存中,最后统一写入到磁盘中。好处是,存储的数据是一个满足json规则的数据。坏处是如果数据量比较大,那么比较耗内存。
JsonLinesItemExorter:每次调用exort_item时就把item存储待硬盘中。坏处是每一个字典是一行,整个文件是一个不满足json格式的文件。好处是每次处理数据的时候就直接存储到了硬盘中,这样不会耗内存,数据也比较安全。
方式2
fromscray.exortersimortJsonItemExorter
"""
这种方式先将每个item转换为字典,放到列表中。最后执行方法self.exorter.finish_exorting()统一将列表写到文件中去。
"""
classQsbkPiene:
def__init__(self):
#以二进制的方式打开文件
self.f=oen('duanzi.json','wb')
self.exorter=JsonItemExorter(self.f,ensu_ascii=False,encoding='utf-8')
self.exorter.start_exorting()defoen_sider(self,sider):
assdefrocess_item(self,item,sider):
self.exorter.exort_item(item)
turnitemdefclose_sider(self,sider):
self.exorter.finish_exorting()
self.f.close()方式3
fromscray.exortersimortJsonLinesItemExorterclassQsbkPiene:
def__init__(self):
self.f=oen('duanzi.json','wb')
self.exorter=JsonLinesItemExorter(self.f,ensu_ascii=False,encoding='utf-8') #oen_sider:当爬虫被打开的时候执行。
defoen_sider(self,sider):
ass #rocess_item:当爬虫有item传过来的时候会被调用。
defrocess_item(self,item,sider):
self.exorter.exort_item(item)#执行这个方法将item转换为字典写入到文件中
turnitem #close_sider:当爬虫关闭的时候会被调用。
defclose_sider(self,sider):
self.f.close()还要激活iene,才能使用。在setting.y中,设置ITEM_PIPLINES。
ITEM_PIPELINES={
'qsbk.ienes.QsbkPiene':300,
}qsbk_sider.y爬取多页的代码
imortscray
fromqsbk.itemsimortQsbkItemclassQsbkSiderSider(scray.Sider):
name='qsbk_sider'
allowed_domains=['qiushibaike.com']
start_urls=['htts:www.qiushibaike.comtext']
"""
sonse是一个scray.htt.sonse.html.HtmlResonse对象。可以执行xath和css语法来提取数据。提取出来的数据是一个Selector或者是一个SelectorList对象,这两个对象有get和getall方法。getall方法:将每一个Selector对象转换为字符串,并放在列表中返回。get方法:将第一个Selector对象转换为字符串,直接返回。
"""
defarse(self,sonse):
duanzidivs=sonse.xath("[@class='c1d-style-c1']")
#rint("========")
#rint(duanzidivs.getall())#将SelectorList对象中的每个Selector转换为字符串,并放在列表中返回。
#rint("========")
forduazidivinduanzidivs:
author=duazidiv.xath(".text()").get().stri()
content=duazidiv.xath(".[@class='content']text()").getall()
content="".join(content).stri()
item=QsbkItem(author=author,content=content)
yielditem
next_age_url=sonse.xath("[@class='agination'][last()]@hf").get()
#爬到最后一页时,next_age_url提取不到,为None,爬虫结束。
ifnotnext_age_url:
turn
next_age_url="htts:www.qiushibaike.com"+next_age_url
rint(next_age_url)
#请求下一页,当请求回来后执行callback指定的回调函数
yieldscray.Request(next_age_url,callback=self.arse)编写完爬取多页的代码后,在settings.y中设置下载延迟DOWNLOAD_DELAY=1,每隔一秒请求一次
运行scray项目:
运行scray项目。需要在终端,进入项目所在的路径,然后scraycrawl[爬虫名字]即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在ycharm中执行运行这个文件就可以了。比如现在在项目的根目录下新创建一个文件叫做start.y,然后在这个文件中编写代码:
fromscrayimortcmdnecmdne.execute("scraycrawlqsbk_sider".st())
#cmdne.execute(['scray','crawl','qsbk_sider'])上面的命令与这个等价3.CrawlSider
在糗事百科的爬虫案例中。我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,都给我进行爬取。那么这时候我们就可以通过CrawlSider来帮我们完成了。CrawlSider继承自Sider,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scray碰到满足条件的url都进行爬取,而不用手动的yieldRequest。
3.1创建CrawlSider爬虫
之前创建爬虫的方式是通过scraygensider[爬虫名字][域名]的方式创建的。如果想要创建CrawlSider爬虫,那么应该进入你想要存放爬虫项目的目录,通过命令scraygensider-tcrawl[爬虫名字][域名]创建。
CrawlSider需要使用LinkExtractors类和Re类,实现对满足条件的url进行自动爬取。
3.2LinkExtractors链接提取器
使用LinkExtractors可以不用程序员自己提取相应的url,然后发送请求。这些工作都可以交给LinkExtractors,它会在所有爬的页面中找到满足我们自己设置规则的url,实现自动的爬取。
创建该类的对象时需要的主要参数为allowallow:允许的url。所有满足这个正则表达式的url都会被提取。3.3Re规则类
定义爬虫的规则类。
创建该类的对象时需要的主要参数nk_extractor:一个LinkExtractor对象,用于定义爬取规则。
callback:满足这个规则的url,应该要执行哪个回调函数。
flow:指定根据该规则从sonse中提取的链接是否需要跟进。3.4微信小程序社区CrawlSider案例
主要代码如下:
items.y
imortscray
classWxaItem(scray.Item):
#definethefieldsforyouritemheke:
#name=scray.Field()
title=scray.Field()
author=scray.Field()
ubc_time=scray.Field()
content=scray.Field()wxa_sider.y
#-*-coding:utf-8-*-
imortscray
fromscray.nkextractorsimortLinkExtractor
fromscray.sidersimortCrawlSider,Re
fromwxa.itemsimortWxaItemclassWxaSiderSider(CrawlSider):
name='wxa_sider'
allowed_domains=['wxa-union.com']
start_urls=['htt:www.wxa-union.comortal.h?mod=st&am;catid=2&am;age=1']
"""
这里定义了两个Re对象,第一个Re对象中的爬取规则为.+mod=st&am;catid=2&am;age=d即将每一页的url提取出来,进行爬取。这里的flow参数传入的是True,即根据定义的规则提取的链接,将它下载下来后,继续从它的sonse中提取满足该规则的url。我们只需要从每一页中获取每一个教程的url即可,不需要解析该页面的内容,所以不需要callback参数。
第二个Re对象中的爬取规则为.+article-.+.html即提取每一个教程的url,将相应url下载下来后,执行回调函数arse_item提取教程的详细信息。flow参数设置为False,即提取出的每一个教程的url,下载下来后不需要再根据规则.+article-.+.html提取url,因为每一页根据规则.+article-.+.html就能将所有的教程url检索出来。
"""
s=(
Re(LinkExtractor(allow=r'.+mod=st&am;catid=2&am;age=d'),flow=True),
Re(LinkExtractor(allow=r'.+article-.+.html'),callback="arse_item",flow=False)
)defarse_item(self,sonse):
title=sonse.xath("[@class='h']text()").get()
author=sonse.xath("[@class='authors']text()").get()
ubc_time=sonse.xath("[@class='time']text()").get()
content=sonse.xath("td[@id='article_content']text()").getall()
content="".join(content).stri()
item=WxaItem(title=title,author=author,ubc_time=ubc_time,content=content)
yielditem
ienes.y
fromscray.exortersimortJsonLinesItemExorterclassWxaPiene:
def__init__(self):
self.f=oen('wxjc.json','wb')
self.exorter=JsonLinesItemExorter(self.f,ensu_ascii=False,encoding='utf-8')defrocess_item(self,item,sider):
self.exorter.exort_item(item)
turnitemdefclose_sider(self,sider):
self.f.close()3.5总结
定义的各个Re规则类的检索条件将作用于每一个下载下来的url的sonse。
Re对象中什么情况下使用flow:如果在爬取页面的时候,需要将满足条件的url再进行跟进(即将满足条件的url下载下来后,继续从它的sonse中提取满足定义规则的url),那么就设置为True,否则设置为False
什么情况下该指定callback:如果这个url对应的页面,只是为了获取更多的url,并不需要提取里面的数据,那么可以不指定callback。如果想要获取url对应页面中的数据,那么就需要指定一个callback。