scrapy框架(一)
scrapy框架的介紹
scrapy是一個開源和協作的框架。是一個快速、簡單、並且可擴充套件的方法。Scrapy使用了非同步網路框架來處理網路通訊,可以獲得較快的下載速度,因此,我們不需要去自己實現非同步框架。並且,Scrapy包含了各種中介軟體介面,可以靈活的完成各種需求。所以我們只需要定製開發幾個模組就可以輕鬆的實現一個爬蟲,用來抓取網頁上的各種內容。
scrapy爬蟲的優點:
<1>內建的css選擇器和xpath表示式; <2>擴充套件性強,可使用signals和api(中介軟體,外掛,管道)新增自定義功能; <3>多用於session,cookies,http認證,user-agent,robots.txt,抓取深度限制的中介軟體和外掛 <4>scrapy內建teinet console,可用於debug; <5>健壯的編碼支援。
scrapy
框架的組成
從上圖中,可以看出scrapy
框架由7個部分組成,分別是5個元件和2箇中間件。
1.引擎(EGINE
)
引擎(egine)是整個框架的核心部分,維繫著整個框架的生命。通過它,可以處理整個系統的資料流,並在某些動作時觸發事務。
2.排程器(SCHEDULER
)
用來接受引擎發過來的請求, 傳入佇列中, 並在引擎再次請求的時候返回. 可以想像成一個URL(抓取網頁的網址或者說是連結)的優先佇列, 由它來決定下一個要抓取的網址是什麼, 同時比較強大的功能就是:可以對以前已經抓取過得連結進去重。
3.下載器(DOWNLOADER
)
用於下載網頁內容, 並將網頁內容返回給Spider(Scrapy下載器是建立在twisted這個高效的非同步模型上的),其實是將抓取的內容返回給引擎,引擎然後將網站的內容返回給spider。spider將內容進行解析。提取有用的資料或者進行下一步的抓取,或者將資料結構化給pipeline。
4.爬蟲(SPIDERS
)
SPIDERS是開發人員自己定義的類,用來解析responses,並且提取items, 獲取重新發送新請求
5.專案管道(TIEM PIPLINES
)
在items被提取後負責處理它們,主要包括清理、驗證,持久化(比如存到資料庫中)等操作
6.下載中介軟體(DOWNLOADER MIDDLEWARES
)
位於Scrapy
引擎和下載器之間,主要是用來處理從EGINE
傳到DOWNLOADER
的請求request,已經從DOWNLOADER
傳到EGINE
的響應response。
process a request just before it is sent to the Downloader (i.e. right before Scrapy sends the request to the website); change received response before passing it to a spider; send a new Request instead of passing received response to a spider; pass response to a spider without fetching a web page; silently drop some requests.
7.爬蟲中介軟體(Spider middlewares
)
位於EGINE
和SPIDERS
之間,主要工作是處理SPIDERS
的輸入(responses)和輸出(request)
官網連結:https://docs.scrapy.org/en/latest/topics/architecture.html
Srapy
框架的安裝
windows平臺安裝
step1 pip install wheel
step2 pip install lxml
step3 pip install pyopenssl
step4 pip install pywin32
step5 pip install Twisted-20.3.0-cp38-cp38-win_amd64.whl
step6 pip install scrapy
建立Scrapy
專案
# scrapy可執行檔案的路徑
D:\Python\Python38\Scripts\scrapy.exe
# 建立scrapy專案
E:\>scrapy startproject firstscrapy
# 建立爬蟲
scrapy genspider 爬蟲名 爬蟲地址
E:\firstscrapy\firstscrapy\spiders>scrapy genspider chouti dig.chouti.com
# 執行爬蟲
# 帶執行日誌
scrapy crawl chouti
# 不帶執行日誌
scrapy crawl chouti --nolog
# 支援右鍵執行爬蟲
<1>建立可執行檔案run.py
<2>在run.py中輸入:
from scrapy.cmdline import execute
execute(['scrapy','crawl','chouti','--nolog']) # 這裡的‘chouti’是類中定義的name
scrapy
專案的目錄結構
firstscrapy # 專案名稱
firstscrapy # 包
spiders # 所有爬蟲的包
__init__.py
chouti.py
__init__.py
items.py # 一個一個的類
middlewares.py # 中介軟體(爬蟲,下載中介軟體都寫在這)
pipelines.py # 持久化相關寫在這(items.py中類的物件
run.py # 執行爬蟲
settings.py # 配置檔案
scrapy.cfg # 上線相關
settings
配置檔案
1 預設情況,scrapy會去遵循爬蟲協議
2 修改配置檔案引數,強行爬取,不遵循協議
-ROBOTSTXT_OBEY = False
3 USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
4 LOG_LEVEL='ERROR'
使用scrapy
框架爬取抽屜新聞
使用解析的三種方式
使用第三方解析:
from bs4 import BeautifulSoup
def parse(self, response):
print(response.text)
# 解析資料(第一種方案,自己解析,bs4,lxml)
soup=BeautifulSoup(response.text,'lxml')
divs=soup.find_all(class_='link-title')
for div in divs:
print(div.text)
繼續爬取其他網址:
def parse(self, response):
# 以後解析都在這
print(response.status)
# 假設解析出一個網址(繼續爬取)
# don_filter=True 不過濾除了allowed_domains之外的URL
return Request('https://www.baidu.com/',dont_filter=True)
使用自帶的解析方式:
# 取文字
-css選擇器
response.css('.link-title::text').extract() #取多個,列表
-xpath選擇器
response.xpath('//a[contains(@class,"link-title")]/text()').extract()
# 取屬性
response.css('.link-title::attr(href)').extract_first() # 取第一個
response.xpath('//a[contains(@class,"link-title")]'/@href).extra_first()
解析出所有的標題和圖片地址
使用
xpath
選擇器
def parse(self, response, **kwargs):
news_list = [
{
'title':div.xpath('.//a[contains(@class,"link-title")]/text()').extract_first(),
'img_url':div.xpath('.//img[contains(@class,"image-scale")]/@src').extract_first(),
# 'title':div.css('.link-title::text').extract_first(),
# 'img_url':div.css('.image-scale::attr(src)').extract_first(),
}
for div in response.xpath('//div[contains(@class,"link-item")]')
]
print(len(news_list))
for news in news_list:
print(news)
使用
css
選擇器
news_list = [
{
# 'title':div.xpath('.//a[contains(@class,"link-title")]/text()').extract_first(),
# 'img_url':div.xpath('.//img[contains(@class,"image-scale")]/@src').extract_first(),
'title':div.css('.link-title::text').extract_first(),
'img_url':div.css('.image-scale::attr(src)').extract_first(),
}
for div in response.xpath('//div[contains(@class,"link-item")]')
]
print(len(news_list))
for news in news_list:
print(news)
scrapy
持久化
方案一:parser函式必須返回列表套字典的形式(瞭解)
儲存為
csv
檔案
def parse(self, response, **kwargs):
news_list = []
for div in response.xpath('//div[contains(@class,"link-item")]'):
news_list.append(
{
'title':div.xpath('.//a[contains(@class,"link-title")]/text()').extract_first(),
'img_url':div.xpath('.//img[contains(@class,"image-scale")]/@src').extract_first()
}
)
return news_list
在終端中執行以下命令:
scrapy crawl drawer -o drawer.csv -s FEED_EXPORT_ENCODING=GBK
# 也可以在settings.py中指定編碼
FEED_EXPORT_ENCODING='GBK'
儲存為
json
格式檔案
scrapy crawl drawer -o drawer.json
# 開啟檔案顯示的是Unicode編碼,如果想顯示為中文,需要設定下:-s FEED_EXPORT_ENCODING=UTF-8
方案二:高階,pipline item
儲存(mysql
,redis
,file
)
在Items.py
中寫一個類
import scrapy
class DrawerItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
url = scrapy.Field()
img_url = scrapy.Field()
在spinder
中匯入,例項化,把資料放進去
import scrapy
from firstscrapy.items import DrawerItem
class DrawerSpider(scrapy.Spider):
name = 'drawer'
allowed_domains = ['dig.chouti.com']
start_urls = ['http://dig.chouti.com/']
def parse(self, response, **kwargs):
for div in response.xpath('//div[contains(@class,"link-item")]'):
item = DrawerItem()
title = div.xpath('.//a[contains(@class,"link-title")]/text()').extract_first()
url = div.xpath('.//a[contains(@class,"link-title")]/@href').extract_first()
img_url = div.xpath('.//img[contains(@class,"image-scale")]/@src').extract_first()
item['title'] = title
item['url'] = url
item['img_url'] = img_url
yield item
在settings
中配置(數字越小,級別越高)
ITEM_PIPELINES = {
'firstscrapy.pipelines.DrawerFilePipeline': 300,
'firstscrapy.pipelines.DrawerMysqlPipeline': 305,
'firstscrapy.pipelines.DrawerRedisPipeline': 310,
}
在pipelines.py
中寫DrawerFilePipeline
class DrawerFilePipeline:
def open_spider(self,spider):
self.file = open('drawer.txt',mode='w',encoding='utf-8')
def process_item(self, item, spider):
title = item['title']
url = item['url']
img_url = item['img_url'] or 'None'
self.file.write(title+'\n'+url+'\n'+img_url+'\n')
return item
def close_spider(self,spider):
self.file.close()
"""
-open_spider(開始的時候)
-close_spider(結束的時候)
-process_item(在這持久化)
"""