框架:具有很強的通用性,且封裝了一些通用實現方法的項目模板 (非同步框架): 高性能的網路請求 高性能的數據解析 高性能的持久化存儲 高性能的全站數據爬取 高性能的深度爬取 高性能的分散式 Scrapy環境安裝 IOS和Linux windows 安裝完成後,輸入 測試一下,出現如下圖顯示,即安裝成功 ...
- 框架:具有很強的通用性,且封裝了一些通用實現方法的項目模板
scrapy
(非同步框架):- 高性能的網路請求
- 高性能的數據解析
- 高性能的持久化存儲
- 高性能的全站數據爬取
- 高性能的深度爬取
- 高性能的分散式
Scrapy環境安裝
IOS和Linux
pip install scrapy
windows
a. pip3 install wheel
b. 下載twisted http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
# Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl; Python是3.5版本的就選擇cp35下載
c. 進入下載目錄,執行 pip3 install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl
# 安裝失敗可能是這個文件的版本導致的,即使Python版本都是對的,可以重新下載一個32位的試試
# 還安裝失敗的話就下載其python版本的,總有一個能成功
d. pip3 install pywin32
e. pip3 install scrapy
安裝完成後,輸入``scrapy`測試一下,出現如下圖顯示,即安裝成功。
Scrapy的基本使用
創建工程
-
scrapy startprojct proNmame
cd proNmame
進入到工程目錄下執行爬蟲文件
proName # 工程名字
spiders # 爬蟲包(文件夾)
__init__.py
__init__.py
items.py
middlewares.py
pipelines.py
settings.py # 創建好的工程的配置文件
scrapy.cfg # scrapy的配置文件,不用修改
創建爬蟲文件
- 創建爬蟲文件是py源文件
scrapy genspider spiderName www.xxx.com
網址後期可以修改- 在
spiders
包下創建一個py文件
- 在
# -*- coding: utf-8 -*-
import scrapy
class FirstSpider(scrapy.Spider): # scrapy.Spider所有爬蟲類的父類
# name表示的爬蟲文件的名稱,當前爬蟲文件的唯一標識
name = 'first'
# 允許的功能變數名稱,通常會註釋掉
# allowed_domains = ['www.xx.com']
# 起始的url列表,最開始要爬的網址列表
# 作用:可以將內部的列表元素進行get請求的發送
start_urls = ['http://www.sougou.com/','www.baidu.com']
# 調用parse方法解析數據,方法調用的次數由start_urls列表元素個數決定的
def parse(self, response): # response表示一個響應對象,
pass
基本配置
-
UA偽裝
-
robots協議的不遵從
在
settings.py
中將ROBOTSTXT_OBEY = True
修改為False -
指定日誌等級
在
settings.py
中添加LOG_LEVEL = 'ERROR'
執行工程
-
scrapy crawl spiderName
-
執行工程是不展示日誌文件
scrapy crawl spiderName --nolog
這種方式下程式報錯,不會展示;設置好日誌等級後直接執行工程即可。
數據解析
-
response.xpath('xpath表達式')
-
與
etree
的不同之處:取文本/屬性:返回的是一個
Selector
對象,文本數據是存儲在該對象中Selector對象[0].extract()
返回字元串Selector對象.extract_first()
返回字元串Selector對象.extract()
返回列表
常用操作
- 如果列表只有一個元素用
Selector對象.extract_first()
,返回字元串 - 如果列表有多個元素
Selector對象.extract()
,返回列表,列表裡裝的是字元串
spiderName.py
文件
# -*- coding: utf-8 -*-
import scrapy
class DuanziSpider(scrapy.Spider):
name = 'duanzi'
# allowed_domains = ['www.xx.com']
start_urls = ['https://duanziwang.com/']
def parse(self, response):
article_list = response.xpath('/html/body/section/div/div/main/article') # 基於xpath表達式解析
for article in article_list:
title = article.xpath('./div[1]/h1/a/text()')[0] # 返回一個Selector對象
# <Selector xpath='./div[1]/h1/a/text()' data='關於健康養生、延年益壽的生活諺語_段子網收錄最新段子'>
title = article.xpath('./div[1]/h1/a/text()')[0].extract() # 返回字元串
# 關於健康養生、延年益壽的生活諺語_段子網收錄最新段子
title = article.xpath('./div[1]/h1/a/text()').extract_first() # 返回字元串
# 關於健康養生、延年益壽的生活諺語_段子網收錄最新段子
title = article.xpath('./div[1]/h1/a/text()').extract() # 返回列表
# ['關於健康養生、延年益壽的生活諺語_段子網收錄最新段子']
print(title)
break
持久化存儲
基於終端指令的持久化存儲
-
只可以將parse方法的返回值存儲到指定尾碼的文本文件中
指定尾碼:
'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle'
,通常用csv指令
scrapy crawl spiderName -o filePath
案例:將文本數據持久化存儲
# -*- coding: utf-8 -*-
import scrapy
class DuanziSpider(scrapy.Spider):
name = 'duanzi'
# allowed_domains = ['www.xx.com']
start_urls = ['https://duanziwang.com/']
# 基於終端指令的持久化存儲
def parse(self, response):
article_list = response.xpath('/html/body/section/div/div/main/article') # 基於xpath表達式解析
all_data = []
for article in article_list:
title = article.xpath('./div[1]/h1/a/text()').extract_first()
content = article.xpath('./div[2]/p//text()').extract()
content = ''.join(content)
dic = {
"title": title,
"content": content
}
all_data.append(dic)
return all_data
# 終端指令
# scrapy crawl spiderName -o duanzi.csv
基於管道的持久化存儲
scrapy
建議使用管道持久化存儲
實現流程
-
數據解析(
spiderName .py
) -
實例化item類型對象(
items.py
)在
items.py
的item類中定義相關的屬性fieldNmae = scrapy.Field()
-
將解析的數據存儲封裝到item類型的對象中(
spiderName .py
)item['fileName'] = value
給item對象的fieldNmae屬性賦值 -
將item對象提交給(
spiderName .py
)yield item
將item提交給優先順序最高的管道 -
在管道中接收item,可以將item中存儲的數據進行任意形式的持久化存儲(
pipelines.py
)process_item()
:負責接收item對象且對其進行持久化存儲 -
在配置文件
settings.py
中開啟管道機制找到如下代碼,取消註釋
ITEM_PIPELINES = { # 300表示的是優先順序,數值越小,優先順序越高 'duanziPro.pipelines.DuanziproPipeline': 300, }
案例:將文本數據持久化存儲
按上述在settings.py
找到管道代碼,取消註釋。
spiderName .py
# -*- coding: utf-8 -*-
import scrapy
from duanziPro.items import DuanziproItem
class DuanziSpider(scrapy.Spider):
name = 'duanzi'
# allowed_domains = ['www.xx.com']
start_urls = ['https://duanziwang.com/']
# 基於管道的持久化存儲
def parse(self, response):
article_list = response.xpath('/html/body/section/div/div/main/article') # 基於xpath表達式解析
for article in article_list:
title = article.xpath('./div[1]/h1/a/text()').extract_first()
content = article.xpath('./div[2]/pre/code//text()').extract()
content = ''.join(content)
print(content)
# 實例化item對象
item = DuanziproItem()
# 通過中括弧的形式訪問屬性給其賦值
item['title'] = title
item['content'] = content
# 向管道提交item
yield item
items.py
import scrapy
class DuanziproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 使用固有屬性定義了兩個屬性
# Field是一個萬能數據類型
title = scrapy.Field()
content = scrapy.Field()
pipelines.py
class DuanziproPipeline(object):
# 重寫父類的該方法:該方法只會在爬蟲開始的時候執行一次
fp = None
# 打開文件
def open_spider(self, spider):
print('open spider')
self.fp = open('./duanzi.txt', 'w', encoding='utf-8')
# 關閉文件
def close_spider(self, spider):
print('close spider')
self.fp.close()
# 接收爬蟲文件返回item對象,process_item方法每調用一次可接收一個item對象
# item參數:接收到的某一個item對象
def process_item(self, item, spider):
# 取值
title = item['title']
content = item['content']
self.fp.write(title + ":" + content + "\n")
return item
管道存儲細節處理
-
管道文件中的管道類表示的是什麼?
一個管道類對應的就是一種存儲形式(文本文件,資料庫)
如果想要實現數據備份,則需要使用多個管道類(多種存儲形式:MySQL,Redis)
-
process_item中的
retutn item
:將item傳遞給下一個即將被執行(按照配置文件中ITEM_PIPELINES得權重排序)的管道類
存儲到MySQL
在pipelines.py
中添加如下代碼
import pymysql
class MysqlPipeline(object):
conn = None
cursor = None
def open_spider(self, spider):
self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='123', db='spider',
charset='utf8')
def process_item(self, item, spider):
# 取值
title = item['title']
content = item['content']
self.cursor = self.conn.cursor()
# sql語句
sql = 'insert into duanzi values ("%s","%s")' % (title, content)
try:
self.cursor.execute(sql)
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback()
return item
def close_spider(self, spider):
self.cursor.close()
self.conn.close()
在settings.py
中將MysqlPipeline類註冊到ITEM_PIPELINES中
ITEM_PIPELINES = {
# 300表示的是優先順序,數值越小,優先順序越高
'duanziPro.pipelines.DuanziproPipeline': 300,
'duanziPro.pipelines.MysqlPipeline': 301,
}
存儲到Redis
-
因為redis有的版本不支持存儲字典,下載2.10.6版本
pip install redis==2.10.6
在pipelines.py
中添加如下代碼
from redis import Redis
class RedisPipeline(object):
conn = None
def open_spider(self, spider):
self.conn = Redis(host='127.0.0.1', port=6379, password='yourpassword')
def process_item(self, item, spider):
self.conn.lpush('duanziList', item)
# 報錯:因為redis有的版本不支持存儲字典,pip install redis==2.10.6
在settings.py
中將RedisPipeline類註冊到ITEM_PIPELINES中
ITEM_PIPELINES = {
# 300表示的是優先順序,數值越小,優先順序越高
'duanziPro.pipelines.DuanziproPipeline': 300,
'duanziPro.pipelines.RedisPipeline': 301,
}
手動發送請求
-
可以在start_urls這個列表中添加url,但是比較繁瑣
-
get請求發送
yield scrapy.Request(url,callback)
- url:指定好請求的url
- callback:callback指定的回調函數一定會被執行(數據解析)
-
post請求發送
yield scrapy.FormRequest(url,callback,formdata)
- formdata存放請求參數,字典類型
-
父類中start_requests請求發送的原理
# 簡單模擬父類的方法,主要看yield
def start_requests(self):
for url in self.start_urls:
# 發起get請求
yield scrapy.Request(url=url,callback=self.parse)
# 發起post請求,formdata存放請求參數
yield scrapy.FormRequest(url=url,callback=self.parse,formdata={})
代碼實現
-
主要是在
spiderName .py
中使用遞歸方法,且明確遞歸結束的條件;使用父類yield實現全站爬取
# -*- coding: utf-8 -*-
import scrapy
from duanziPro.items import DuanziproItem
class DuanziSpider(scrapy.Spider):
name = 'duanzi'
# allowed_domains = ['www.xx.com']
start_urls = ['https://duanziwang.com/']
# 手動請求的發送,對其他頁碼的數據進行請求操作
# 定義通用url模板
url = "https://duanziwang.com/page/%d/"
pageNum = 2
def parse(self, response):
article_list = response.xpath('/html/body/section/div/div/main/article') # 基於xpath表達式解析
all_data = []
for article in article_list:
title = article.xpath('./div[1]/h1/a/text()').extract_first()
content = article.xpath('./div[2]/pre/code//text()').extract()
content = ''.join(content)
# 實例化item對象
item = DuanziproItem()
# 通過中括弧的形式訪問屬性給其賦值
item['title'] = title
item['content'] = content
# 向管道提交item
yield item
if self.pageNum < 5:
new_url = format(self.url%self.pageNum)
self.pageNum += 1
# 遞歸實現全站數據爬取,callback指定解析的方法
yield scrapy.Request(url=new_url, callback=self.parse)
- 在
pipelines.py
中實現數據持久化存儲
class DuanziproPipeline(object):
# 重寫父類的該方法:該方法只會在爬蟲開始的時候執行一次
fp = None
def open_spider(self, spider):
print('open spider')
self.fp = open('./duanzi.txt', 'w', encoding='utf-8')
# 關閉fp
def close_spider(self, spider):
print('close spider')
self.fp.close()
# 接收爬蟲文件返回item對象,process_item方法每調用一次可接收一個item對象
# item參數:接收到的某一個item對象
def process_item(self, item, spider):
# 取值
title = item['title']
content = item['content']
self.fp.write(title + ":" + content + "\n")
# 將item轉交給下一個即將被執行的管道類
return item
- 在
settings.py
中開啟管道類
ITEM_PIPELINES = {
# 300表示的是優先順序,數值越小,優先順序越高
'duanziPro.pipelines.DuanziproPipeline': 300,
}
yield在scrapy中的使用
-
向管道中提交item對象
yield item
-
手動請求發送
yield scrapy.Request(url,callback)
五大核心組件
-
引擎(Scrapy Engine)
處理整個系統的數據流,觸發事物(框架核心)。
-
調度器(Scheduer)
用來接收引擎發過來的請求,壓入隊列中,併在引擎再次請求的時候返回。
-
下載器(Downloader)
用於下載網頁內容,並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效模型上的)。
-
爬蟲(Spiders)
爬蟲主要是幹活的,用於從特定的網頁中提取自己需要的信息,即所謂的實體(item)。用戶也可以從中提取出鏈接,讓Scrapy繼續抓取下一個頁面
-
管道(item Pipeline)
負責處理爬蟲從網頁抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並經過幾個特定的次序處理數據。
五大核心組件的工作流程
當執行爬蟲文件時,5大核心組件就在工作了
首先執行爬蟲文件spider,spider的作用是
(1)解析(2)發請求,原始的url存儲在於spider中
1:當spider執行的時候,首先對起始的url發送請求,將起始url封裝成請求對象
2:將請求對象傳遞給引擎
3:引擎將請求對象傳遞給調度器(內部含有隊列和過濾器兩個機制),調度器將請求存儲在隊列(先進先出)中
4:調度器從隊列中調度出url的相應對象再將請求傳遞給引擎
5:引擎將請求對象通過下載中間件發送給下載器
6:下載器拿到請求到互聯網上去下載
7:互聯網將下載好的數據封裝到響應對象給到下載器
8:下載器將響應對象通過下載中間件發送給引擎
9:引擎將封裝了數據的響應對象回傳給spider類parse方法中的response對象
10:spider中的parse方法被調用,response就有了響應值
11:在spider的parse方法中進行解析代碼的編寫;
(1)會解析出另外一批url,(2)會解析出相關的文本數據
12: 將解析拿到的數據封裝到item中
13:item將封裝的文本數據提交給引擎
14:引擎將數據提交給管道進行持久化存儲(一次完整的請求數據)
15:如果parder方法中解析到的另外一批url想繼續提交可以繼續手動進行發請求
16:spider將這批請求對象封裝提交給引擎
17:引擎將這批請求對象發配給調度器
16:這批url通過調度器中過濾器過濾掉重覆的url存儲在調度器的隊列中
17:調度器再將這批請求對象進行請求的調度發送給引擎
引擎作用:
1:處理流數據 2:觸發事物
引擎根據相互的數據流做判斷,根據拿到的流數據進行下一步組件中方法的調用
下載中間件: 位於引擎和下載器之間,可以攔截請求和響應對象;攔截到請求和響應對象後可以
篡改頁面內容和請求和響應頭信息。
爬蟲中間件:位於spider和引擎之間,也可以攔截請求和響應對象,不常用。