多爬蟲實現之三 -- 多爬蟲檔案
阿新 • • 發佈:2018-12-27
目標
- 優化現有的爬蟲結構,實現同時開始執行多個爬蟲
1 為什麼需要優化現有的爬蟲結構
當爬蟲比較少的時候,我們的專案結構相對合理,但是當要抓取的網站比較多的時候,可以借鑑scrapy的方法,把不同網站的爬蟲分別在不同的py檔案中編寫,之後放在一個目錄下;同時,我們很多時候還希望能夠有同時啟動專案中的所有的爬蟲
2 將多個爬蟲類分離為多個爬蟲檔案爬蟲檔案
為了解耦合,應將每個站點的爬蟲寫為單獨一個py檔案,因此更改一下放置爬蟲的模組,結構如下:
- 專案資料夾 -- main.py -- spiders -- __init__.py -- baidu.py -- douban.py -- settings.py
其中baidu.py
和douban.py
分別是抓取百度和豆瓣的爬蟲檔案
baidu.py
:
# project_dir/spiders/baidu.py
from scrapy_plus.core.spider import Spider
# 繼承框架的爬蟲基類
class BaiduSpider(Spider):
start_urls = ['http://www.baidu.com'] # 設定初始請求url
douban.py
: 抓取豆瓣電影top250的列表頁資訊
# project_dir/spiders/douban.py from scrapy_plus.core.spider import Spider from scrapy_plus.http.request import Request from scrapy_plus.item import Item class DoubanSpider(Spider): start_urls = [] # 重寫start_requests方法後,這個屬性就沒有設定的必要了 def start_requests(self): # 重寫start_requests方法,返回多個請求 base_url = 'http://movie.douban.com/top250?start=' for i in range(0, 250, 25): # 逐個返回第1-10頁的請求屬相 url = base_url + str(i) yield Request(url) def parse(self, response): '''解析豆瓣電影top250列表頁''' title_list = [] # 儲存所有的 for li in response.xpath("//ol[@class='grid_view']/li"): # 遍歷每一個li標籤 # title = li.xpath(".//span[@class='title'][1]/text()") # 提取該li標下的 標題 # title_list.append(title[0]) detail_url = li.xpath(".//div[@class='info']/div[@class='hd']/a/@href")[0] yield Request(detail_url, parse="parse_detail") # 發起詳情頁的請求,並指定解析函式是parse_detail方法 # yield Item(title_list) # 返回標題 def parse_detail(self, response): '''解析詳情頁''' print('詳情頁url:', response.url) # 列印一下響應的url return [] # 由於必須返回一個容器,這裡返回一個空列表
-
對main.py進行相應修改,測試新增的douban爬蟲
from scrapy_plus.core.engine import Engine # 匯入引擎 from spiders.baidu import BaiduSpider from spiders.douban import DoubanSpider if __name__ == '__main__': # spider = BaiduSpider() # 例項化爬蟲物件 douban_spider = DoubanSpider() # 例項化爬蟲物件 engine = Engine(douban_spider) # 傳入爬蟲物件 engine.start() # 啟動引擎
3 同時執行多個不同的爬蟲
如把豆瓣爬蟲和百度爬蟲一起啟動並執行
傳入形式:並用字典的形式傳入多個爬蟲:
main.py
# project_dir/main.py
from scrapy_plus.core.engine import Engine # 匯入引擎
from spiders.baidu import BaiduSpider
from spiders.douban import DoubanSpider
if __name__ == '__main__':
baidu_spider = BaiduSpider() # 例項化爬蟲物件
douban_spider = DoubanSpider() # 例項化爬蟲物件
spiders = {'baidu':baidu_spider, 'douban':douban_spider}
engine = Engine(spiders) # 傳入爬蟲物件
engine.start() # 啟動引擎
在引擎中用到爬蟲物件的地方都要做相應的修改
engine.py
:
'''引擎
a. 對外提供整個的程式的入口
b. 依次呼叫其他元件對外提供的介面,實現整個框架的運作(驅動)
'''
......
class Engine(object):
def __init__(self, spiders): # 接收外部傳入的多個爬蟲物件
self.spiders = spiders # 爬蟲物件
......
......
def _start_requests(self):
'''向排程器新增初始請求'''
# 1. 爬蟲模組發出初始請求
for spider_name, spider in self.spiders.items():
for start_request in spider.start_requests():
# 2. 把初始請求新增給排程器
# 利用爬蟲中介軟體預處理請求物件
start_request = self.spider_mid.process_request(start_request)
start_request.spider_name = spider_name #為請求物件繫結它所屬的爬蟲的名稱
self.scheduler.add_request(start_request)
def _execute_request_response_item(self):
'''根據請求、發起請求獲取響應、解析響應、處理響應結果'''
......
spider = self.spiders[request.spider_name] # 根據請求的spider_name屬性,獲取對應的爬蟲物件
# 5. 利用爬蟲的解析響應的方法,處理響應,得到結果
parse = getattr(spider, request.parse) # 獲取對應的解析函式
results = parse(response) # parse函式的返回值是一個容器,如列表或者生成器物件
for result in results:
# 6. 判斷結果物件
# 6.1 如果是請求物件,那麼就再交給排程器
if isinstance(result, Request):
# 利用爬蟲中介軟體預處理請求物件
result = self.spider_mid.process_request(result)
result.spider_name = request.spider_name # 為請求物件繫結它所屬的爬蟲的名稱
self.scheduler.add_request(result)
# 6.2 否則,就交給管道處理
......
......
安裝程式碼,並執行main.py
,直到除錯成功
4 再次改進,將每個爬蟲的名稱直接設定為爬蟲類的一個屬性
參考:
class BaiduSpider(Spider):
name = 'baidu' # 為爬蟲命名
start_urls = ['http://www.baidu.com'] # 設定初始請求url
'''那麼main.py就可以按照這樣的方式設定key值'''
spiders = {BaiduSpider.name: baidu_spider, DoubanSpider.name: douban_spider}