Scrapy框架架構
什麼是Scrapy框架
scrapy 是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架,我們只需要實現少量代碼,就能夠快速的抓取到數據內容。Scrapy 使用了 Twisted(其主要對手是Tornado)異步網絡框架來處理網絡通訊,可以加快我們的下載速度,不用自己去實現異步框架,並且包含了各種中間件接口,可以靈活的完成各種需求。
Scrapy架構圖
各模塊的功能:
- Scrapy Engine(引擎):Scrapy框架的核心部分。負責在Spider和ItemPipeline、Downloader、Scheduler中間通信、傳遞數據等。
- Spider(爬蟲):發送需要爬取的鏈接給引擎,最後引擎把其他模塊請求回來的數據再發送給爬蟲,爬蟲就去解析想要的數據。這個部分是我們開發者自己寫的,因爲要爬取哪些鏈接,頁面中的哪些數據是我們需要的,都是由程序員自己決定。
- Scheduler(調度器):負責接收引擎發送過來的請求,並按照一定的方式進行排列和整理,負責調度請求的順序等。
- Downloader(下載器):負責接收引擎傳過來的下載請求,然後去網絡上下載對應的數據再交還給引擎。
- Item Pipeline(管道):負責將Spider(爬蟲)傳遞過來的數據進行保存。具體保存在哪裏,應該看開發者自己的需求。
- Downloader Middlewares(下載中間件):可以擴展下載器和引擎之間通信功能的中間件。
- Spider Middlewares(Spider中間件):可以擴展引擎和爬蟲之間通信功能的中間件。
scrapy框架的工作流程:
1.首先Spiders(爬蟲)將需要發送請求的url(requests)經ScrapyEngine(引擎)交給Scheduler(調度器)。
2.Scheduler(排序,入隊)處理後,經ScrapyEngine,DownloaderMiddlewares(可選,主要有User_Agent, Proxy代理)交給Downloader。
3.Downloader向互聯網發送請求,並接收下載響應(response)。將響應(response)經ScrapyEngine,SpiderMiddlewares(可選)交給Spiders。
4.Spiders處理response,提取數據並將數據經ScrapyEngine交給ItemPipeline保存(可以是本地,可以是數據庫)。
提取url重新經ScrapyEngine交給Scheduler進行下一個循環。直到無Url請求程序停止結束。
Scrapy的安裝
1.安裝:通過pip install scrapy即可安裝。
2. Scrapy官方文檔:http://doc.scrapy.org/en/latest
3.Scrapy中文文檔:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
注意:
在ubuntu上安裝scrapy之前,需要先安裝以下依賴:
sudo apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev,然後再通過pip install scrapy安裝。
如果在windows系統下,提示這個錯誤ModuleNotFoundError: No module named 'win32api',那麼使用以下命令可以解決:pip install pypiwin32。
創建項目
要使用Scrapy框架創建項目,需要通過命令來創建。首先進入到你想把這個項目存放的目錄。然後使用以下命令創建:
scrapy startproject [項目名稱]
目錄結構:
- items.py:用來存放爬蟲爬取下來數據的模型。
- middlewares.py:用來存放各種中間件的文件。
- pipelines.py:用來將items的模型存儲到本地磁盤中。
- settings.py:本爬蟲的一些配置信息(比如請求頭、多久發送一次請求、ip代理池等)。
- scrapy.cfg:項目的配置文件。
- spiders包:以後所有的爬蟲,都是存放到這個裏面。
爬取溴事百科
1.使用命令創建一個爬蟲:
scrapy genspider choushibaikespider "qiushibaike.com"
創建了一個名字叫做choushibaikespider的爬蟲,並且能爬取的網頁只會限制在qiushibaike.com這個域名下。
choushibaikespider.py代碼解析:
import scrapy
class ChoushibaikespiderSpider(scrapy.Spider):
name = 'choushibaikespider'
allowed_domains = ['qiushibaike.com']
start_urls = ['http://qiushibaike.com/']
def parse(self, response):
pass
其實這些代碼我們完全可以自己手動去寫,而不用命令。只不過是不用命令,自己寫這些代碼比較麻煩。
要創建一個Spider,那麼必須自定義一個類,繼承自scrapy.Spider,然後在這個類中定義三個屬性和一個方法。
(1) name:這個爬蟲的名字,名字必須是唯一的。
(2) allow_domains:允許的域名。爬蟲只會爬取這個域名下的網頁,其他不是這個域名下的網頁會被自動忽略。
(3). start_urls:爬蟲從這個變量中的url開始。
(4). parse:引擎會把下載器下載回來的數據扔給爬蟲解析,爬蟲再把數據傳給這個parse方法。這個是個固定的寫法。這個方法的作用有兩個,第一個是提取想要的數據。第二個是生成下一個請求的url。
2.修改settings.py代碼:
在做一個爬蟲之前,一定要記得修改setttings.py中的設置。兩個地方是強烈建議設置的。
1. ROBOTSTXT_OBEY設置爲False。默認是True。即遵守機器協議,那麼在爬蟲的時候,scrapy首先去找robots.txt文件,如果沒有找到。則直接停止爬取。
2.DEFAULT_REQUEST_HEADERS添加User-Agent。這個也是告訴服務器,我這個請求是一個正常的請求,不是一個爬蟲。
3.完成代碼:
choushibaikespider.py
(1).response是一個scrapy.http.response.html.HtmlResponse
對象。可以執行xpath
和css
語法來提取數據。
(2). 提取出來的數據,是一個Selector
或者是一個SelectorList
對象。如果想要獲取其中的字符串。那麼應該執行getall
或者get
方法。
(3). getall方法:獲取Selector
中的所有文本。返回的是一個列表。
(4). get方法:獲取的是Selector
中的第一個文本。返回的是一個str類型。
(5). 如果數據解析回來,要傳給pipline處理。那麼可以使用yield
來返回。或者是收集所有的item。最後統一使用return返回。
# -*- coding: utf-8 -*-
import scrapy
from choushibaike.items import ChoushibaikeItem
class ChoushibaikespiderSpider(scrapy.Spider):
name = 'choushibaikespider'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/page/1/']
base_domain = 'https://www.qiushibaike.com'
def parse(self, response):
# SelectorList
duanzidivs=response.xpath("//div[@id='content-left']/div")
for duanzidiv in duanzidivs:
# Selector
author = duanzidiv.xpath(".//h2/text()").get().strip()
content = duanzidiv.xpath(".//div[@class='content']//text()").getall()
content = "".join(content).strip()
item = ChoushibaikeItem(author=author,content=content)
yield item
next_url = response.xpath("//ul[@class='pagination']/li[last()]/a/@href").get()
if not next_url:
return
else:
yield scrapy.Request(self.base_domain+next_url,callback=self.parse)
items.py
(6).item:建議在items.py
中定義好模型。以後就不要使用字典。
import scrapy
class ChoushibaikeItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
author = scrapy.Field()
content = scrapy.Field()
pipelines.py
(7). pipeline:這個是專門用來保存數據的。其中有三個方法是會經常用的。
* open_spider(self,spider)
:當爬蟲被打開的時候執行。
* process_item(self,item,spider)
:當爬蟲有item傳過來的時候會被調用。
* close_spider(self,spider)
:當爬蟲關閉的時候會被調用。
要激活piplilne,應該在settings.py
中,設置ITEM_PIPELINES
。示例如下:
ITEM_PIPELINES = { 'choushibaike.pipelines.ChoushibaikePipeline': 300, }
import json
class ChoushibaikePipeline(object):
def __init__(self):
self.fp = open("duanzhi.js",'w',encoding="utf-8")
def open_spider(self,spider):
print("爬蟲開始...")
def process_item(self, item, spider):
item_json = json.dumps(dict(item),ensure_ascii=False)
self.fp.write(item_json)
return item
def close_spider(self,spider):
self.fp.close()
print("爬蟲結束了.")
4.運行爬蟲
運行scrapy項目。需要在終端,進入項目所在的路徑,然後scrapy crawl [爬蟲名字]即可運行指定的爬蟲。如果不想每次都在命令行中運行,那麼可以把這個命令寫在一個文件中。以後就在pycharm中執行運行這個文件就可以了。比如現在新創建一個文件叫做start.py,然後在這個文件中填入以下代碼:
from scrapy import cmdline
cmdline.execute("scrapy crawl choushibaikespider".split())
JsonItemExporter和JsonLinesItemExporter
保存json數據的時候,可以使用這兩個類,讓操作變得得更簡單。
JsonItemExporter
:這個是每次把數據添加到內存中。最後統一寫入到磁盤中。好處是,存儲的數據是一個滿足json規則的數據。壞處是如果數據量比較大,那麼比較耗內存。
from scrapy.exporters import JsonItemExporter
class ChoushibaikePipeline(object):
def __init__(self):
self.fp = open("duanzi.json",'wb')
self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
self.exporter.start_exporting()
def open_spider(self,spider):
print('爬蟲開始了...')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self,spider):
self.exporter.finish_exporting()
self.fp.close()
print('爬蟲結束了...')
JsonLinesItemExporter
:這個是每次調用export_item
的時候就把這個item存儲到硬盤中。壞處是每一個字典是一行,整個文件不是一個滿足json格式的文件。好處是每次處理數據的時候就直接存儲到了硬盤中,這樣不會耗內存,數據也比較安全。
from scrapy.exporters import JsonLinesItemExporter
class ChoushibaikePipeline(object):
def __init__(self):
self.fp = open("duanzi.json",'wb')
self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8')
def open_spider(self,spider):
print('爬蟲開始了...')
def process_item(self, item, spider):
self.exporter.export_item(item)
return item
def close_spider(self,spider):
self.fp.close()
print('爬蟲結束了...')
注:參考網易課堂知了課程。