爬虫 Scrapy 学习系列之一:Tutorial
2018-02-07 00:00
316 查看
前言
笔者打算写本文为作者的原创作品,转载需注明出处;本文转载自本人的博客,伤神的博客:
Scrapy安装
我本地安装有两个版本的python,2.7和3.6;而正如前言所描述的那样,笔者打算使用Python3.6的环境来搭建Scrapy;默认安装的支持Python2.7版本的Scrapy;
安装的是支持python3.x版本的Scrapy;不过安装过程中,遇到了些问题,HTTPSConnectionPool(host=’pypi.python.org’,port=443):Readtimedout.解决办法是,在安装的过程中,延长超时的时间,
ScrapyTutorial
创建tutorial项目
使用2 3 | NewScrapyproject'tutorial',usingtemplatedirectory'/usr/local/lib/python2.7/site-packages/scrapy/templates/project',createdin: /Users/mac/workspace/scrapy/tutorial |
2 3 | NewScrapyproject'tutorial',usingtemplatedirectory'/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/scrapy/templates/project',createdin: /Users/mac/workspace/scrapy/tutorial |
导入PyCharm
直接open项目工程/Users/mac/workspace/scrapy/tutorial;这里需要注意的是默认的PyCharm使用的解释器Interpretor是我本地的Python2.7;这里需要将解释器改为Python3.6;下面记录下修改的步骤,点击左上角PyCharmCommunityEdition,进入Preferences
点击Project:tutorial,然后选择ProjectInterpreter,然后设置解释器的版本,如下
工程结构
通过命令构建出来的项目骨架如图所示第一个Spider
我们来新建一个Spider类,名叫quotes_spider.py,并将其放置到tutorial/spiders目录中2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | classQuotesSpider(scrapy.Spider): name="quotes" defstart_requests(self): urls=[ 'http://quotes.toscrape.com/page/1/', 'http://quotes.toscrape.com/page/2/', ] forurlinurls: yieldscrapy.Request(url=url,callback=self.parse) defparse(self,response): page=response.url.split("/")[-2] filename='quotes-%s.html'%page withopen(filename,'wb')asf: f.write(response.body) self.log('Savedfile%s'%filename) |
name
是Spider的标识符,用于唯一标识该Spider;它必须在整个项目中是全局唯一的;
start_requests()
必须定义并返回一组可以被Spider爬取的Requests,Request对象由一个URL和一个回调函数构成;
parse()
就是Request对象中的回调方法,用来解析每一个Request之后的Response;所以,parse()方法就是用来解析返回的内容,通过解析得到的URL同样可以创建对应的Requests进而继续爬取;
再来看看具体的实现,
start_request(self)方法分别针对
parse(self,response)方法既是对Request的反馈的内容Response进行解析,这里的解析的逻辑很简单,就是分别创建两个本地文件,然后将response.body的内容放入这两个文件当中。
如何执行
执行的过程需要使用到命令行,注意,这里需要使用到2 | $python3-mscrapycrawlquotes |
2 3 4 5 6 7 8 9 10 11 | 2016-12-1621:24:05[scrapy.core.engine]INFO:Spideropened 2016-12-1621:24:05[scrapy.extensions.logstats]INFO:Crawled0pages(at0pages/min),scraped0items(at0items/min) 2016-12-1621:24:05[scrapy.extensions.telnet]DEBUG:Telnetconsolelisteningon127.0.0.1:6023 2016-12-1621:24:05[scrapy.core.engine]DEBUG:Crawled(404)<GET 2016-12-1621:24:05[scrapy.core.engine]DEBUG:Crawled(200)<GET 2016-12-1621:24:05[scrapy.core.engine]DEBUG:Crawled(200)<GET 2016-12-1621:24:05[quotes]DEBUG:Savedfilequotes-1.html 2016-12-1621:24:05[quotes]DEBUG:Savedfilequotes-2.html 2016-12-1621:24:05[scrapy.core.engine]INFO:Closingspider(finished) ... |
如何提取
通过命令行的方式提取
Scrapy提供了命令行的方式可以对需要被爬取的内容进行高效的调试,通过使用如何进入Scrapyshell环境
我们试着通过Scrapyshell来提取下“
2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 2016-09-1912:09:27[scrapy.core.engine]DEBUG:Crawled(200)<GET [s]AvailableScrapyobjects: [s]scrapyscrapymodule(containsscrapy.Request,scrapy.Selector,etc) [s]crawler<scrapy.crawler.Crawlerobjectat0x7fa91d888c90> [s]item{} [s]request<GET [s]spider<DefaultSpider'default'at0x7fa91c8af990> [s]Usefulshortcuts: [s]shelp()Shellhelp(printthishelp) [s]fetch(req_or_url)Fetchrequest(orURL)andupdatelocalobjects [s]view(response)Viewresponseinabrowser >>> |
通过CSS标准进行提取
这里主要是遵循CSS标准
通过使用css()选择我们要提取的元素;下面演示一下如何提取元素<title/>
2 | [<Selectorxpath=u'descendant-or-self::title'data=u'<title>QuotestoScrape</title>'>] |
提取
通过使用extract()或者extract_first()方法来提取元素的内容;下面演示如何提取#1返回的元素<title/>中的文本内容text;
2 | 'QuotestoScrape' |
2 | 'QuotestoScrape' |
通过re()方法来使用正则表达式的方式来进行提取元素的文本内容
2 3 4 5 6 | ['QuotestoScrape'] >>>response.css('title::text').re(r'Q\w+') ['Quotes'] >>>response.css('title::text').re(r'(\w+)to(\w+)') ['Quotes','Scrape'] |
使用XPath
除了使用
2 3 4 | [<Selectorxpath='//title'data='<title>QuotestoScrape</title>'>] >>>response.xpath('//title/text()').extract_first() 'QuotestoScrape' |
提取quotes和authors
下面我们将来演示如何提取
可以看到,里面每个段落包含了一个名人的一段语录,那么我们如何来提取所有的相关信息呢?
我们从提取第一个名人的信息入手,看看如何提取第一个名人的名言信息;可以看到,第一个名人的语句是爱因斯坦的,那么我们试着来提取
2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <spanclass="text">“Theworldaswehavecreateditisaprocessofour thinking.Itcannotbechangedwithoutchangingourthinking.”</span> <span> by<smallclass="author">AlbertEinstein</small> <ahref="/author/Albert-Einstein">(about)</a> </span> <divclass="tags"> Tags: <aclass="tag"href="/tag/change/page/1/">change</a> <aclass="tag"href="/tag/deep-thoughts/page/1/">deep-thoughts</a> <aclass="tag"href="/tag/thinking/page/1/">thinking</a> <aclass="tag"href="/tag/world/page/1/">world</a> </div> </div> |
首先,进入ScrapyShell,
提取
2 3 | >>>title '“Theworldaswehavecreateditisaprocessofourthinking.Itcannotbechangedwithoutchangingourthinking.”' |
2 3 | >>>author 'AlbertEinstein' |
2 3 | >>>tags ['change','deep-thoughts','thinking','world'] |
2 3 4 5 6 7 8 | ...text=quote.css("span.text::text").extract_first() ...author=quote.css("small.author::text").extract_first() ...tags=quote.css("div.tagsa.tag::text").extract() ...print(dict(text=text,author=author,tags=tags)) {'tags':['change','deep-thoughts','thinking','world'],'author':'AlbertEinstein','text':'“Theworldaswehavecreateditisaprocessofourthinking.Itcannotbechangedwithoutchangingourthinking.”'} {'tags':['abilities','choices'],'author':'J.K.Rowling','text':'“Itisourchoices,Harry,thatshowwhatwetrulyare,farmorethanourabilities.”'} ...afewmoreofthese,omittedforbrevity |
通过Python程序来进行提取
本小计继续沿用提取数据
修改该之前的quotes_spider.py内容,如下,
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | classQuotesSpider(scrapy.Spider): name="quotes" start_urls=[ 'http://quotes.toscrape.com/page/1/', 'http://quotes.toscrape.com/page/2/', ] defparse(self,response): forquoteinresponse.css('div.quote'): yield{ 'text':quote.css('span.text::text').extract_first(), 'author':quote.css('small.author::text').extract_first(), 'tags':quote.css('div.tagsa.tag::text').extract(), } |
2 3 4 | 2016-09-1918:57:19[scrapy.core.scraper]DEBUG:Scrapedfrom<200 |
保存数据
最简单保存方式被爬取的数据是通过使用
使用JSON格式
使用JSONLines格式
补充,JSONLines是另一种JSON格式的定义,基本设计是每行是一个有效的JSONValue;比如它的格式比CSV格式更友好,
2 3 4 5 | ["Gilbert","2013",24,true] ["Alexa","2013",29,true] ["May","2012B",14,false] ["Deloise","2012A",19,true] |
2 3 4 | {"name":"Alexa","wins":[["twopair","4♠"],["twopair","9♠"]]} {"name":"May","wins":[]} {"name":"Deloise","wins":[["threeofakind","5♣"]]} |
如果是一个小型的项目,使用JSONLines的方式就足够了;但是,如果你面临的是一个更复杂的项目,而且有更复杂的数据需要爬取,那么你就可以使用
提取下一页(提取链接信息)
我们可以看到,下一页的链接HTML元素,
2 3 4 5 | <liclass="next"> <ahref="/page/2/">Next<spanaria-hidden="true">→</span></a> </li> </ul> |
2 | '<ahref="/page/2/">Next<spanaria-hidden="true">→</span></a>' |
2 | '/page/2/' |
使用scrapy.Request
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | classQuotesSpider(scrapy.Spider): name="quotes" start_urls=[ 'http://quotes.toscrape.com/page/1/', ] defparse(self,response): forquoteinresponse.css('div.quote'): yield{ 'text':quote.css('span.text::text').extract_first(), 'author':quote.css('small.author::text').extract_first(), 'tags':quote.css('div.tagsa.tag::text').extract(), } next_page=response.css('li.nexta::attr(href)').extract_first() ifnext_pageisnotNone: next_page=response.urljoin(next_page) yieldscrapy.Request(next_page,callback=self.parse) |
基于此,你就可以建立起非常复杂的爬虫了,同样,可以根据不同链接的类型,构建不同的Parser,那么就可以对不同类型的返回页面进行分别处理;
使用response.follow
不同于2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | classQuotesSpider(scrapy.Spider): name="quotes" start_urls=[ 'http://quotes.toscrape.com/page/1/', ] defparse(self,response): forquoteinresponse.css('div.quote'): yield{ 'text':quote.css('span.text::text').extract_first(), 'author':quote.css('spansmall::text').extract_first(), 'tags':quote.css('div.tagsa.tag::text').extract(), } next_page=response.css('li.nexta::attr(href)').extract_first() ifnext_pageisnotNone: yieldresponse.follow(next_page,callback=self.parse) |
2 3 | ifnext_pageisnotNone: yieldresponse.follow(next_page,callback=self.parse) |
定义更多的Parser
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | classAuthorSpider(scrapy.Spider): name='author' start_urls=['http://quotes.toscrape.com/'] defparse(self,response): #followlinkstoauthorpages forhrefinresponse.css('.author+a::attr(href)'): yieldresponse.follow(href,self.parse_author) #followpaginationlinks forhrefinresponse.css('li.nexta::attr(href)'): yieldresponse.follow(href,self.parse) defparse_author(self,response): defextract_with_css(query): returnresponse.css(query).extract_first().strip() yield{ 'name':extract_with_css('h3.author-title::text'), 'birthdate':extract_with_css('.author-born-date::text'), 'bio':extract_with_css('.author-description::text'), } |
进入parse(),从当前的页面中爬取得到所有相关的authorhref属性值既是一个链接,然后针对该链接,通过response.follow创建一个新的Request继续进行爬取,通过回调parse_author()方法对爬取的内容进行进一步的解析,这里就是对爬取到的Author的信息进行提取;
当#1有关当前页面所有的Author信息都已经爬取成功以后,便开始对
从这个例子中,我们需要注意的是,当爬取当前页面的时候,我们依然可以通过创建子的Requests对子链接进行爬取直到所有有关当前页面的信息都已经被爬取完毕以后,方可进入下一个页面继续进行爬取;
另外,需要注意的是,在爬取整个网站信息的时候,必然会有多个相同Author的名言,那么势必要爬取到许多的重复的Author的信息;这无疑是增加了爬取的压力同时也需要处理大量的冗余数据,基于此,Scrapy默认实现了对
使用Spider参数
你可以通过commondline的方式为你的Spider提供参数,2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | classQuotesSpider(scrapy.Spider): name="quotes" defstart_requests(self): url='http://quotes.toscrape.com/' tag=getattr(self,'tag',None) iftagisnotNone: url=url+'tag/'+tag yieldscrapy.Request(url,self.parse) defparse(self,response): forquoteinresponse.css('div.quote'): yield{ 'text':quote.css('span.text::text').extract_first(), 'author':quote.css('small.author::text').extract_first(), } next_page=response.css('li.nexta::attr(href)').extract_first() ifnext_pageisnotNone: yieldresponse.follow(next_page,self.parse) |
Reference
Scrapy爬虫框架:InstallationGuide:
explainhowvirtualenvused:
tutorialguide:
ScrapyClusters:
ScrapyDeployment:
Scrapy0.24文档:
相关文章推荐
- 爬虫 Scrapy 学习系列之一:Tutorial
- 爬虫 Scrapy 学习系列之一:Tutorial
- scrapy爬虫学习系列五:图片的抓取和下载
- scrapy爬虫学习系列二:scrapy简单爬虫样例学习
- scrapy爬虫学习系列四:portia的学习入门
- [置顶] Scrapy学习系列之拉勾网爬虫实践
- [置顶] Scrapy学习系列之天眼查爬虫实践
- 【Python学习系列五】Python网络爬虫框架Scrapy环境搭建
- scrapy爬虫学习系列三:scrapy部署到scrapyhub上
- scrapy爬虫学习系列一:scrapy爬虫环境的准备
- scrapy爬虫学习系列四:portia的学习入门
- Scrapy系列教程(3)------Spider(爬虫核心,定义链接关系和网页信息抽取)
- Python爬虫系列之----Scrapy(二)win10下安装Python 2.7.13+Scrapy 1.3.3
- Python的爬虫程序编写框架Scrapy入门学习教程
- scrapy爬虫学习
- Scrapy框架学习(四)----CrawlSpider、LinkExtractors、Rule及爬虫示例
- scrapy爬虫框架学习----安装scrapy
- Scrapy爬虫系列笔记之四:爬取实战之创建爬虫以及Scapy环境搭建_by_书訢
- 跟我学系列,走进Scrapy爬虫(四)Scrapy命令行工具
- Scrapy爬虫学习,及实践项目。