Scrapy爬虫框架的介绍和使用
Scrapy简介
Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。
基本功能
Scrapy是一个为遍历爬行网站、分解获取数据而设计的应用程序框架,它可以应用在广泛领域:数据挖掘、信息处理和或者历史片(历史记录)打包等等
尽管Scrapy原本是设计用来屏幕抓取(更精确的说,是网络抓取)的目的,但它也可以用来访问API来提取数据,比如Amazon的AWS或者用来当作通常目的应用的网络蜘蛛
Scrapy框架
Scrapy是用Python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘、信息处理或存储历史数据等一系列的程序中。
Scrapy使用Twisted基于事件的高效异步网络框架来处理网络通信,可以加快下载速度,不用自己去实现异步框架,并且包含了各种中间件接口,可以灵活的完成各种需求。
Scrapy架构
Scrapy Engine
引擎,负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。此组件相当于爬虫的“大脑”,是整个爬虫的调度中心
调度器(Scheduler)
调度器接收从引擎发送过来的request,并将他们入队,以便之后引擎请求他们时提供给引擎
初始的爬取URL和后续在页面中获取的待爬取的URL将放入调度器中,等待爬取。同时调度器会自动去除重复的URL(如果特定的URL不需要去重也可以通过设置实现,如post请求的URL)
下载器(Downloader)
下载器负责获取页面数据并提供给引擎,而后提供给spider
Spiders爬虫
Spider是编写的类,作用如下:
Scrapy用户编写用于分析response并提取item(即获取到的item)
额外跟进的URL,将额外跟进的URL提交给引擎,加入到Scheduler调度器中。将每个spider负责处理一个特定(或一些)网站
Item Pipeline
Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)
当页面被爬虫解析所需的数据存入Item后,将被发送到项目管道(Pipeline),并经过设置好次序的pipeline程序处理这些数据,最后将存入本地文件或存入数据库
类似管道 $ ls | grep test 或者类似于Django 模板中的过滤器
以下是item pipeline的一些典型应用:
清理HTML数据
验证爬取的数据(检查item包含某些字段)
查重(或丢弃)
将爬取结果保存到数据库中
下载器中间件(Downloader middlewares)
简单讲就是自定义扩展下载功能的组件。
下载器中间件,是在引擎和下载器之间的特定钩子(specific hook),处理它们之间的请求request和响应response。
它提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能
通过设置下载器中间件可以实现爬虫自动更换user-agent、IP等功能
Spider中间件(Spider middlewares)
Spider中间件,是在引擎和Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items或requests)。
也提供了同样的简便机制,通过插入自定义代码来扩展Scrapy功能。
数据流(Data flow)
1.引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个(批)要爬取的URL(s)
2.引擎从Spider中获取到第一个要爬取的URL并加入到调度器(Scheduler)作为请求以备调度
3.引擎向调度器请求下一个要爬取的URL
4.调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件并转发给下载器(Downloader)
5.一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件发送给引 擎
6.引擎从下载器中接收到Response,然后通过Spider中间件发送给Spider处理
7.Spider处理Response并返回提取到的Item及(跟进的)新的Request给引擎
8.引擎将Spider返回的Item交给Item Pipeline,将Spider返回的Request交给调度器
9.(从第二步)重复执行,直到调度器中没有待处理的request,引擎关闭
注意:
只有当调度器中没有任何request了,整个程序才会停止执行。如果有下载失败的URL,会重新下载
安装scrapy
安装wheel支持
$ pip install wheel
安装scrapy框架
$ pip install scrapy
window下,为了避免windows编译安装twisted依赖,安装下面的二进制包
$ pip install Twisted-18.4.0-cp35-cp35m-win_amd64.whl
windows下出现如下问题:
copyingsrc\twisted\words\xish\xpathparser.g->build\lib.win-amd64-3.5\twisted\words\xishrunningbuild_extbuilding'twisted.test.raiser'extensionerror:MicrosoftVisualC++14.0isrequired.Getitwith"MicrosoftVisualC++BuildTools":http://landinghub.visualstudio.com/visual-cpp-build-tools解决方案是,下载编译好的twisted,PythonExtensionPackagesforWindowspython3.5下载Twisted-18.4.0-cp35-cp35m-win_amd64.whlpython3.6下载Twisted-18.4.0-cp36-cp36m-win_amd64.whl安装twisted$pipinstallTwisted-18.4.0-cp35-cp35m-win_amd64.whl之后在安装scrapy就没有什么问题了
安装好,使用scrapy命令看看
1.>scrapy2.Scrapy1.5.0-noactiveproject3.4.Usage:5.scrapy<command>[options][args]6.7.Availablecommands:8.benchRunquickbenchmarktest9.checkCheckspidercontracts10.crawlRunaspider11.editEditspider12.fetchFetchaURLusingtheScrapydownloader13.genspiderGeneratenewspiderusingpre-definedtemplates14.listListavailablespiders15.parseParseURL(usingitsspider)andprinttheresults16.runspiderRunaself-containedspider(withoutcreatingaproject)17.settingsGetsettingsvalues18.shellInteractivescrapingconsole19.startprojectCreatenewproject20.versionPrintScrapyversion21.viewOpenURLinbrowser,asseenbyScrapy
Scrapy开发
项目编写流程
1.创建项目
使用 scrapy startproject proname 创建一个scrapy项目
scrapy startproject <project_name> [project_dir]
2.编写item
在items.py中编写Item类,明确从response中提取的item
3.编写爬虫
编写spiders/proname_spider.py,即爬取网站的spider并提取出item
4.编写item pipeline
item的处理,可以存储
1 创建项目
1.1 豆瓣书评爬取
标签为“编程”,第一页、第二页链接:
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=0&type=T
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T
随便找一个目录来创建项目,执行下面命令
$ scrapy startproject first .
会产生如下目录和文件
first├─scrapy.cfg└─first├─items.py├─middlewares.py├─pipelines.py├─settings.py├─__init__.py└─spiders└─__init__.py
first:
外部的first目录是整个项目目录,内部的first目录是整个项目的全局目录
scrapy.cfg:
必须有的重要的项目的配置文件
first 项目目录
__init__.py 必须有,包文件
items.py 定义Item类,从scrapy.Item继承,里面定义scrapy.Field类实例
pipelines.py 重要的是process_item()方法,处理item
settings.py:
BOT_NAME 爬虫名
ROBOTSTXT_OBEY = True 是否遵从robots协议
USER_AGENT = '' 指定爬取时使用
CONCURRENT_REQEUST = 16 默认16个并行
DOWNLOAD_DELAY = 3 下载延时,一般要设置,不宜过快发起连续请求
COOKIES_ENABLED = False 缺省是启用,一般需要登录时才需要开启cookie
SPIDER_MIDDLEWARES 爬虫中间件
DOWNLOADER_MIDDLEWARES 下载中间件
'firstscrapy.pipelines.FirstscrapyPipeline': 300item交给哪一个管道处理,300 越小优先
级越高
ITEM_PIPELINES 管道配置
'first.middlewares.FirstDownloaderMiddleware': 543543 越小优先级越高
spiders目录
__init__.py 必须有,可以在这里写爬虫类,也可以写爬虫子模块
1.#first/settings.py参考2.BOT_NAME='first'3.SPIDER_MODULES=['first.spiders']4.NEWSPIDER_MODULE='first.spiders'5.6.USER_AGENT="Mozilla/5.0(WindowsNT6.1)AppleWebKit/537.36(KHTML,likeGecko)Chrome/55.0.2883.75Safari/537.36"7.ROBOTSTXT_OBEY=False8.9.DOWNLOAD_DELAY=310.11.#Disablecookies(enabledbydefault)12.COOKIES_ENABLED=False
注意一定要更改User-Agent,否则访问https://book.douban.com/会返回403
2 编写Item
1.在items.py中编写2.importscrapy3.classBookItem(scrapy.Item):4.title=scrapy.Field()#书名5.rate=scrapy.Field()#评分
3 编写爬虫
为爬取豆瓣书评编写爬虫类,在spiders目录下:
编写的爬虫类需要继承自scrapy.Spider,在这个类中定义爬虫名、爬取范围、其实地址等
在scrapy.Spider中parse方法未实现,所以子类应该实现parse方法。该方法传入response对象
1.#scrapy源码中2.classSpider():3.defparse(self,response):#解析返回的内容4.raiseNotImplementedError
爬取读书频道,tag为“编程”的书名和评分:
https://book.douban.com/tag/%E7%BC%96%E7%A8%8B?start=20&type=T
使用模板创建spider, $ scrapy genspider -t basic book https://www.douban.com/
1.importscrapy2.3.classBookSpider(scrapy.Spider):#BookSpider4.name='doubanbook'#爬虫名,可修改,重要5.allowed_domains=['豆瓣']#爬虫爬取范围6.url='豆瓣图书标签:编程'7.start_urls=[url]#起始URL8.9.#下载器获取了WEBServer的response就行了,parse就是解析响应的内容10.defparse(self,response):11.print(type(response),'~~~~~~~~~')#scrapy.http.response.html.HtmlResponse12.print(response)13.print('-'*30)
使用crawl爬取子命令
1.$scrapylist2.$scrapycrawl-h3.scrapycrawl[options]<spider>4.5.指定爬虫名称开始爬取6.$scrapycrawldoubanbook7.8.可以不打印日志9.$scrapycrawldoubanbook--nolog
如果在windows下运行发生twisted的异常 ModuleNotFoundError: No module named 'win32api' ,请安装 $ pip install pywin32。
response是服务器端HTTP响应,它是scrapy.http.response.html.HtmlResponse类。
由此,修改代码如下
1.importscrapy2.fromscrapy.http.response.htmlimportHtmlResponse3.4.classBookSpider(scrapy.Spider):#BookSpider5.name='doubanbook'#爬虫名6.allowed_domains=['豆瓣']#爬虫爬取范围7.url='豆瓣图书标签:编程'8.start_urls=[url]#起始URL9.10.#下载器获取了WEBServer的response就行了,parse就是解析响应的内容11.defparse(self,response:HtmlResponse):12.print(type(response))#scrapy.http.response.html.HtmlResponse13.print('-'*30)14.print(type(response.text),type(response.body))15.print('-'*30)16.print(response.encoding)17.withopen('o:/testbook.html','w',encoding='utf-8')asf:18.try:19.f.write(response.text)20.f.flush()21.exceptExceptionase:22.print(e)
3.1 解析HTML
爬虫获得的内容response对象,可以使用解析库来解析。
scrapy包装了lxml,父类TextResponse类也提供了xpath方法和css方法,可以混合使用这两套接口解析HTML。
选择器参考:
https://scrapy-chs.readthedocs.io/zh_CN/0.24/topics/selectors.html#id3
1.importscrapy2.fromscrapy.http.response.htmlimportHtmlResponse3.4.response=HtmlResponse('file:///O:/testbook.html',encoding='utf-8')#构造对象5.6.withopen('o:/testbook.html',encoding='utf8')asf:7.response._set_body(f.read())#填充数据8.#print(response.text)9.1O.#获取所有标题及评分11.#xpath解析12.subjects=response.xpath('//li[@class="subject-item"]')13.forsubjectinsubjects:14.title=subject.xpath('.//h3/a/text()').extract()#list15.print(title[0].strip())16.17.rate=subject.xpath('.//span[@class="rating_nums"]/text()').extract()18.print(rate[0].strip())19.2O.print('-'*30)21.#css解析22.subjects=response.css('li.subject-item')23.forsubjectinsubjects:24.title=subject.css('h3a::text').extract()25.print(title[0].strip())26.27.rate=subject.css('span.rating_nums::text').extract()28.print(rate[0].strip())29.print('-'*30)30.31.#xpath和css混合使用、正则表达式匹配32.subjects=response.css('li.subject-item')33.forsubjectinsubjects:34.#提取链接35.href=subject.xpath('.//h3').css('a::attr(href)').extract()36.print(href[0])37.38.#使用正则表达式39.id=subject.xpath('.//h3/a/@href').re(r'\d*99\d*')40.ifid:41.print(id[0])42.43.#要求显示9分以上数据44.rate=subject.xpath('.//span[@class="rating_nums"]/text()').re(r'^9.*')45.#rate=subject.css('span.rating_nums::text').re(r'^9\..*')46.ifrate:47.print(rate)
3.2 item封装数据
1.#spiders/bookspider.py2.importscrapy3.fromscrapy.http.response.htmlimportHtmlResponse4.from..itemsimportBookItem5.6.classBookSpider(scrapy.Spider):#BookSpider7.name='doubanbook'#爬虫名8.allowed_domains=['豆瓣']#爬虫爬取范围9.url='豆瓣图书标签:编程'10.start_urls=[url]#起始URL11.12.#下载器获取了WEBServer的response就行了,parse就是解析响应的内容13.defparse(self,response:HtmlResponse):14.items=[]15.#xpath解析16.subjects=response.xpath('//li[@class="subject-item"]')17.forsubjectinsubjects:18.title=subject.xpath('.//h3/a/text()').extract()19.rate=subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()20.item=BookItem()21.item['title']=title[0].strip()22.item['rate']=rate.strip()23.items.append(item)24.25.print(items)26.27.returnitems#一定要return,否则保存不下来28.29.#使用命令保存return的数据30.#scrapycrawl-h31.#--output=FILE,-oFILEdumpscrapeditemsintoFILE(use-forstdout)32.#文件扩展名支持'json','jsonlines','jl','csv','xml','marshal','pickle'33.#scrapycrawldoubanbook-odbbooks.json
得到下图数
注意上图的数据已经是unicode字符,汉字的unicode表达。
4 pipeline处理
将bookspider.py中BookSpider改成生成器,只需要把 return items 改造成 yield item ,即由产生一个列表变成yield一个个item
脚手架帮我们创建了一个pipelines.py文件和一个类
4.1 开启pipeline
1.#Configureitempipelines2.#SeeItemPipeline-Scrapy1.8.0documentation3.ITEM_PIPELINES={4.'first.pipelines.FirstPipeline':300,5.}
整数300表示优先级,越小越高。
取值范围为0-1000
4.2常用方法
1.classFirstPipeline(object):2.def__init__(self):#全局设置3.print('~~~~~~~~~~init~~~~~~~~~~~~')4.5.defopen_spider(self,spider):#当某spider开启时调用6.print(spider,'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')7.8.defprocess_item(self,item,spider):9.#item获取的item;spider获取该item的spider10.returnitem11.12.defclose_spider(self,spider):#当某spider关闭时调用13.print(spider,'========================================')14.
需求
通过pipeline将爬取的数据存入json文件中
1.#spider/bookspider.py2.importscrapy3.fromscrapy.http.response.htmlimportHtmlResponse4.from..itemsimportBookItem5.6.classBookSpider(scrapy.Spider):#BookSpider7.name='doubanbook'#爬虫名8.allowed_domains=['豆瓣']#爬虫爬取范围9.url='豆瓣图书标签:编程'10.start_urls=[url]#起始URL11.12.#spider上自定义配置信息13.custom_settings={14.'filename':'o:/books.json'15.}16.#下载器获取了WEBServer的response就行了,parse就是解析响应的内容17.defparse(self,response:HtmlResponse):18.#items=[]19.#xpath解析20.subjects=response.xpath('//li[@class="subject-item"]')21.forsubjectinsubjects:22.title=subject.xpath('.//h3/a/text()').extract()23.rate=subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()24.item=BookItem()25.item['title']=title[0].strip()26.item['rate']=rate.strip()27.#items.append(item)28.29.yielditem30.#returnitems31.32.#pipelines.py33.importsimplejsonasjson34.35.classFirstPipeline(object):36.def__init__(self):#全局设置37.print('~~~~~~~~~~init~~~~~~~~~~~~')38.39.defopen_spider(self,spider):#当某spider开启时调用40.print('{}~~~~~~~~~~~~~~~~~~~~'.format(spider))41.print(spider.settings.get('filename'))42.self.file=open(spider.settings['filename'],'w',encoding='utf-8')43.self.file.write('[\n')44.45.defprocess_item(self,item,spider):46.#item获取的item;spider获取该item的spider47.self.file.write(json.dumps(dict(item))+',\n')48.returnitem49.50.defclose_spider(self,spider):#当某spider关闭时调用51.self.file.write(']')52.self.file.close()53.print('{}======================='.format(spider))54.print('-'*30)
5 url提取
如果要爬取下一页内容,可以自己分析每一页的页码变化,也可以通过提取分页栏的链接
1.#spider/bookspider.py2.importscrapy3.fromscrapy.http.response.htmlimportHtmlResponse4.from..itemsimportBookItem5.6.classBookSpider(scrapy.Spider):#BookSpider7.name='doubanbook'#爬虫名8.allowed_domains=['豆瓣']#爬虫爬取范围9.url='豆瓣图书标签:编程'10.start_urls=[url]#起始URL11.12.#spider上自定义配置信息13.custom_settings={14.'filename':'o:/books.json'15.}16.17.#下载器获取了WEBServer的response就行了,parse就是解析响应的内容18.defparse(self,response:HtmlResponse):19.#items=[]20.#xpath解析21.#获取下一页,只是测试,所以使用re来控制页码22.print('-'*30)23.urls=response.xpath('//div[@class="paginator"]/span[@class="next"]/a/@href').re(24.r'.*start=[24]\d[^\d].*')25.print(urls)26.print('-'*30)27.yieldfrom(scrapy.Request(response.urljoin(url))forurlinurls)28.print('++++++++++++++++++++++++')29.30.subjects=response.xpath('//li[@class="subject-item"]')31.forsubjectinsubjects:32.#解决图书副标题拼接33.title="".join(map(lambdax:x.strip(),subject.xpath('.//h3/a//text()').extract()))34.rate=subject.xpath('.//span[@class="rating_nums"]/text()').extract_first()35.#print(rate)#有的没有评分,要注意可能返回None36.37.item=BookItem()38.item['title']=title39.item['rate']=rate40.#items.append(item)41.yielditem42.43.#returnitems
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。