Scrapy是一個用python實現的開源爬蟲框架,簡單易用,功能強大,只須要在框架的基礎上自定義本身的分析規則便可,具體如何新建工程等都在官方文檔上面講解得很是清楚,官方文檔tutorial(http://doc.scrapy.org/en/latest/intro/tutorial.html)請保證下載較新版本的Scrapy(個人是0.24.2,scrapy -v)舊版本會出現一些問題。html
下面我使用Scrapy抓取豆瓣上面編程書籍的一些簡單信息python
1、準備爬取的頁面以下,新建一個douban工程:mysql
每一頁有20本書籍,雖然下面顯示由97頁,實際上只有50頁,也即1000本書籍,很是快就能夠抓取下來算法
(很是開心看到本身最喜歡的兩本書排在前兩位《算法導論》and《深刻理解計算機系統》 ^_^)sql
2、肯定所需信息爲(title, link, author, price, description),編輯items.py文件,添加所需的項數據庫
3、定義本身的爬蟲,在douban/douban/spiders/目錄下新建一個.py文件(此處sola.py)編程
1. 導入所需模塊,此處的爬蟲繼承自最簡單的Spider, 除此以外scrapy還實現了CrawlSpider(可自定義Rules)等功能更強大的爬蟲可使用json
2.肯定 爬蟲名字是sola,必須惟一 ; allowed_domains: 容許在book.douban.com 域名下抓取; 以及起始urlapp
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">的文本中(這裏須要對字符串進行處理,取出‘/’ 劃分的子串的第一個和最後一個),而description則在<p>元素中(後面有一些書籍沒有描述,因此這裏須要增長一些特殊處理), 因此獲得的對書籍的處理代碼以下:
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'] + "'}" target = json.dumps(ss, ensure_ascii=False) f.write(target+'\n') print('-----------------lALALALALALA----------------------------------------------\n\n') books.append(book) 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>
4、這樣子只要在主目錄下面運行 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)
5、將數據存儲到MySQL數據庫中,由於parse處理獲得的item都要提交到pipeline那裏通過process_item() 函數進一步的篩選才能最終保存,初始化的工程中,pipelines.py裏面的process_item()函數默認實現是對全部item都「pass「,因此須要修改這個函數自定義一些規則來篩選,此處將item保存到數據庫中。
首先要在MySQL數據庫中新建一個數據庫sola和一個表格book,這裏爲了終端顯示方便,只保存了title,author和price三個信息,爲了不陷入麻煩的mysql服務端的中文編碼問題,能夠在創建表格的時候默認使用 charset = utf8
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html import MySQLdb class DoubanPipeline(object): def __init__(self): #使用connect鏈接數據庫,編碼使用charset='utf8' self.conn = MySQLdb.connect(host='localhost', user='root', passwd='123', charset='utf8') #獲取一個cursor對象以執行命令 self.curs = self.conn.cursor() #選擇數據庫 self.conn.select_db('sola') def process_item(self, item, spider): #插入新元素 self.curs.execute('INSERT INTO book VALUES(%s, %s, %s)', (item["title"], item["author"], item["price"])) #執行操做後須要提交纔有效 self.conn.commit() return item