開始爬取網頁:(2)寫入源文件的爬取
為了使代碼易於修改,更清晰高效的爬取網頁,我們將代碼寫入源文件進行爬取。
主要分為以下幾個步驟:
一.使用scrapy創建爬蟲框架:
二.修改並編寫源代碼,確定我們要爬取的網頁及內容
三.開始爬取並存入文件(數據庫)
注:為了避免冗長的敘述,更直觀地理解,這里先講具體的操作方法,如果想要深入理解其原理,具體解釋在最后。
*操作方法:
1.創建爬蟲框架
打開命令行,使用cd命令,進入你想要創建文件的位置
scrapy startproject 文件夾名稱(假設為demo)
scrapy startproject demo
強大的scrapy會在當前路徑下自動生成爬蟲框架如下:demo/
scrapy.cfg #項目配置文件
demo/
__init__.py
items.py #設定數據存入的對象
pipelines.py #與數據庫交互
settings.py #爬蟲配置文件
spiders/
__init__.py
2.編輯源代碼,確定爬取方案
scrapy不僅僅幫助我們生成好了框架,還提供了很多內置的方法供我們使用,我們只需要利用這些方法並結合BeautifulSoup庫
進行網頁信息的篩選。
開始寫代碼吧!
首先打開items.py文件,修改如下:
1 # -*- coding: utf-8 -*-
2
3 # Define here the models for your scraped items
4 # 5 # See documentation in:
6 # http://doc.scrapy.org/en/latest/topics/items.html
7
8 import scrapy 9
10
11 class DemoItem(scrapy.Item): 12 # define the fields for your item here like:
13 name = scrapy.Field() 14 #pass
然后在spider目錄下新建spider.py,將下述代碼復制到其中
1 # -*- coding: utf-8 -*- 2 import scrapy 3 from bs4 import BeautifulSoup 4 from demo.items import DemoItem 5 6 class demo(scrapy.Spider): 7 name = "demo" 8 start_urls = ['http://www.cnblogs.com/KoalaDream/'] 9 10 def parse(self, response): 11 soup = BeautifulSoup(response.body) 12 tmp=soup.find_all('div',attrs={'class':'postTitle'}) 13 for i in range(0,len(tmp)): 14 item=DemoItem() 15 item['name']=tmp[i].text.encode('utf-8') 16 yield item
這段代碼的作用是爬取我的博客主頁的文章標題,具體語句的作用在后面有介紹。
3.開始爬取並存入文件
代碼編寫好了,我們要開始爬取網頁了,是不是很容易?
在命令行中,使用cd命令進入scrapy.cfg所在的目錄
scrapy crawl demo -o output.json
開始爬取,並將內容輸出到含有scrapy.cfg文件的目錄下output.json文件中
完成!
打開output.json文件,什么?怎么全是亂碼?
這是因為我們爬取下的中文字符,以unicode的編碼格式存儲在文件中。
想要看到中文字符,可以將輸出的內容復制,打開python,輸入print u"---"將復制的unicode碼粘貼,便可輸出對應的中文字符。
*代碼詳解
首先,設置編碼格式為utf-8,能夠顯示中文注釋。
在spider中,scrapy框架規定其中有三個必需的定義的成員。
name: 名字,spider的標識,我們爬取時在命令行中輸入的 scrapy crawl ____ 即為此處定義的name
start_urls:一個url列表,spider從這些網頁開始抓取
parse():一個方法,當start_urls里面的網頁抓取下來之后scrapy會自動調用這個方法解析網頁內容。
總的來說就是name告訴scrapy框架,我要運行哪個框架。然后scrapy會運行該框架,自動鏈接到start_url的web頁面,生成response對象,將爬取下源代碼存入response對象里面。
下面我們來看具體的parse()方法是如何工作的。
首先引入BeautifulSoup庫,它可以大大簡化我們的爬取過程。
然后我們打開要爬取的網頁即 http://www.cnblogs.com/KoalaDream/
查看源代碼。(chrome瀏覽器中,鼠標右鍵點擊某一個文章標題,點擊審查元素)
可以看到,每一個標題都是在一個<div>標簽里面,class屬性均為postTitle
於是,我們使用BeautifulSoup中提供的方法篩選出標題
tmp=soup.find_all('div',attrs={'class':'postTitle'})
將標題以列表的形式存入tmp中。
還記得我們剛剛修改過的items.py文件嗎?
下面我們要遍歷tmp中的每一項,並將其存入剛剛定義過的item中。
通過 item=DemoItem(),創建了一個DemoItem類的對象,下面的操作
item['name']=tmp[i].text.encode('utf-8')
左側的運算符有些讓人費解。因為想要操作類中的成員,一般使用item.name,這個操作符是怎么回事呢?
仔細觀察items.py文件中,DemoItem類繼承了scrapy.Item類。經試驗證明,如果把繼承去除,此運算符不再存在。
說明在scrapy.Item類中,對運算符[]進行了重載。個人感覺意義上和.操作符沒什么區別,我們直接使用此語法即可。
最后就是 yield item了
yield語法查了很久,已經被生成器,迭代器搞得有些暈。
如果想了解具體的yield語法,請看下面的鏈接
http://www.oschina.net/translate/improve-your-python-yield-and-generators-explained
對於這里的yield語句的作用,我說說我的理解,哪里不准確希望大家多多指教。
首先簡單介紹下yield語句:
yield用於函數,就像return,給函數一個返回值。不同的是,return標志着函數結束,而yield是讓函數暫停,直到這個函數再次被調用,會從暫停的地方繼續執行,而不是從頭開始。
在spider.py的代碼中,每循環一次,yield語句將列表中當前的值返回給parse。可是我們並沒有主動調用和返回。那么輸出的內容是哪里來的?
因為parse是在scrapy框架中,自動被調用的方法,我們可以推測出:
當告訴scrapy輸出到output.json文件中時,對於每一次yield的返回值,會被自動print到output.json文件中。然后scrapy會再次調用parse方法,從剛剛間斷的位置,即for循環的下一個列表項開始。如此循環,直到函數結束。這樣一來,tmp中的內容就被存放到item['name']中,然后被輸出到output.json中。
每一次yield返回的一個值就是所謂的生成器。
而每一次從暫停狀態,再被調用時,自動從暫停前的下一個對象開始,就是所謂的迭代器。
附:官方文檔
Scrapy http://doc.scrapy.org/en/latest/
