CrawlSpider 的基本用法 CrawlSpider 是 Spider 的一个子类。Spider 是爬虫文件中爬虫类的父类。
一般来讲,子类的功能要比父类多,所以 CrawlSpider 的功能是比 Spider 更完善更强大的。
CrawlSpider 的作用:常被用作于专业实现全站数据爬取,也就是将一个页面下所有页码对应的数据进行爬取。
CrawlSpider 的基本使用:
创建一个工程
cd 到这个工程根目录
创建一个基于 CrawlSpider 的爬虫文件
1 scrapy genspider -t crawl SpiderName www.xxx.com
执行工程
注意:
一个链接提取器对应一个规则解析器(多个链接提取器和多个规则解析器)
在实现深度爬取的过程中需要和 scrapy.Request()
结合使用
需求:爬取校花网图片
网址 url:http://www.521609.com/daxuexiaohua/
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Ruleclass FirstSpider (CrawlSpider ): name = 'first' start_urls = ['http://www.521609.com/daxuexiaohua/' ] link = LinkExtractor(allow=r'' ) rules = ( Rule(link, callback='parse_item' , follow=True ), ) def parse_item (self, response ): print (response)
问:如何将一个网站中全站所有的链接都进行爬取。
答:把正交规则写成空字符串 ''
即可。如果仅要爬取本站所有内容,需要设置 allowed_domains 为当前域名,避免爬取到其他网站。
使用 CrawlSpider 实现深度爬取 需求:爬取阳光热线问政平台最新问政的问政标题、状态和问政详细信息。
网址 url:http://wz.sun0769.com/political/index/politicsNewest?id=1&type=4&page=
分析:问政标题和状态均可在首页得到,但是详细信息需要爬取详情页内容。使用 crawl 确实可以获取到每一个详情页的网址,并获取详细信息,但是如果要将首页的问政标题和状态与详情页的详细信息匹配,需要下一番功夫。(事实上,我们可以在详情页拿到所有这三个信息,但是为了学习和训练,我们分别从首页和详情页抓取。)
我们可以使用问政编号,作为数据的标识。如此一来,可以将首页内容和详情页内容一一对应了。
基于上面的思路,我们可以编写出爬虫的源代码:
1 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 from scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom sunPro.items import SunproItem, SunproDeatilItemclass SunSpider (CrawlSpider ): name = 'sun' start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&type=4&page=' ] rules = ( Rule(LinkExtractor(allow=r'id=1&page=\d+' ), callback='parse_item' , follow=True ), Rule(LinkExtractor(allow=r'index\?id=\d+' ), callback='parse_detail' , follow=False ), ) def parse_item (self, response ): """首页,用来提取标题和状态""" li_list = response.xpath('//ul[@class="title-state-ul"]/li' ) for li in li_list: item = SunproItem() qid = li.xpath('./span[1]/text()' ).extract_first().strip() status = li.xpath('./span[2]/text()' ).extract_first().strip() title = li.xpath('./span[3]/a/text()' ).extract_first().strip() item['qid' ] = qid item['status' ] = status item['title' ] = title yield item def parse_detail (self, response ): """详情页,用来提取详细信息,实现深度爬取""" qid = response.xpath('//div[@class="mr-three"]/div[1]/span[4]/text()' ).extract_first().replace('编号:' , '' ).strip() content = response.xpath('//div[@class="mr-three"]/div[2]/pre/text()' ).extract_first().strip() item = SunproDeatilItem() item['qid' ] = qid item['content' ] = content yield item
数据通过问政 id 进行标识,数据存储的时候,相同的问政 id 保存在一起就好。
别忘了在 items.py
中写好两个 Item 类,SunproItem 和 SunproDetailItem。
但是现在还有一个问题:首页和详情页解析出来的数据统统给了管道。但是这两种数据是不同的,处理方式也是有差异的。我们该如何在管道中区分这两种数据,并对其进行分别存储呢?
很简单,这两个 item 是不同类的实例化对象,我们只需要通过查询类名,即可对两种数据进行区分。item 类名的查询方式为:
通过判断类名,分别处理,根据 qid 进行一一对应存储,我这里就不详细写了:
1 2 3 4 5 6 7 8 9 10 11 12 class SunproPipeline (object ): def process_item (self, item, spider ): """数据持久化存储,根据qid进行一一对应,就不详细写了""" qid = item['qid' ] if item.__class__.__name__ == 'SunproItem' : title = item['title' ] status = item['status' ] print (qid, title, status) else : content = item['content' ] print (qid, content) return item
CrawlSpider 结合 Spider 实现深度爬取 像上面那种,单独使用 CrawlSpider 确实能够实现深度爬取。但是我们发现,坑一个接着一个:首页和详情页的数据不容易一一对应,两个不同的 item 对象传入管道,携带的数据和数据处理方式也不同,需要区别对待。
链接获取和发送请求的过程的确是节省了,但是又多出了许多其他的麻烦,有些得不偿失。所以对于深度爬取的任务,我们更倾向于使用的方法是,通过 CrawlSpider 与 spider.Request
结合使用的方式,实现深度爬取。
这就很容易处理了,手动请求,加上请求参数传递即可实现:
1 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 27 28 29 30 31 32 33 34 35 36 37 38 import scrapyfrom scrapy.linkextractors import LinkExtractorfrom scrapy.spiders import CrawlSpider, Rulefrom sunPro.items import SunproItem, SunproDeatilItemclass SunSpider (CrawlSpider ): name = 'sun' start_urls = ['http://wz.sun0769.com/political/index/politicsNewest?id=1&type=4&page=' ] rules = ( Rule(LinkExtractor(allow=r'id=1&page=\d+' ), callback='parse_item' , follow=False ), ) def parse_item (self, response ): """首页,用来提取标题和状态""" li_list = response.xpath('//ul[@class="title-state-ul"]/li' ) for li in li_list: item = SunproItem() qid = li.xpath('./span[1]/text()' ).extract_first().strip() status = li.xpath('./span[2]/text()' ).extract_first().strip() title = li.xpath('./span[3]/a/text()' ).extract_first().strip() url = 'http://wz.sun0769.com/' + li.xpath('./span[3]/a/@href' ).extract_first().strip() item['qid' ] = qid item['status' ] = status item['title' ] = title yield scrapy.Request(url, callback=self.parse_detail, meta={'item' : item}) def parse_detail (self, response ): item = response.meta['item' ] qid = response.xpath('//div[@class="mr-three"]/div[1]/span[4]/text()' ).extract_first().replace('编号:' , '' ).strip() content = response.xpath('//div[@class="mr-three"]/div[2]/pre/text()' ).extract_first().strip() item['qid' ] = qid item['content' ] = content yield item
这样,只会有一种 item 对象,里面是我们需要的所有数据。在管道中,不需要分类处理,不需要考虑首页和详情页一一对应的问题。一切都变得很简单了。