Scrapy是一个用python实现都爬虫框架,简单易用,功能强大,只需要在框架的基础上自定义自己的分析规则即可,具体如何新建工程等待都在官方文档上面讲解得非常清楚,官方文档tutorial(http://doc.scrapy.org/en/latest/intro/tutorial.html)请保证下载较新版本的Scrapy(我的是0.24.2,scrapy -v)旧版本会出现一些问题。
下面我使用Scrapy抓取豆瓣上面编程书籍的一些简单信息
一、准备爬取的页面如下,新建一个douban工程:
每一页有20本书籍,虽然下面显示由97页,实际上只有50页,也即1000本书籍,非常快就可以抓取下来
(非常开心看到自己最喜欢的两本书排在前两位《算法导论》and《深入理解计算机系统》 ^_^)
二、确定所需信息为(title, link, author, price, description),编辑items.py文件,添加所需的项
三、定义自己的爬虫,在douban/douban/spiders/目录下新建一个.py文件(此处sola.py)
1. 导入所需模块,此处的爬虫继承自最简单的Spider, 除此之外scrapy还实现了CrawlSpider等功能更强大的爬虫可以使用
2.确定 爬虫名字是sola,必须唯一 ; allowed_domains: 允许在book.douban.com 域名下抓取; 以及起始url
3. 编写parse函数,parse函数是对每一url请求返回的响应所对应作出的分析
parse函数由两部分组成,第一部分是如何处理相应所获的的书籍信息(Item),第二部分是如何递归抓取下一页面的书籍, 也就是返回item和Request的问题
1)处理Item
首先查看网页源码,可以发现每一本书籍对应的html文档如下(以下以《编程珠玑》为例),
1 <li class="subject-item"> 2 <div class="pic"> 3 <a class="nbg" href="http://book.douban.com/subject/3227098/" 4 onclick="moreurl(this,{i:'5'})" }> 5 <img class="" src="http://img3.douban.com/mpic/s4687321.jpg" 6 width="90"> 7 </a> 8 </div> 9 <div class="info"> 10 <h2 class=""> 11 12 13 <a href="http://book.douban.com/subject/3227098/" title="编程珠玑" 14 onclick="moreurl(this,{i:'5'})"> 15 16 编程珠玑 17 18 </a> 19 20 </h2> 21 <div class="pub"> 22 23 24 Jon Bentley / 黄倩、钱丽艳 / 人民邮电出版社 / 2008-10 / 39.00元 25 26 </div> 27 28 <div class="star clearfix"> 29 <span class="allstar45"></span> 30 <span class="rating_nums">9.2</span> 31 32 <span class="pl"> 33 (1387人评价) 34 </span> 35 </div> 36 37 <p>本书是计算机科学方面的经典名著。书的内容围绕程序设计人员面对的一系列实际问题展开。作者Jon Bentley 以其独有的洞察力和创造力,引导读者理解这些问题... </p> 38 39 <div class="ft"> 40 41 <div class="collect-info"> 42 </div> 43 44 <div class="buy-info"> 45 46 <a href="http://book.douban.com/subject/3227098/buylinks"> 47 有售 48 27.60 元起 49 </a> 50 </div> 51 52 53 </div> 54 55 </div> 56 </li>
所以首先找到所有 <li class="subject-item">的元素 : info = Selector(response).xpath(‘//li[@class="subject-item"]‘)
然后对于每一个<li class="subject-item">元素,可以发现书籍信息在里面的<div class="info">元素里,title和link在div里面的<h2 class="">的a元素中,作者和价格在<div class="pub">的文本中(这里需要对字符串进行处理,取出‘/’ 划分的子串的第一个和最后一个),而描述则在<p>元素中(后面有一些书籍没有描述,所以这里需要增加一些特殊处理), 所以得到的对书籍的处理代码如下:
1 def parse(self, response): 2 books = [] 3 sel = Selector(response) 4 info = sel.xpath(‘//li[@class="subject-item"]‘) 5 if len(info) == 0: 6 raise CloseSpider(‘---------------------End Search!---------------‘) 7 8 9 f = open(‘books.data‘,‘a‘) 10 for site in info.xpath(‘div[@class="info"]‘): 11 book = DoubanItem() 12 book[‘title‘] = site.xpath(‘h2/a/@title‘).extract()[0].encode(‘utf-8‘) 13 book[‘link‘] = site.xpath(‘h2/a/@href‘).extract()[0].encode(‘utf-8‘) 14 pub = site.xpath(‘div[@class="pub"]/text()‘).extract()[0].encode(‘utf-8‘) 15 pub = pub.strip().split(‘/‘) 16 17 book[‘author‘] = pub[0] 18 book[‘price‘] = pub[-1] 19 desc = site.xpath(‘p/text()‘).extract() 20 book[‘desc‘] = desc[0].encode(‘utf-8‘) if (len(desc) != 0) else ‘‘ 21 print(‘-----------------lALALALALALA----------------------------------------------‘) 22 print(book[‘title‘]) 23 print(book[‘link‘]) 24 print(book[‘author‘]) 25 print(book[‘price‘]) 26 print(book[‘desc‘]) 27 ss = "{‘title‘: ‘" + book[‘title‘] + "‘ , " + "‘link‘: ‘" + book[‘link‘] + "‘ , " + "‘author‘: ‘" + book[‘author‘] + "‘ , " + "‘price‘: ‘" + book[‘price‘] + "‘ , " + "‘desc‘: ‘" + book[‘desc‘] + "‘}" 28 29 target = json.dumps(ss, ensure_ascii=False) // 转换成json格式保存到文件中 30 31 f.write(target+‘\n‘) 32 print(‘-----------------lALALALALALA----------------------------------------------\n\n‘) 33 books.append(book) 34 yield book
2)返回Request,继续抓取下一页面
查看源码只需每次返回下一页的链接(注意url是采用utf-8编码,关于编码的知识这两篇博客写得很好,给博主赞一个:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html),也就是找到<span class="next">元素,抽取其中的href链接,然后并上“http://book.douban.com”即可得到下一页的连接,最后使用 yield scrapy.Request(new_url, callback=self.parse) 会默认递归继续执行下去。
1 <div class="paginator"> 2 <span class="prev"> 3 <前页 4 </span> 5 6 7 8 <span class="thispage">1</span> 9 10 <a href="/tag/编程?start=20&type=T" >2</a> 11 12 13 <a href="/tag/编程?start=40&type=T" >3</a> 14 15 16 <a href="/tag/编程?start=60&type=T" >4</a> 17 18 19 <a href="/tag/编程?start=80&type=T" >5</a> 20 21 22 <a href="/tag/编程?start=100&type=T" >6</a> 23 24 25 <a href="/tag/编程?start=120&type=T" >7</a> 26 27 28 <a href="/tag/编程?start=140&type=T" >8</a> 29 30 31 <a href="/tag/编程?start=160&type=T" >9</a> 32 33 <span class="break">...</span> 34 35 <a href="/tag/编程?start=1900&type=T" >96</a> 36 37 <a href="/tag/编程?start=1920&type=T" >97</a> 38 39 <span class="next"> 40 <link rel="next" href="/tag/编程?start=20&type=T"/> 41 <a href="/tag/编程?start=20&type=T" >后页></a> 42 </span> 43 44 </div>
四、这样子只要在主目录下面运行 scrapy crawl sola就可以得到所有1000本书籍的信息(终端及books.data)
当爬取到没有书籍的时候应及时关掉爬虫~~
结果如下:
这里本可以使用 -o items.json 自动生成一个json文件,但是得到的文件始终无法显示中文,经过一整天的查找与尝试猜测scrapy框架里面可能是使用dumps()函数时没有给第二个参数ensure_ascii 赋值为False, 导致只能以Ascll码解析为json文件。希望知道如何输出为中文的朋友指点一下~
sola.py 代码:
import scrapy import json from scrapy.selector import Selector from scrapy.spider import Spider from douban.items import DoubanItem from scrapy.exceptions import CloseSpider class DoubanSpider(Spider): name = ‘sola‘ allowed_domains = [‘book.douban.com‘] start_urls = [ ‘http://book.douban.com/tag/%E7%BC%96%E7%A8%8B‘ ] def parse(self, response): books = [] sel = Selector(response) info = sel.xpath(‘//li[@class="subject-item"]‘) if len(info) == 0: raise CloseSpider(‘---------------------End Search!---------------‘) f = open(‘books.data‘,‘a‘) for site in info.xpath(‘div[@class="info"]‘): book = DoubanItem() book[‘title‘] = site.xpath(‘h2/a/@title‘).extract()[0].encode(‘utf-8‘) book[‘link‘] = site.xpath(‘h2/a/@href‘).extract()[0].encode(‘utf-8‘) pub = site.xpath(‘div[@class="pub"]/text()‘).extract()[0].encode(‘utf-8‘) pub = pub.strip().split(‘/‘) book[‘author‘] = pub[0] book[‘price‘] = pub[-1] desc = site.xpath(‘p/text()‘).extract() book[‘desc‘] = desc[0].encode(‘utf-8‘) if (len(desc) != 0) else ‘‘ print(‘-----------------lALALALALALA----------------------------------------------‘) print(book[‘title‘]) print(book[‘link‘]) print(book[‘author‘]) print(book[‘price‘]) print(book[‘desc‘]) ss = "{‘title‘: ‘" + book[‘title‘] + "‘ , " + "‘link‘: ‘" + book[‘link‘] + "‘ , " + "‘author‘: ‘" + book[‘author‘] + "‘ , " + "‘price‘: ‘" + book[‘price‘] + "‘ , " + "‘desc‘: ‘" + book[‘desc‘] + "‘}" print ss target = json.dumps(ss, ensure_ascii=False) print target f.write(target+‘\n‘) print(‘-----------------lALALALALALA----------------------------------------------\n\n‘) books.append(book) yield book f.close() site = sel.xpath(‘//span[@class="next"]/a/@href‘).extract()[0] print(‘url: ‘ + response.url) print(‘site: ‘ + site) temp = response.url.split(‘/‘) new_url = temp[0] i = 1 while i < len(temp)-2: new_url += ‘/‘ + temp[i] i += 1 new_url += site new_url = new_url.encode(‘utf-8‘) print(‘url: ‘ + new_url) yield scrapy.Request(new_url, callback=self.parse)
python实现爬虫(一)--- Scrapy框架抓取豆瓣书籍信息