Scrapy框架的使用之Scrapy對接硒

Scrapy抓取頁面的方式和請求庫相似,都是直接模擬HTTP請求,而Scrapy也不能抓取的JavaScript動態渲染的頁面。在前文中抓取的JavaScript渲染的頁面有兩種方式。一種是分析的Ajax請求,找到其對應的接口抓取,Scrapy一樣能夠用此種方式抓取。另外一種是直接用硒或飛濺模擬瀏覽器進行抓取,咱們不須要關心頁面後臺發生的請求,也不須要分析渲染過程,只須要關心頁面最終結果便可,可見便可爬。那麼,若是Scrapy能夠對接硒,那Scrapy就能夠處理任何網站的抓取了。

一,本節目標

本節咱們來看看Scrapy框架如何對接硒,以PhantomJS進行演示。咱們依然抓取淘寶商品信息,抓取邏輯和前文中用硒抓取淘寶商品徹底相同。

二,準備工做

請確保PhantomJS和MongoDB中已經安裝好並能夠正常運行,安裝好Scrapy,硒,PyMongo庫。

三,新建項目

首先新建項目,名爲scrapyseleniumtest,命令以下所示:

scrapy startproject scrapyseleniumtest複製代碼

新建一個蜘蛛,命令以下所示:

scrapy genspider淘寶網www.taobao.com複製代碼

修改
ROBOTSTXT_OBEY
False
,以下所示:

ROBOTSTXT_OBEY = False複製代碼

四,定義

定義首先
Item
對象,名爲
ProductItem
,代碼以下所示:

來自 scrapy import Item,Field 

class  ProductItem (Item):

     collection = 'products'image
     = Field()
    price = Field()
    deal = Field()
    title = Field()
    shop = Field()
    location = Field()複製代碼

這裏咱們定義了6個領域,也就是6個字段,跟以前的案例徹底相同。定義而後一個了
collection
屬性,即此項目保存的MongoDB中的集合名稱。

初步實現蜘蛛的
start_requests()
方法,以下所示:

從 scrapy 進口請求,蜘蛛
從的urllib.parse 進口報價
從 scrapyseleniumtest.items 導入 ProductItem 

類 TaobaoSpider (蜘蛛):
     名稱= '淘'
     allowed_domains = [ 'www.taobao.com' ] 
    BASE_URL = 「https://s.taobao的.com /查詢q =」?

    DEF  start_requests (個體):
        爲關鍵字在 self.settings.get('關鍵字'):
            對頁面在範圍(1,self.settings.get('MAX_PAGE')+ 1):
                url = self.base_url + quote(關鍵字)
                yield請求(url = url,callback = self.parse,meta = { 'page':page},dont_filter = True)複製代碼

首先定義了一個
base_url
,即商品列表的URL,其後拼接一個搜索關鍵字就是該關鍵字在淘寶的搜索結果商品列表頁面

用關鍵字
KEYWORDS
標識,定義爲一個列表最大翻頁頁碼用。
MAX_PAGE
表示它們統必定義在setttings.py裏面,以下所示:

KEYWORDS = [ 'iPad' ] 
MAX_PAGE = 100複製代碼

start_requests()
方法裏,咱們首先遍歷了關鍵字,遍歷了分頁頁碼,構造並生成請求。因爲每次搜索的URL是相同的,因此頁碼分頁用
meta
參數來傳遞,設置同時
dont_filter
不去重。這樣爬蟲啓動的時候,就會生成每一個關鍵字對應的商品列表的每一頁的請求了

五,對接Selenium

接下來咱們須要處理這些請求的抓取。此次咱們對接Selenium進行抓取,採用Downloader Middleware來實現。在Middleware裏面的
process_request()
方法裏對每一個抓取請求進行處理,啓動瀏覽器並進行頁面渲染,再將渲染後的結果構造一個
HtmlResponse
。對象返回代碼實現以下所示:

來自 selenium import webdriver 
from selenium.common.exceptions 導入 TimeoutException異常
從 selenium.webdriver.common.by 進口經過
從 selenium.webdriver.support.ui 進口 WebDriverWait 
從 selenium.webdriver.support 進口 expected_conditions 爲 EC 
從 scrapy.http 進口 HtmlResponse 
從記錄import getLogger 

類 SeleniumMiddleware ():
    def  __init__(個體,超時=無,service_args = []) :
         self.logger = getLogger(__ name__)
        self.timeout =超時
        self.browser = webdriver.PhantomJS(service_args = service_args)
        self.browser.set_window_size( 1400, 700)
        的自我。 browser.set_page_load_timeout(self.timeout)
        self.wait = WebDriverWait(self.browser,self.timeout)

     def  __del __ (self):
         self.browser.close()

     def  process_request (self,request,spider):
        「」「 
        用PhantomJS抓取頁面
        :param request:請求對象
        :param spider:Spider對象
        :return:HtmlResponse 
        「」「
         self.logger.debug('PhantomJS is Starting')
        page = request.meta.get('page',1)
        try:
            self.browser.get(request.url)
            if page> 1:
                input = self.wait.until(
                    EC.presence_of_element_located((By.CSS_SELECTOR,'#mainsrp-pager div.form> input))) submit = self.wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR,'# mainsrp-pager div.form> span.btn.J_Submit'))) input.clear() input.send_keys(page) submit.click() self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR,'#mainsrp-pager li.item.active> span'),str(page))) self.wait .until(EC.presence_of_element_located((By.CSS_SELECTOR,'.m-itemlist .items .item'))) return HtmlResponse(url = request.url,body = self.browser.page_source,request = request,encoding = 'utf -8',status = 200), 除了 TimeoutException: return HtmlResponse(url = request.url,status = 500,request = request) @classmethod def from_crawler (cls,crawler): return cls(timeout = crawler.settings.get(' SELENIUM_TIMEOUT '), service_args = crawler.settings.get('PHANTOMJS_SERVICE_ARGS'))複製代碼

首先咱們在
__init__()
裏對一些對象進行初始化,包括
PhantomJS
WebDriverWait
等對象,同時設置頁面大小和頁面加載超時時間
。在
process_request()
方法中,咱們經過請求的
meta
屬性電子雜誌當前須要爬取的頁碼,調用PhantomJS對象的
get()
方法訪問請求的對應的URL。這就至關於從請求對象裏獲取請求連接,而後再用PhantomJS加載,而再也不使用Scrapy裏的下載。

隨後的處理等待和翻頁的方法在此再也不贅述
,和前文的原理徹底相同。最後,頁面加載完成以後,咱們調用PhantomJS的
page_source
屬性便可電子雜誌當前頁面的源代碼,而後用它來直接構造並一個報道查看
HtmlResponse
對象構造這個對象的時候須要傳入多個參數,如。
url
body
等,這些參數實際上就是它的基礎屬性能夠在官方文檔查看
HtmlResponse
對象的結構:HTTPS://doc.scrapy.org/en /latest/topics/request-response.html。這樣咱們就成功利用PhantomJS來代替Scrapy完成了頁面的加載,最後將響應返回便可。

有人可能會納悶:爲何實現這麼一個Downloader Middleware就能夠了?以前的請求對象怎麼辦?Scrapy再也不處理了嗎?回覆後又又傳遞給了誰?

是的,申請對象到這裏就不會再處理了,也不會再像之前同樣交給下載器下載.Response會直接傳給蜘蛛進行解析。

咱們須要回顧一下Downloader Middleware的
process_request()
方法的處理邏輯,內容以下所示:

process_request()
方法返回響應對象的時候,更低優先級的Downloader Middleware的
process_request()
process_exception()
方法就不會被繼續調用了,轉而開始執行每一個Downloader Middleware的
process_response()
方法,調用完畢以後直接響應對象發送給Spider來處理。

這裏直接返回了一個
HtmlResponse
對象,它是Response的子類,返回以後便順次調用每一個Downloader Middleware的
process_response()
方法。而在
process_response()
中咱們沒有對其作特殊處理,它會被髮送給給Spider,傳給Request的回調函數進行解析。

到如今,咱們應該能瞭解Downloader Middleware實現Selenium對接的原理了。

在settings.py裏,咱們設置調用剛纔定義的
SeleniumMiddleware
,以下所示:

DOWNLOADER_MIDDLEWARES = { 
    'scrapyseleniumtest.middlewares.SeleniumMiddleware':543,
}複製代碼

六,解析頁面

響應對象就會回傳給蜘蛛內的回調函數進行解析因此下一步咱們就實現其回調函數,對網頁來進行解析,代碼以下所示:

def  parse (self,response):
     products = response.xpath(
         '// div [@ id =「mainsrp-itemlist」] // div [@ class =「items」] [1] // div [contains(@class , 「項目」)] ')
    爲產品在產品:
        產品= ProductItem()
        項[ '價格' ] = '' 。加入(product.xpath(' .//div[contains(@class, 「價格」)] ()。)(); // text()').extract())。strip()
        item [ 'title' ] = '' .join(product.xpath('.//div[contains ( @class,「title」)] // text()').extract())。strip()
        item [ 'shop' ] = ''. join(product.xpath('.div[contains(@class,「shop」)] // text()').extract())。strip()
        item [ 'image' ] = ''. join(product.xpath('。 / / div [@ class =「pic」] // img [contains(@class,「img」)] / @ data-src').extract())。strip()
        item [ 'deal' ] = product。 xpath(' .//div[contains( @class,「deal-cnt」)] // text()').extract_first()
        item [ 'location' ] = product.xpath('.// div [contains( @class,「location」)] // text()').extract_first()
        yield item複製代碼

在這裏咱們使用的XPath進行解析,調用
response
變量的
xpath()
方法便可。首先咱們傳遞選取全部商品對應的XPath中,能夠匹配全部商品,隨後對結果進行遍歷,依次選取每一個商品的名稱,價格,圖片等內容,並構造一個報道查看
ProductItem
對象。

七,存儲結果

最後咱們實現一個Item Pipeline,將結果保存到MongoDB,以下所示:

導入 pymongo 

類 MongoPipeline (object):
    def  __init__ (self,mongo_uri,mongo_db):
         self.mongo_uri = mongo_uri 
        self.mongo_db = mongo_db 

    @classmethod 
    def  from_crawler (cls,crawler):
        return cls(mongo_uri = crawler.settings.get(' MONGO_URI'),mongo_db = crawler.settings.get('MONGO_DB'))

    def  open_spider(個體,蜘蛛):
         self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client [self.mongo_db]

     DEF  process_item(self,item,spider):
         self.db [item.collection] .insert(dict(item))
         return item

     def  close_spider (self,spider):
         self.client.close()複製代碼

此實現和前文中存儲到MongoDB中的方法徹底一致,原理再也不贅述記得在settings.py中開啓它的調用,以下所示:

ITEM_PIPELINES = { 
    'scrapyseleniumtest.pipelines.MongoPipeline':300,
}複製代碼

其中,
MONGO_URI
狀語從句:
MONGO_DB
的定義以下所示:

MONGO_URI = 'localhost'MONGO_DB
 = 'taobao'複製代碼

八,運行

整個項目就完成了,執行以下命令啓動抓取便可:

scrapy抓取淘寶複製代碼

運行結果以下圖所示。

查看MongoDB的,結果以下圖所示。

這樣咱們便成功在Scrapy中對接硒並實現了淘寶商品的抓取。

九,本節代碼

本節代碼地址爲:HTTPS://github.com/Python3WebSpider/ScrapySeleniumTest。

十,結語

咱們經過實現了Downloader Middleware的方式實現了Selenium的對接。但這種方法實際上是阻塞式的,也就是說這樣就破壞了了Scrapy異步處理的邏輯,速度會受到影響。爲了避免破壞其異步加載邏輯,咱們能夠使用飛濺實現。下一節咱們再來看看Scrapy對接飛濺的方式。


本資源首發於崔慶才的我的博客靜覓: Python3網絡爬蟲開發實戰教程 | 靜覓html

如想了解更多爬蟲資訊,請關注個人我的微信公衆號:進擊的Codergit

weixin.qq.com/r/5zsjOyvEZ… (二維碼自動識別)github

相關文章
相關標籤/搜索