爬蟲
第八天
scrapy框架流程
其流程可以描述如下:
-
調度器把requests–>引擎–>下載中間件—>下載器
-
下載器發送請求,獲取響應---->下載中間件---->引擎—>爬蟲中間件—>爬蟲
-
爬蟲提取數據,分爲兩類:
-
提取的是url地址,組裝成request對象---->爬蟲中間件—>引擎—>調度器
-
提取數據—>引擎—>管道
-
-
管道進行數據的處理和保存
注意:
- 圖中綠色線條的表示數據的傳遞
- 注意圖中中間件的位置,決定了其作用
- 注意其中引擎的位置,所有的模塊之前相互獨立,只和引擎進行交互
scrapy的入門使用
-
創建工程
scrapy startproject pro_name(工程名稱)
-
創建爬蟲文件
cd pro_name
srcapy genspider spider_name allowed_domain
-
找spdier_name.py文中完成爬蟲代碼
class ItcastSpider(scrapy.Spider): name = 'itcast' # 爬蟲名 allowed_domains = ['itcast.cn'] # 允許爬去的域名範圍 start_urls = ['http://www.itcast.cn/channel/teacher.shtml'] # 起始的url def parse(self, response): # start_url發請求後的響應的處理函數 li_list = response.xpath("//div[@class='tea_con']/div/ul/li") for li in li_list: item = {} item['name'] = li.xpath(".//h3/text()").extract_first() item['title'] = li.xpath(".//h3/text()").extract_first() item['desc'] = li.xpath(".//p/text()").extract_first() print(item) # none,request,item ,dict
如果parse函數不存在,會觸發父類scrapy.Spider中的parse函數,拋出異常
def parse(self, response): raise NotImplementedError('{}.parse callback is not defined'.format(self.__class__.__name__))
關於
extract()
和extract_first()
的使用- response.xpath() 返回的是一個特殊列表,該列表具有extract()和extract_first()方法
# 特殊列表返回的元素項是 Selector對象 <Selector xpath="//div[@class='tea_con']/div/ul/li//h3/text()" data='朱老師'> ret = response.xpath("//div[@class='tea_con']/div/ul/li//h3/text()") # 如果要提取想要的數據 ret.extract() # 從ret特殊列表中出去每個selector的data值,放入新列表
-
如果由於xpath寫的有問題,導致返回的列表爲空,從中獲取字符串數據時,可以使用 extract_first()
ret.extract_first() # 判斷ret列表的長度,如果長度爲0,返回None,否則取出列表第0項的元素selector中的data屬性值並返回
-
啓動爬蟲
scrapy crawl spider_name
yield作用
yield出現在函數中,將函數提升爲一個生成器,調用test()會返回一個生成器對象(不會執行函數代碼),當調用生成器的next時候,函數會在執行遇到yield的時候返回,再次調用next的時候,會從上次yield的位置繼續執行,直到遇到下一次的yield
def test():
print("***************")
for i in range(10):
print("-------------")
yield i
print("++++++++++++++")
a = test()
x = next(a)
print(x)
y = next(a)
print(y)
** 生成器是一種特殊的迭代器 **
管道 PIpline
-
創建管道對象,在piplines.py中
class MyspiderPipeline(object): def process_item(self, item, spider): # 被引擎調用,傳遞數據item和爬蟲對象 if spider.name == "itcast": print(item) return item # 如果存在多個管道的情況下,必須有,否則下一個管道接不到數據item
-
開啓管道,在配置文件中開啓管道
ITEM_PIPELINES = { 'myspider.pipelines.MyspiderPipeline': 300, # 300表示當前管道處理數據優先級,數字越小,優先級越高 }
-
將爬蟲的數據通過yield的方式交給引擎,引擎再交給管道
class ItcastSpider(scrapy.Spider): name = 'itcast' allowed_domains = ['itcast.cn'] start_urls = ['http://www.itcast.cn/channel/teacher.shtml'] def parse(self, response): li_list = response.xpath("//div[@class='tea_con']/div/ul/li//h3/text()") for li in li_list: item = {} item['name'] = li.xpath(".//h3/text()").extract_first() item['title'] = li.xpath(".//h3/text()").extract_first() item['desc'] = li.xpath(".//p/text()").extract_first() yield item # none,request,item ,dict
配置文件基本使用
設置日誌等級
LOG_LEVEL = "WARNING"
# 一般情況下,不建議設置“Waring”,很多造成爬蟲抓取數據失敗的原因都是在DEBUG等級顯示的,設置爲WARNING就不會顯示了
設置UA
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
構造請求
-
scrapy.Request(url,callback,meta,dont_filter)
- url:請求地址,必須是完整的url地址
- callback:該請求對應的響應的處理函數
- meta:在不同的解析函數中進行數據傳遞
- dont_filter:默認時False,即過濾請求,scrapy框架默認會過濾重複的請求,相同url的請求只進行一次,第二次會被調度器攔截過濾掉
-
response.follow(url,callback)
- url:請求地址,可以不完整,該方法會自動補充完整