這邊是高級篇,基礎篇在另一篇博客 https://blog.csdn.net/weixin_45304503/article/details/105581137
scrapy自帶多線程,所以效率特別高
1. 框架原理
2. 安裝
windows下
法一:
pip3 install -i https://pypi.tuna./simple scrapy
現(xiàn)在直接安裝就行了,但是去年直接安裝會報錯…所以去年用的是法二
法二: 安裝scrapy
以及pypiwin32
(用輪子裝,win系統(tǒng)下要安裝pypiwin32)
linux下
如果是在ubuntu
下,還需要安裝第三方庫
sudo apt-get install python-dev python-pip libxm12-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
3. 創(chuàng)建
創(chuàng)建項目
:'scrapy startproject [爬蟲的名字]’ (記得進入pycharmprojects的頁面來進行創(chuàng)建)
創(chuàng)建爬蟲
:進入到項目所在的路徑,執(zhí)行命令:'scrapy genspider [爬蟲的名字] [爬蟲的域名]’ 放在spider上創(chuàng)建
4. 結構
items.py:用來存放爬蟲爬取下來的數(shù)據(jù)類型 middlewares.py:用來存放各種中間件的文件 pipelines.py:用來將items的模型存儲到本地磁盤中 settings.py:本爬蟲的一些配置信息(比如請求頭,、多久發(fā)送一次,、ip代理池等等) scrapy.cfg:項目的配置文件 spiders包:以后所有的爬蟲,都是存放到這里面
5. 知識
5.1 基本知識:
response
是一個scrapy.http.response.html.HtmlResponse
對象,可以執(zhí)行xpath
和css語法
來提取數(shù)據(jù)。
提取出來的數(shù)據(jù),是一個Selector
或者是一個SelectorList
對象,。如果想要獲取其中的字符串,那么應該執(zhí)行getall
或是get
方法
getall
方法:獲取selector中的所有
文本,返回的是列表
,。
get
方法:獲取selector中的第一個
文本,返回的是str類型
。
如果數(shù)據(jù)解析回來,要傳給pipline處理,。那么可以使用yield
關鍵字來返回,或是可以收集到所有的item,最后統(tǒng)一使用return
返回,。
item:建議在item.py
中定義好模型,以后就不要使用字典了,在最后將數(shù)據(jù)變成字典就行了
pipeline:這個是專門用來保存數(shù)據(jù)的,其中三個方法是會經(jīng)常用到的。
? open_spider(self,spider)
:當爬蟲被打開的時候就會被調(diào)用
? process_item(self,item,spider)
:當爬蟲有item傳過來的時候就會被調(diào)用
? close_spider(self,spider)
:當爬蟲關閉的時候會被調(diào)用 要激活piplilne , 應該在settings.py中設置ITEM_PIPELINES
將其激活.
JsonItemExporter:這個是每次把數(shù)據(jù)添加到內(nèi)存中,最后統(tǒng)一寫入
到磁盤中
好處就是存儲的數(shù)據(jù)是一個滿足json規(guī)則的數(shù)據(jù),。 壞處就是如何數(shù)據(jù)量比較大,那么比較消耗內(nèi)存,。
from scrapy. exporters import JsonItemExporter # 導入json導出器
class QsbkPipeline ( object ) :
def __init__ ( self) :
self. fp= open ( "duanzi.json" , 'wb' ) # 以二進制的方式打開
self. exporter= JsonItemExporter( self. fp, ensure_ascii= False , encoding= 'utf-8' ) # 創(chuàng)建一個導入的對象
self. exporter. start_exporting( ) # 開始導入
def open_spider ( self, spider) : # 爬蟲開始
print ( "爬蟲開始" )
def process_item ( self, item, spider) : # item是爬蟲返回的數(shù)據(jù)
self. exporter. export_item( item) # 用export_item來進行返回
return item
def close_spider ( self, spider) : #爬蟲結束
self. exporter. finish_exporting( )
self. fp. close( )
print ( "爬蟲結束了" )
JsonLinesItemExporter:這個是每次調(diào)用export_item的時候就吧這個item存儲到硬盤當中。
壞處是每一個字典是一行,整個文件不是一個滿足json格式的文件,。 好處就是每次處理數(shù)據(jù)的時候就直接存儲到硬盤當中,這樣不會消耗內(nèi)存,數(shù)據(jù)也比較安全,。
from scrapy. exporters import JsonLinesItemExporter
# 導入json導出器,有l(wèi)ines, 一行一行的來,數(shù)據(jù)量多的話就用這個
class QsbkPipeline ( object ) :
def __init__ ( self) :
self. fp= open ( "duanzi.json" , 'wb' ) # 以二進制的方式打開
self. exporter= JsonLinesItemExporter( self. fp, ensure_ascii= False , encoding= 'utf-8' )
# 創(chuàng)建一個導入的對象
# 這邊不需要開始導入之類的
def open_spider ( self, spider) : # 爬蟲開始
print ( "爬蟲開始" )
def process_item ( self, item, spider) : # item是爬蟲返回的數(shù)據(jù)
self. exporter. export_item( item) # 用export_item來進行返回
return item
def close_spider ( self, spider) : #爬蟲結束
# self.exporter.finish_exporting() 也不需要什么結束導入
self. fp. close( )
print ( "爬蟲結束了" )
5.2 創(chuàng)建CrawlSpider爬蟲:
scrapy genspider -c crawl [ 爬蟲名字] [ 域名]
5.3 LinkExtractors連接提取器:
這個可以不用程序員自己提取想要的url,然后發(fā)送請求。這些工作可以交給LinkExtractors
,他會在所有爬取的網(wǎng)面中找到滿足規(guī)則的url,實現(xiàn)自動的爬取,。
class scrapy . linkextractors. LinkExtractor(
allow= ( ) , #允許的url 所有滿足這個正則表達式的url都會被提取
deny= ( ) , #禁止的url 所有滿足這個正則表達式的url都不會被提取
allow_domains( ) , #允許的域名 只有在這個里面指定的域名的url才會被提取
deny_extensions= None ,
restrict_xpaths= ( ) , # 嚴格的xpath和allow共同過濾鏈接
tahs= ( 'a' , 'area' ) ,
attrs= ( 'herf' ) ,
unique= True ,
process_value= None
)
5.3 Rule規(guī)則類:
class scrapy . spiders. Rule(
link_extractor,
callback= None , #滿足這個規(guī)則的url就應該執(zhí)行哪個回調(diào)函數(shù).因為CrawlSpider使用了parse作為回調(diào)函數(shù),因此不要副高parse作為回調(diào)函數(shù)自己的回調(diào)函數(shù)
cb_kwargs= None ,
follow= None , # 是否需要跟進默認True
process_links= None , # 從link_extractor中獲取到連接后會傳遞給這個函數(shù),用來過濾不需要爬取的連接
process_request= None
)
CrawlSpider:
需要使用LinkExtractor和Rule這兩個東西決定爬蟲給的具體走向
1.allow設置規(guī)則的方法:要能夠限制在我們想要的url上面,不要跟其他url產(chǎn)生相同的正則表達式即可,。
2.什么情況下使用follow:如果在爬取頁面的時候,需要將滿足當前條件的url再進行跟進,那么就設置為True。否則設置為False.
3.什么情況下該指定callback:如果這個url對應的頁面,只是為了獲取更多的url,并不需要里面的數(shù)據(jù),那么可以不指定callback,。如果想要獲取url對應的頁面中的數(shù)據(jù),那么就需要指定一個callback來進行數(shù)據(jù)的處理,儲存等,。
關于Scrapy Shell
再爬蟲中使用xpath,beautifulsoup,正則表達式,css選擇器等等,但是scrapy是一個比較重的框架,每次運行起來都要等待一段時間,因此要去驗證我們寫的提取規(guī)則是否正確,是一個麻煩的事情,因此scrapy提供了一個shell可以方便測試規(guī)則。
在cmd打開終端,進入到scrapy項目所在的目錄,然后進入到scrapy框架所在的虛擬環(huán)境中,輸入命令,scrapy shell 鏈接,。就會進入到scrapy的shell環(huán)境中,你可以跟在爬蟲中的parse方法一樣使用了,。
1.可以方便我們做一些數(shù)據(jù)提取的測試代碼
2.如果想要執(zhí)行scrapy命令,那么毫無疑問,肯定是要想進入scrapy所在的環(huán)境中,我自己電腦的python環(huán)境只有一個,是定義到全局的,所以就可以直接進行命令
3.如果想要讀取某個項目的配置信息,那么應該先進入到這個項目中。再執(zhí)行’scrapy shell’命令
Request和Response對象
1.Request對象
class Request ( object_ref) :
def __init__ ( self, url, callback= None , method= 'GET' , headers= None , body= None ,
cookies= None , meta= None , encoding= 'utf-8' , priority= 0 ,
dont_filter= False , errback= None , flags= None , cb_kwargs= None ) :
self. _encoding = encoding # this one has to be set first
self. method = str ( method) . upper( )
self. _set_url( url)
self. _set_body( body)
assert isinstance ( priority, int ) , "Request priority not an integer: %r" % priority
self. priority = priority
if callback is not None and not callable ( callback) :
raise TypeError( 'callback must be a callable, got %s' % type ( callback) . __name__)
if errback is not None and not callable ( errback) :
raise TypeError( 'errback must be a callable, got %s' % type ( errback) . __name__)
self. callback = callback
self. errback = errback
self. cookies = cookies or { }
self. headers = Headers( headers or { } , encoding= encoding)
self. dont_filter = dont_filter
self. _meta = dict ( meta) if meta else None
self. _cb_kwargs = dict ( cb_kwargs) if cb_kwargs else None
self. flags = [ ] if flags is None else list ( flags)
Request對象在我們寫爬蟲,爬取一頁的數(shù)據(jù)需要重新發(fā)送一個請求的時候調(diào)用,。這個類需要傳遞一些參數(shù),其中比較常用的參數(shù)有:
1.url:這個request對象發(fā)送請求的url,。
2.callback:在下載器下載完相應的數(shù)據(jù)后執(zhí)行的回調(diào)函數(shù)。
3.method:請求的方法,。默認為GET方法,可以設置為其他方法,。
4.headers:請求頭,對于一些固定的設置,放在settings.py中指定就可以了。對于那些非固定的,可以在發(fā)送請求的時候指定,。
5.meta:比較常用,。用于在不同的請求之間傳遞數(shù)據(jù)用的。
6.encoding:編碼,默認的為utf-8,使用默認的就可以了
7.dot_filter:表示不由調(diào)度器過濾,在執(zhí)行多次重復的請求的時候用得比較多,。比如驗證碼,。
8.errback:在發(fā)生錯誤的時候執(zhí)行的函數(shù)
2.Response對象
Response對象一般是有scrapy給你自動構建的。因此開發(fā)者不需要關心如何創(chuàng)建response對象,而且如何使用他,。response對象有很多屬性,可以用來提取數(shù)據(jù)的,。主要有一下屬性
1.meta:從其他請求傳過來的meta屬性,可以用來保持多個請求之間的數(shù)據(jù)連接。
2.encoding:返回當前字符串編碼和解碼的格式
3.text:將返回的數(shù)據(jù)作為unicode字符串返回
4.body:將返回來的數(shù)據(jù)作為bytes字符串返回
5.xpath:xpath選擇器
6.css:css選擇器
發(fā)送POST請求:
如果需要post請求的話,就需要使用request的子類fromrequest來實現(xiàn),如果想要在爬蟲一開始的時候就發(fā)送給post請求的話,那么需要在爬蟲類中重寫start_requests(self)方法,并且不再調(diào)用start_urls里的url.
3.模擬登陸人人網(wǎng)
1.想發(fā)送post請求,那么推薦使用scrapy.FormRequest方法,??梢苑奖愕闹付ū韱螖?shù)據(jù)
2.如果想在爬蟲一開始的時候就發(fā)送post請求,那么應該重寫start_requests方法,發(fā)送請求
4.模擬登陸豆瓣
下載圖片和文件
scrapy為下載item中包含的文件,提供了一個可重用的item pipelines。這寫pipeline有些共同的方法和結構,我們稱之為media pipeline。一般使用files pipeline或是images pipeline
好處:
1.避免重復下載過的文件
2.可以方便指定下載文件的存儲路徑
3.可以將下載的圖片轉換成通用的格式,比如png或是jpg
4.可以方便的生成縮量圖
5.可以方便的檢測圖片的寬和高,確保他們滿足最小限制
6.異步下載,效率非常高
下載文件的Files Pipeline:
使用Files Pipeline下載文件的話,按照一下步驟來完成
1.定義好一個Item,然后再這個item中定義兩個屬性,分別是file_urls以及files,。file_urls是用來存儲需要下載的文件的url鏈接,需要給一個列表,。
2.當文件下載完成后,會把文件下載的相關信息存儲到item的files屬性中。比如下載路徑,下載的url和文件的效驗碼等,。
3.再配置文件settings.py中配置FILES_STORE,這個配置是用來設置文件下載下來的路徑
4.啟動pipeline:再ITEM_PIPELINES中設置scrapy.pipelines.files.FilesPipeline:1
下載圖片的Images Pipeline:
但是用Images Pipeline下載文件的時候,按照一下步驟來完成:
1.定義好一個Item,然后再這個item中定義兩個屬性,分別為image_urls以及images,。images_urls
是用來儲存需要下載的圖片的url鏈接,需要給一個列表。
2.當文件下載完成后,會把文件下載的相關信息存儲到item的images屬性中,。比如下載路徑,、下載的url和圖片的校驗碼等
3.再配置文件settings.py中配置IMAGES_STORE,這個配置是用來設置圖片下載下來的路徑
4.啟動pipeline:在ITEM_PIPELINES中設置scrapy.pipelines.images.ImagesPipeline:1
下載器中間件:
Downloader Middlewares:
下載器中間件是引擎和下載器之間通信的中間將。在這個中間件中,。我們可以設置代理,更換請求頭等來達到反發(fā)爬蟲的目的,。要寫中間器,可以在下載器中實現(xiàn)兩個方法。一個是process_request(self,request,spider),這個方法是在請求發(fā)送之前會執(zhí)行的,還有一個是process_response(self,request,reponse,spider),這個方法是數(shù)據(jù)下載到引擎之前執(zhí)行,。
process_request(self,request,spider):
這個方法是下載器在發(fā)送請求之前會執(zhí)行的,。一般可以在這個里面設置一些隨機代理ip
1.參數(shù):
? 。request:發(fā)送請求request對象
? ,。spider:發(fā)送請求的spider對象
2.返回值:
? ,。返回None:如果返回None,Scrapy將繼續(xù)處理該request,執(zhí)行其他中間件中的相應方法,知道合適的下載器處理函數(shù)被調(diào)用。
? ,。返回Response對象:Scrapy將不會調(diào)用任何其他的process_request方法,將直接返回這個response對象,。已經(jīng)激活的中間件process_reponse()方法則會在每個response返回時被調(diào)用。
? ,。 返回Request對象:不再使用之前的request對象去下載數(shù)據(jù),而是使用現(xiàn)在的request對象返回數(shù)據(jù)
? 。如果這個方法拋出異常,則會調(diào)用process_exception方法,。
process_response(self,request,spider):
1.參數(shù):
? ,。request:request對象
? 。response: response對象
? ,。spider:spider對象
2.返回值:
? ,。返回Response對象:會將這個新的response對象傳給其他中間件,最終傳給爬蟲
? 。 返回Request對象:下載器鏈被切斷,返回的request會重新被下載器調(diào)度下載
? ,。如果這個方法拋出異常,則會調(diào)用request的errback方法,如果沒有指定這個方法,就會拋出一個異常
隨機請求頭:
如果一直都是用一個請求頭,那么可能會被服務器抓到的,。所以就要用到很多的請求頭。
http://www./pages/useragentstring.php
這個網(wǎng)址上面的有許多請求頭,而且都是比較新的!舒服
代理ip
建議就是芝麻代理,。
6. 分布式爬蟲
scrapy 是一個框架,但是本身就不支持分布式的,就是要加上scrapy-redis
Advantage: 1.可以充分利用多臺機器的帶寬 2.可以充分利用多臺機器的ip地址 3.多臺機器做,爬取效率高,。
Problem: 1.分布式爬蟲是好多臺機器再同時運行,如何保證不同的機器爬取亞眠的時候不會出現(xiàn)重復爬取的問題。 2.同樣,分布式爬蟲在不同的機器運行,在把數(shù)據(jù)爬完后如何保存在同一個地方,。
Redis
redis是一種支持分布式的nosql數(shù)據(jù)庫,是非關系型數(shù)據(jù)庫,同時的保存在內(nèi)存當中,同時redis可以定時把內(nèi)存數(shù)據(jù)同步到磁盤,既可以將數(shù)據(jù)持久化.比memcached支持更多的數(shù)據(jù)結構
1. 啟動redis:
sudo service redis-server start
2.連接上redis-server
redus-cli -h [ ip] -p [ 端口]
3.添加
set key value
eg:set username xiaotou ? 將字符串值value關聯(lián)到key,。如果key已經(jīng)持有其他值,set命令就復寫舊值,無視其類型。并且默認的時期時間是永久,即永遠不會過期,。
4.刪除:
del key
eg:del username
5.設置過期時間:
expire key timeout (秒)
6.列表操作:
在列表的左邊添加元素:
lpush key value
?將值value插入到列表key的表頭中,如果key不存在,一個空列表會被創(chuàng)建并執(zhí)行l(wèi)push操作,。當key存在當不是列表類型是,將返回一個錯誤.
在列表右邊添加元素:
lpush key value
?將值value插入到列表key的表尾,。如果key不存在,一個空列表會被創(chuàng)建并執(zhí)行rpush的操作。當key存在當不是列表類型是,返回一個錯誤,。
查看列表中的元素
lrange key start stop
返回列表key中指定區(qū)間內(nèi)的元素,區(qū)間以偏移量start和stop指定 如果要左邊的第一個到最后一個lrange key 0-1, 0是第一個,。-1是最后一個
移除列表中的元素:
?1)移除并返回列表key的頭元素
lpop key
?2)移除并返回列表的尾元素
rpop key
?3)移除并返回列表的中的元素
lrem key count value
?將刪除key這個列表中,count個值為value ?4)指定返回第幾個元素
lindex key index
將返回key這個列表中索引index這個元素
? 5)獲取列表中的元素個數(shù)
llen key
eg:llen langueage 6)刪除指定元素
lrem key count value
eg: lrem languages 0 php
根據(jù)參數(shù)count的值,移除列表中與參數(shù)value相等的元素。count的值可以是一下幾種 count>0:從表頭開始向表尾搜索,移除與value相等的元素,數(shù)量是count count<0:從表尾開始先表頭搜索,移除與value相等的元素,數(shù)量為count的絕對值 count=0:移除表中所有與value相等的值
7.set集合操作
1.添加元素
sadd set value1 value2
eg: sadd team xiaotuo datuo
2.查看元素
smembers set
eg: smember set
3.移除元素
srem set member.. .
eg : srem team xiaotuo datuo
4.查看集合中的元素個數(shù)
scard set
eg:scard teaml
5.獲取多個集合的交集
sinter set1 set2
eg: sinter team1 team2
6.獲取多個集合的并集
sunion set1 set2
eg:sunion team1 team2
7.獲取多個集合的差集
sdiff set1 set2
eg:sdiff team1 team2
8.hash哈希操作
redis中的字典,。 1.添加新值
hset key field value
eg:hset website baidu baidu.com
2.獲取哈希中的field對應的值
hget key field
eg:hget website baidu
3.刪除field中的某個field
hdel key field
eg:hdel website baidu
4.獲取某個哈希中所有的field和value
hgetall key
eg:hgetall website
5.獲取某個哈希中所有的值
hvals key
eg:hvals website
6.判斷哈希中是否存在某個field
hexists key field
eg:hexists website baidu
7.獲取哈希中總共的鍵值對
hlen field
eg:hlen website