Scrapy架構架構
Scrapy架構介紹:
寫一個爬蟲,需要做很多的事情。比如:發送網絡請求、資料解析、資料存儲、反反爬蟲機制(更換ip代理、設定請求頭等)、異步請求等。這些工作如果每次都要自己從零開始寫的話,比較浪費時間。是以
Scrapy
把一些基礎的東西封裝好了,在他上面寫爬蟲可以變的更加的高效(爬取效率和開發效率)。是以真正在公司裡,一些上了量的爬蟲,都是使用
Scrapy
架構來解決。
Scrapy架構圖:
- 流程圖(1):
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN2XjlGcjAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9UFVNlXTqJWMw1mYwx2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4MDN4ADMxITM1ETOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
- 流程圖(2):
架構子產品功能:
1.爬蟲發送請求 并不是馬上發出去 而是給引擎
2.引擎發給排程器 排程器接收到url 以後 将url生成requests對象 存儲到隊列中
3.引擎從排程器中取出請求
4.引擎将requests對象 扔給下載下傳器
5.下載下傳器拿到請求從網上下載下傳資料 再将資料 組裝成response對象傳回給引擎
6.引擎拿到response對象 傳回給爬蟲
7.爬蟲對資料再進行分析 留下想要 的 資料 再傳回給 引擎
8.引擎再給管道 存到 redis 或者mysql 或者 mongodb中
引擎和下載下傳器 之間 有中間件 爬蟲和 引擎之間 也有中間件
-
:Scrapy Engine(引擎)
架構的核心部分。負責在Scrapy
和Spider
、ItemPipeline
、Downloader
中間通信、傳遞資料等。類似于汽車發動機Scheduler
-
:發送需要爬取的連結給引擎,最後引擎把其他子產品請求回來的資料再發送給爬蟲,爬蟲就去解析想要的資料。這個部分是Spider(爬蟲)
的,因為要爬取哪些連結,頁面中的哪些資料是我們需要的,都是由程式員自己決定。我們開發者自己寫
-
:負責接收引擎發送過來的請求,并按照一定的方式進行排列和整理,負責排程請求的順序等。Scheduler(排程器)
-
:負責接收引擎傳過來的下載下傳請求,然後去網絡上下載下傳對應的資料再交還給引擎。Downloader(下載下傳器)
-
:負責将Item Pipeline(管道)
傳遞過來的資料進行儲存。具體儲存在哪裡,應該看開發者自己的需求。Spider(爬蟲)
-
:可以擴充下載下傳器和引擎之間通信功能的中間件。Downloader Middlewares(下載下傳中間件)
-
:可以擴充引擎和爬蟲之間通信功能的中間件。Spider Middlewares(Spider中間件)
Scrapy快速入門
安裝和文檔:
- 安裝:通過
即可安裝。 如果出現 pyasn1的錯誤 裝一下 pip install pyasn1-modulespip install scrapy
- Scrapy官方文檔:http://doc.scrapy.org/en/latest
- Scrapy中文文檔:http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
注意:下載下傳指定的python版本及系統版本
- 在
上安裝
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
- 下載下傳 Twisted-18.9.0-cp36-cp36m-win_amd64.whl 然後放到指定的目錄下 純英文 沒權限限制 切換到這個目錄 pip install Twisted-18.9.0-cp36-cp36m-win_amd64.whl
- pip install scrapy
快速入門:
建立項目:
要使用
Scrapy
架構建立項目,需要通過指令來建立。首先進入到你想把這個項目存放的目錄。然後使用以下指令建立:
scrapy startproject [項目名稱]
目錄結構介紹:
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-oKD1NWYM-1600142893172)(目錄結構.png)]
以下介紹下主要檔案的作用:
1. items.py:用來存放爬蟲爬取下來資料的模型。
2. middlewares.py:用來存放各種中間件的檔案。爬蟲中間件 下載下傳中間件
3. pipelines.py:用來将`items`的模型存儲到本地磁盤中。
4. settings.py:本爬蟲的一些配置資訊(比如請求頭、多久發送一次請求、ip代理池等)。
5. scrapy.cfg:項目的配置檔案。
6. spiders包:以後所有的爬蟲,都是存放到這個裡面。
當引擎将下載下傳組裝的respons對象給 爬蟲的時候
爬蟲對資料進行分析
response.xpath() 詳情 ctrl+滑鼠點選 xpath 檢視 其它的分析方法 傳回的内容是 SelectorList
一下兩個都是将其轉成Unicode編碼 并提取出來
get() 傳回的是Selector 中的第一個文本
getall()傳回的是Selector 中的所有文本 是個清單
使用Scrapy架構爬取糗事百科段子:
使用指令建立一個爬蟲:
scrapy genspider qsbk "qiushibaike.com"
建立了一個名字叫做
qsbk
的爬蟲,并且能爬取的網頁隻會限制在
qiushibaike.com
這個域名下。
爬蟲代碼解析:
import scrapy
class QsbkSpider(scrapy.Spider):
name = 'qsbk'
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。
修改 settings.py
代碼:
settings.py
在做一個爬蟲之前,一定要記得修改
setttings.py
中的設定。兩個地方是強烈建議設定的。
1. `ROBOTSTXT_OBEY`設定為False。預設是True。即遵守機器協定,那麼在爬蟲的時候,scrapy首先去找robots.txt檔案,如果沒有找到。則直接停止爬取。
2. `DEFAULT_REQUEST_HEADERS`添加`User-Agent`。這個也是告訴伺服器,我這個請求是一個正常的請求,不是一個爬蟲。
完成的爬蟲代碼:
- 爬蟲部分代碼:
import scrapy from abcspider.items import QsbkItem class QsbkSpider(scrapy.Spider): name = 'qsbk' allowed_domains = ['qiushibaike.com'] start_urls = ['https://www.qiushibaike.com/text/'] def parse(self, response): outerbox = response.xpath("//div[@id='content-left']/div") items = [] for box in outerbox: author = box.xpath(".//div[contains(@class,'author')]//h2/text()").extract_first().strip() content = box.xpath(".//div[@class='content']/span/text()").extract_first().strip() item = QsbkItem() item["author"] = author item["content"] = content items.append(item) return items
- items.py部分代碼:
import scrapy class QsbkItem(scrapy.Item): author = scrapy.Field() content = scrapy.Field()
- pipeline部分代碼:
import json class AbcspiderPipeline(object): def __init__(self): self.items = [] def process_item(self, item, spider): self.items.append(dict(item)) print("="*40) return item def close_spider(self,spider): with open('qsbk.json','w',encoding='utf-8') as fp: json.dump(self.items,fp,ensure_ascii=False)
運作scrapy項目:
運作scrapy項目。需要在終端,進入項目所在的路徑,然後
scrapy crawl [爬蟲名字]
即可運作指定的爬蟲。如果不想每次都在指令行中運作,那麼可以把這個指令寫在一個檔案中。以後就在pycharm中執行運作這個檔案就可以了。比如現在新建立一個檔案叫做
start.py
,然後在這個檔案中填入以下代碼:
from scrapy import cmdline
cmdline.execute("scrapy crawl qsbk".split())
pipline 管道 用來将資料存儲在檔案或者資料庫中 有三個方法是常用的
- open_spider(self,spider) #當爬蟲被打開的時候執行
-
process_item(self, item, spider)當爬蟲 有item 傳遞過來的時候 調用
- close_spider(self,spider) #當爬蟲關閉的時候調用
scrapy 導出器 JsonItemExporter 、JsonLinesItemExporter
1.JsonItemExporter
每次把資料添加到記憶體中 最後統一寫到磁盤中 好處存儲的是一個滿足json規則的資料 缺點:資料量大 耗記憶體
from scrapy.exporters import JsonItemExporter,JsonLinesItemExporter
class QsbkPipeline(object):
def __init__(self):
self.fp = open("qsbk.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.fp.close()
print("爬蟲結束了......")
2.JsonLinesItemExporter
每次調用export_item 存到磁盤中 好處 不耗記憶體 直接持久化 安全 壞處 是每個字典是一行 整個檔案不滿足json規則
from scrapy.exporters import JsonLinesItemExporter
class QsbkPipeline(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("爬蟲結束了......")
糗事百科 分頁
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)
CrawlSpider
在上一個糗事百科的爬蟲案例中。我們是自己在解析完整個頁面後擷取下一頁的url,然後重新發送一個請求。有時候我們想要這樣做,隻要滿足某個條件的url,都給我進行爬取。那麼這時候我們就可以通過
CrawlSpider
來幫我們完成了。
CrawlSpider
繼承自
Spider
,隻不過是在之前的基礎之上增加了新的功能,可以定義爬取的url的規則,以後scrapy碰到滿足條件的url都進行爬取,而不用手動的
yield Request
。
CrawlSpider爬蟲:
建立CrawlSpider爬蟲:
之前建立爬蟲的方式是通過
scrapy genspider [爬蟲名字] [域名]
的方式建立的。如果想要建立
CrawlSpider
爬蟲,那麼應該通過以下指令建立:
scrapy genspider -t crawl [爬蟲名字] [域名]
LinkExtractors連結提取器:
使用
LinkExtractors
可以不用程式員自己提取想要的url,然後發送請求。這些工作都可以交給
LinkExtractors
,他會在所有爬的頁面中找到滿足規則的
url
,實作自動的爬取。以下對
LinkExtractors
類做一個簡單的介紹:
class scrapy.linkextractors.LinkExtractor(
allow = (),
deny = (),
allow_domains = (),
deny_domains = (),
deny_extensions = None,
restrict_xpaths = (),
tags = ('a','area'),
attrs = ('href'),
canonicalize = True,
unique = True,
process_value = None
)
主要參數講解:
- allow:允許的url。所有滿足這個正規表達式的url都會被提取。
- deny:禁止的url。所有滿足這個正規表達式的url都不會被提取。
- allow_domains:允許的域名。隻有在這個裡面指定的域名的url才會被提取。
- deny_domains:禁止的域名。所有在這個裡面指定的域名的url都不會被提取。
- restrict_xpaths:嚴格的xpath。和allow共同過濾連結。
Rule規則類:
定義爬蟲的規則類。以下對這個類做一個簡單的介紹:
class scrapy.spiders.Rule(
link_extractor,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
)
主要參數講解:
- link_extractor:一個
對象,用于定義爬取規則。LinkExtractor
- callback:滿足這個規則的url,應該要執行哪個回調函數。因為
使用了CrawlSpider
作為回調函數,是以不要覆寫parse
作為回調函數自己的回調函數。parse
- follow:指定根據該規則從response中提取的連結是否需要跟進。
- process_links:從link_extractor中擷取到連結後會傳遞給這個函數,用來過濾不需要爬取的連結。
Scrapy Shell
我們想要在爬蟲中使用xpath、beautifulsoup、正規表達式、css選擇器等來提取想要的資料。但是因為
scrapy
是一個比較重的架構。每次運作起來都要等待一段時間。是以要去驗證我們寫的提取規則是否正确,是一個比較麻煩的事情。是以
Scrapy
提供了一個shell,用來友善的測試規則。當然也不僅僅局限于這一個功能。
打開Scrapy Shell:
打開cmd終端,進入到
Scrapy
項目所在的目錄,然後進入到
scrapy
架構所在的虛拟環境中,輸入指令
scrapy shell [連結]
。就會進入到scrapy的shell環境中。在這個環境中,你可以跟在爬蟲的
parse
方法中一樣使用了。
scrapy shell https://www.qiushibaike.com/text/page/1/
>>> title = response.xpath("//div[@class='col1 old-style-col1']/div//h2/text()").get().strip()
>>> print(title)
石頭,剪刀,破布 #代表成功了
Request和Response對象
Request對象:
Request對象在我們寫爬蟲,爬取一頁的資料需要重新發送一個請求的時候調用。這個類需要傳遞一些參數,其中比較常用的參數有:
-
:這個request對象發送請求的url。url
-
:在下載下傳器下載下傳完相應的資料後執行的回調函數。callback
-
:請求的方法。預設為method
方法,可以設定為其他方法。GET
-
:請求頭,對于一些固定的設定,放在headers
中指定就可以了。對于那些非固定的,可以在發送請求的時候指定。settings.py
-
:比較常用。用于在不同的請求之間傳遞資料用的。meta
-
:編碼。預設的為encoding
,使用預設的就可以了。utf-8
-
:表示不由排程器過濾。在執行多次重複的請求的時候用得比較多。dont_filter
-
:在發生錯誤的時候執行的函數。errback
Response對象:
Response對象一般是由
Scrapy
給你自動建構的。是以開發者不需要關心如何建立
Response
對象,而是如何使用他。
Response
對象有很多屬性,可以用來提取資料的。主要有以下屬性:
- meta:從其他請求傳過來的
屬性,可以用來保持多個請求之間的資料連接配接。meta
- encoding:傳回目前字元串編碼和解碼的格式。
- text:将傳回來的資料作為
字元串傳回。unicode
- body:将傳回來的資料作為
字元串傳回。bytes
- xpath:xapth選擇器。
- css:css選擇器。
發送POST請求:
有時候我們想要在請求資料的時候發送post請求,那麼這時候需要使用
Request
的子類
FormRequest
來實作。如果想要在爬蟲一開始的時候就發送
POST
請求,那麼需要在爬蟲類中重寫
start_requests(self)
方法,并且不再調用
start_urls
裡的url。