本章將從案例開始介紹python scrapy框架,更多內(nèi)容請參考:python學(xué)習(xí)指南
入門案例
學(xué)習(xí)目標(biāo)
- 創(chuàng)建一個Scrapy項目
- 定義提取的結(jié)構(gòu)化數(shù)據(jù)(Item)
- 編寫爬取網(wǎng)站的Spider并提取出結(jié)構(gòu)化數(shù)據(jù)(Item)
- 編寫Item Pipelines來存儲提取到的Item(即結(jié)構(gòu)化數(shù)據(jù))
一关霸、新建項目(scrapy startproject)
- 在開始爬取之前勿锅,必須創(chuàng)建一個新的Scrapy項目云石。進入自定義的項目目錄中,運行下列命令:
scrapy startproject cnblogSpider
- 其中,cnblogSpider為項目名稱嚼隘,可以看到將會創(chuàng)建一個cnblogSpider文件夾寄月,目錄結(jié)構(gòu)大致如下:
scrapy.cfg:項目部署文件
cnblogSpider/: 該項目的python模塊辜膝,之后可以在此加入代碼
cnblogSpider/items.py: 項目中的item文件。
cnblogSpider/pipelines.py: 項目中的Pipelines文件漾肮。
cnblogSpider/settings.py: 項目的配置文件厂抖。
cnblogSpider/spiders/: 放置Spider代碼的目錄。
二克懊、明確目標(biāo)(mySpider/items.py)
我們打算抓瘸栏ā:"http://www.cnblogs.com/miqi1992/default.html?page=2" 網(wǎng)站里博客地址七蜘、標(biāo)題、創(chuàng)建時間墙懂、文本橡卤。
打開cnblogSpider目錄下的items.py
item定義結(jié)構(gòu)化數(shù)據(jù)字段,用來保存爬取到的數(shù)據(jù)损搬,有點像Python中的dict,但是提供了一些額外的保護減少錯誤碧库。
可以通過創(chuàng)建一個scrapy.item類,并且定義類型為scrapy.Field的類屬性來定義一個Item(可以理解成類似于ORM的映射關(guān)系)巧勤。
接下來嵌灰,創(chuàng)建一個CnblogspiderItem類,和模型item模型(model)颅悉。
import scrapy
class CnblogspiderItem(scrapy.Item):
# define the fields for your item here like:
url = scrapy.Field()
time = scrapy.Field()
title = scrapy.Field()
content = scrapy.Field()
三沽瞭、制作爬蟲(spiders/cnblogsSpider.py)
爬蟲功能主要分兩步:
1. 爬數(shù)據(jù)
- 在當(dāng)前目錄下輸入命令,將在
cnblogSpider/spiders
目錄下創(chuàng)建一個名為cnblog
的爬蟲剩瓶,并制定爬取域的范圍:
scrapy genspider cnblog "cnblogs.com"
- 打開
cnblogSpider/spiders
目錄下的cnblog
驹溃,默認增加了下列代碼:
# -*- coding: utf-8 -*-
import scrapy
class CnblogSpider(scrapy.Spider):
name = 'cnblog'
allowed_domains = ['cnblogs.com']
start_urls = ['http://cnblogs.com/']
def parse(self, response):
pass
其實也可以由我們自行創(chuàng)建cnblog.py并編寫上面的代碼,只不過使用命令可以免去編寫固定代碼的麻煩
要建立一個Spider,你必須用scrapy.Spider類創(chuàng)建一個子類儒搭,并確定了三個強制的屬性和一個方法吠架。
-
name = ""
: 這個爬蟲的識別名稱,必須是唯一的搂鲫,在不同的爬蟲必須定義不同的名字傍药。 -
allow_domains=[]
: 是搜索的域名范圍,也就是爬蟲的約束區(qū)域魂仍,規(guī)定爬蟲只爬取這個域名下的網(wǎng)頁拐辽,不存在的URL會被忽略。 -
start_urls=()
:爬取的URL元祖/列表擦酌。爬蟲從這里開始爬取數(shù)據(jù)俱诸,所以,第一次下載的數(shù)據(jù)將會從這些urls開始赊舶。其他子URL將會從這些起始URL中繼承性生成睁搭。 -
parse(self, response)
:解析的方法,每個初始URL完成下載后將被調(diào)用笼平,調(diào)用的時候傳入從每一個URL傳回的Response對象來作為唯一參數(shù)园骆,主要作用如下:- 負責(zé)解析返回的網(wǎng)頁數(shù)據(jù)(respose.body),提取結(jié)構(gòu)化數(shù)據(jù)(生成item)
- 生成需要下一頁的URL請求
將start_urls的值改為需要爬取的第一個url:
start_urls=("http://www.cnblogs.com/miqi1992/default.html?page=2")
修改parse()方法
def parse(self, response):
filename = "cnblog.html"
with open(filename, 'w') as f:
f.write(response.body)
然后運行一下看看,在cnblogSpider目錄下運行:
scrapy crawl cnblog
是的寓调,就是cnblog,看上面代碼锌唾,它是CnblogSpider類的name屬性,也就是scrapy genspider
命令的唯一爬蟲名。
運行之后晌涕,如果打印的日志出現(xiàn)[scrapy]INFO: Spider closed(finished)
滋捶,代表執(zhí)行完成。之后當(dāng)前文件夾中就出現(xiàn)了一個cnblog.html文件余黎,里面就是我們剛剛要爬取的網(wǎng)頁的全部源代碼信息重窟。
#注意,Python2.x默認編碼環(huán)境是ASCII惧财,當(dāng)和取回的數(shù)據(jù)編碼格式不一致時厨姚,可能會造成亂碼;
#我們可以指定保存內(nèi)容的編碼格式煮甥,一般情況下噩死,我們可以在代碼最上方添加:
import os
reload(sys)
sys.setdefaultencoding('utf-8')
#這三行代碼是Python2.x里面解決中文編碼的萬能鑰匙惶室,警告這么多年的吐槽后Python3學(xué)乖了斋枢,默認編碼是Unicode了
2.爬數(shù)據(jù)
-
爬取整個網(wǎng)頁完畢帘靡,接下來就是取過程了,首先觀察頁面源碼:
頁面結(jié)構(gòu)
<div class="day">
<div class="dayTitle">...</div>
<div class="postTitle">...</div>
<div class="postCon">...</div>
</div>
- XPath表達式如下:
- 所有文章:.//*[@class='day']
- 文章發(fā)表時間:.//*[@class='dayTitle']/a/text()
- 文章標(biāo)題內(nèi)容:.//*[@class='postTitle']/a/text()
- 文章摘要內(nèi)容:.//*[@class='postCon']/div/text()
- 文章鏈接:.//*[@class='postTitle']/a/@href
是不是一目了然瓤帚?直接上XPath開始提取數(shù)據(jù)吧描姚。
- 我們之前在cnblogSpider/items.py里定義了一個CnblogItem類。這里引入進來
from cnblogSpider.items import CnblogspiderItem
- 然后將我們得到的數(shù)據(jù)封裝到一個
CnblogspiderItem
對象中戈次,可以保存每個博客的屬性:
form cnblogSpider.items import CnblogspiderItem
def parse(self, response):
# print(response.body)
# filename = "cnblog.html"
# with open(filename, 'w') as f:
# f.write(response.body)
#存放博客的集合
items = []
for each in response.xpath(".//*[@class='day']"):
item = CnblogspiderItem()
url = each.xpath('.//*[@class="postTitle"]/a/@href').extract()[0]
title = each.xpath('.//*[@class="postTitle"]/a/text()').extract()[0]
time = each.xpath('.//*[@class="dayTitle"]/a/text()').extract()[0]
content = each.xpath('.//*[@class="postCon"]/div/text()').extract()[0]
item['url'] = url
item['title'] = title
item['time'] = time
item['content'] = content
items.append(item)
#直接返回最后數(shù)據(jù)
return items
- 我們暫時先不處理管道轩勘,后面會詳細介紹。
保存數(shù)據(jù)
scrapy保存信息的最簡單的方法主要有四種怯邪, -o 輸出指定格式的文件绊寻,命令如下:
#json格式,默認為Unicode編碼
scrapy crawl cnblog -o cnblog.json
#json lines格式悬秉,默認為Unicode編碼
scrapy crawl cnblog -o cnblog.jsonl
#csv逗號表達式澄步,可用excel打開
scrapy crawl cnblog -o cnblog.csv
#xml格式
scrapy crawl cnblog -o cnblog.xml
思考
如果將代碼改成下面形式,結(jié)果完全一樣
請思考yield在這里的作用:
form cnblogSpider.items import CnblogspiderItem
def parse(self, response):
# print(response.body)
# filename = "cnblog.html"
# with open(filename, 'w') as f:
# f.write(response.body)
#存放博客的集合
# items = []
for each in response.xpath(".//*[@class='day']"):
item = CnblogspiderItem()
url = each.xpath('.//*[@class="postTitle"]/a/@href').extract()[0]
title = each.xpath('.//*[@class="postTitle"]/a/text()').extract()[0]
time = each.xpath('.//*[@class="dayTitle"]/a/text()').extract()[0]
content = each.xpath('.//*[@class="postCon"]/div/text()').extract()[0]
item['url'] = url
item['title'] = title
item['time'] = time
item['content'] = content
# items.append(item)
#將獲取到的數(shù)據(jù)交給pipelines
yield item
#直接返回最后數(shù)據(jù),不經(jīng)過pipelines
#return items