99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線電話:13121318867

登錄
首頁精彩閱讀手把手教你使用scrapy框架來爬取北京新發(fā)地價(jià)格行情(理論篇)
手把手教你使用scrapy框架來爬取北京新發(fā)地價(jià)格行情(理論篇)
2021-09-29
收藏

來源:Python爬蟲與數(shù)據(jù)挖掘

作者:霖hero

大家好!我是霖hero。上個(gè)月的時(shí)候,我寫了一篇關(guān)于IP代理的文章,手把手教你使用XPath爬取免費(fèi)代理IP,今天在這里分享我的第二篇文章,希望大家可以喜歡。

前言

有一天,我在逛街,突然被一聲靚仔打斷了我的腳步,回頭一看,原來是水果攤阿姨叫我買水果,說我那么靚仔,便宜一點(diǎn)買給我,自戀的我無法拒絕阿姨的一聲聲靚仔,于是買了很多水果回家,家人問我水果多少錢,結(jié)果如何,沒錯(cuò),水果買貴了!今天我們使用scrapy框架來爬取北京新發(fā)地價(jià)格行情,了解商品價(jià)格,家人再也不怕我買貴東西啦。

Scrapy簡(jiǎn)介

在爬取之前,我們先來學(xué)習(xí)一下什么Scrapy框架。

Scrapy是一個(gè)基于Twisted的異步處理框架,是純Python實(shí)現(xiàn)的爬蟲框架,是提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架,其架構(gòu)清晰,模塊之間的耦合程度低,可擴(kuò)展性極強(qiáng),我們只需要少量的代碼就能夠快速抓取數(shù)據(jù)。

Scrapy框架介紹

首先我們看看經(jīng)典的Scrapy框架架構(gòu)圖,如下圖所示:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

學(xué)Scrapy框架的絕大部分都看過這個(gè)圖,圖中分了很多部分,接下來,我們通過下面的表來簡(jiǎn)單地了解各個(gè)部分的作用。

名稱

作用

是否要手寫代碼

Engine

引擎,負(fù)責(zé)數(shù)據(jù)和信號(hào)的在不同模塊間的傳遞。

Scheduler

調(diào)度器,存放引擎發(fā)過來的requests請(qǐng)求,在引擎再次請(qǐng)求的時(shí)候?qū)⒄?qǐng)求提供給引擎。

Downloader

下載器,下載網(wǎng)頁響應(yīng)的內(nèi)容并將內(nèi)容返回給引擎。

Spiders

爬蟲,處理引擎?zhèn)鬟^來的網(wǎng)頁內(nèi)容并提取數(shù)據(jù)、url,并返回給引擎。

Item Pipeline

管道,處理引擎?zhèn)鬟^來的數(shù)據(jù),主要任務(wù)是清洗、驗(yàn)證和存儲(chǔ)數(shù)據(jù)。

Downloader Middlewares

下載器中間件,位于引擎和下載器之間的橋梁框架,主要是處理引擎與下載器之間的請(qǐng)求及響應(yīng),可以自定義下載擴(kuò)展,如設(shè)置代理。

一般不用手寫

Spider MiddlewaresSpider

中間件,位于引擎和爬蟲之間的橋梁框架,主要處理向爬蟲輸入的響應(yīng)和輸出的結(jié)果及新的請(qǐng)求。

一般不用手寫

在表中,我們可以發(fā)現(xiàn),每部分都要經(jīng)過引擎,上圖中Scrapy Engine部分也是放在正中心,由此可知,Engine引擎是整個(gè)框架的核心。

注意:這些模塊部分只有Spiders和Item Pipeline需要我們自己手寫代碼,其他的大部分都不需要。

Scrapy項(xiàng)目

大致了解了Scrapy框架的各個(gè)部分后,接下來我們開始創(chuàng)建一個(gè)Scrapy項(xiàng)目,可以使用如下命令:

scrapy startproject <Scrapy項(xiàng)目名>

創(chuàng)建一個(gè)scrapy項(xiàng)目名為test1的項(xiàng)目,如下圖所示:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

這樣我們就創(chuàng)建好Scrapy項(xiàng)目了,如下圖所示:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

其中:

  • spiders:存放spiders的文件夾;
  • items.py:Items的定義,定義爬取的數(shù)據(jù)結(jié)構(gòu);
  • middlewares.py:項(xiàng)目中間件文件,定義爬取時(shí)的中間件;
  • pipelines.py:項(xiàng)目管道文件,定義數(shù)據(jù)管道;
  • settings:項(xiàng)目設(shè)置文件;
  • scrapy.cfg:Scrapy部署配置文件。

Spider爬蟲

創(chuàng)建spider爬蟲

要?jiǎng)?chuàng)建Spider爬蟲,首先我們要進(jìn)入剛才創(chuàng)建的Scrapy目錄中,再在命令行運(yùn)行以下命令:

scrapy genspider <爬蟲名字> <允許爬取的域名>

http://quotes.toscrape.com

網(wǎng)站為例子,該網(wǎng)站是一個(gè)著名作家名言的網(wǎng)站,創(chuàng)建Spider爬蟲如下圖所示:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

創(chuàng)建spider爬蟲后,spiders文件夾中多了一個(gè)firstspider.py,這個(gè)py文件就是我們創(chuàng)建爬蟲,文件內(nèi)容如下所示:

import scrapy class FirstspiderSpider(scrapy.Spider): name = 'firstSpider' allowed_domains = ['quotes.toscrape.com']
    start_urls = ['http://quotes.toscrape.com/'] def parse(self, response): pass

其中:

  • class FirstspiderSpider()是自定義spider類,繼承自scrapy.Spider
  • name是定義此爬蟲名稱的字符串,每個(gè)項(xiàng)目唯一的名字,用來區(qū)分不同的Spider,啟動(dòng)爬蟲時(shí)使用scrapy crawl +該爬蟲名字;
  • allowed_domains是允許爬取的域名,防止爬蟲爬到其他網(wǎng)站;
  • start_urls是最開始爬取的url;
  • parse()方法是負(fù)責(zé)解析返回響應(yīng)、提取數(shù)據(jù)或進(jìn)一步生成要處理的請(qǐng)求,注意:不能修改這個(gè)方法的名字。

parse()提取數(shù)據(jù)并啟動(dòng)爬蟲

大致了解了firstspider.py文件內(nèi)容后,我們接下來嘗試在parse()方法中提取響應(yīng)的數(shù)據(jù),具體代碼如下所示:

xpath_parse = response.xpath('/html/body/div[1]/div[2]/div[1]/div') for xpath in xpath_parse:
    item={}
    item['text'] = xpath.xpath('./span[1]/text()').extract_first().replace('“','').replace('”','')       item['author']=xpath.xpath('./span[2]/small/text()').extract_first() print(item)

這樣我們就成功提取到引擎響應(yīng)的內(nèi)容數(shù)據(jù)了,接著輸入以下命令來運(yùn)行spider爬蟲:

scrapy crawl firstSpider

運(yùn)行結(jié)果如下:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

運(yùn)行后發(fā)現(xiàn)我們結(jié)果里面多了很多l(xiāng)og日志,這時(shí)可以通過在settings.py添加以下代碼,就可以屏蔽這些log日志:

LOG_LEVEL="WARNING"

這樣就可以直接輸入我們想要的內(nèi)容,如下圖所示:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

有人可能問:那User-Agent在哪里設(shè)置?

我們可以在settings.py中設(shè)置User-Agent,代碼如下所示:

手把手教你使用<a href='/map/scrapy/' style='color:#000;font-size:inherit;'>scrapy</a>框架來爬取北京新發(fā)地價(jià)格行情(理論篇)

items.py介紹

為了避免拼寫錯(cuò)誤或者定義字段錯(cuò)誤,我們可以在items.py文件中定義好字段,在上面提取數(shù)據(jù)中,我們獲取了text、author內(nèi)容,所以我們可以在items.py定義text和author字段,具體代碼如下所示:

import scrapy class Test1Item(scrapy.Item): text= scrapy.Field() author = scrapy.Field()

在items.py文件中,我們只需要使用scrapy.Field()來進(jìn)行定義即可,scrapy.Field()是一個(gè)字典,總的來說我們可以把該類理解為一個(gè)字典。

接著在firstspider.py文件中導(dǎo)入我們的items.py,以及修改item={},如下所示:

from test1.items import Test1Item
item=Test1Item()

有人可能會(huì)說為什么要多此一舉定義一個(gè)字典呢?

當(dāng)我們?cè)讷@取到數(shù)據(jù)的時(shí)候,使用不同的item來存放不同的數(shù)據(jù),在把數(shù)據(jù)交給pipeline的時(shí)候,可以通過isinstance(item,Test1Item)來判斷數(shù)據(jù)屬于哪個(gè)item,進(jìn)行不同的數(shù)據(jù)(item)處理。

例如我們獲取到京東、淘寶、拼多多的數(shù)據(jù)時(shí),我們可以items.py文件中定義好對(duì)應(yīng)的字段,具體代碼如下:

import scrapy class jingdongItem(scrapy.Item): text= scrapy.Field() author = scrapy.Field() class taobaoItem(scrapy.Item): text= scrapy.Field() author = scrapy.Field() class pddItem(scrapy.Item): text= scrapy.Field() author = scrapy.Field()

定義好字段后,這是我們通過在pipeline.py文件中編寫代碼,對(duì)不同的item數(shù)據(jù)進(jìn)行區(qū)分,具體代碼如下:

from test1.items import jingdongItem class Test1Pipeline: def process_item(self, item, spider): if isinstance(item,jingdongItem):
            print(item)

首先我們通過導(dǎo)入我們的items.py,通過isinstance()函數(shù)來就可以成功獲取到對(duì)應(yīng)的item數(shù)據(jù)了。

pipelines.py介紹

Item Pipeline為項(xiàng)目管道,當(dāng)Item生成后,它就會(huì)自動(dòng)被送到Item Pipeline進(jìn)行處理,我們常用Item Pipeline來做以下操作:

  • 清理HTML數(shù)據(jù);
  • 驗(yàn)證爬取數(shù)據(jù),檢測(cè)爬取字段;
  • 查看并丟棄重復(fù)內(nèi)容;
  • 將爬取結(jié)果保存到數(shù)據(jù)庫(kù)。

pipelines.py內(nèi)容如下所示:

from itemadapter import ItemAdapter class Test1Pipeline: def process_item(self, item, spider): return item

在process_item()方法中,傳入了兩個(gè)參數(shù),一個(gè)參數(shù)是item,每次Spider生成的Item都會(huì)作為參數(shù)傳遞過來。另一個(gè)參數(shù)是spider,就是Spider的示例。

完成pipeline代碼后,需要在setting.py中設(shè)置開啟,開啟方式很簡(jiǎn)單,只要把setting.py內(nèi)容中的以下代碼的注釋取消即可:

ITEM_PIPELINES = { 'test1.pipelines.Test1Pipeline': 300, }

其中:

  • test1.pipelines.Test1Pipeline是pipeline的位置;
  • 300是pipeline的權(quán)重。

注意:

  • pipeline的權(quán)重越小優(yōu)先級(jí)越高;
  • pipeline中的process_item()方法名不能修改為其他的名稱;
  • pipeline能夠定義多個(gè)。

當(dāng)我們有多個(gè)spider爬蟲時(shí),為了滿足不同的spider爬蟲需求,這時(shí)可以定義不同的pipeline處理不同的item內(nèi)容;

當(dāng)一個(gè)spider的內(nèi)容可能要做不同的操作時(shí),例如存入不同的數(shù)據(jù)庫(kù)中,這時(shí)可以定義不同的pipeline處理不同的item操作。

例如當(dāng)我們有多個(gè)spider爬蟲時(shí),可以通過pipeline.py編寫代碼定義多個(gè)pipeline,具體代碼如下:

class jingdongPipeline1: def process_item(self, item, spider): if spider.name=="jingdong":
            print(item) return item class taobaoPipeline: def process_item(self, item, spider): if spider.name=="taobao":
            print(item) return item

這樣我們就可以處理到對(duì)應(yīng)的spider爬蟲傳遞過來的數(shù)據(jù)了。

定義好pipeline后,我們要在settings.py中設(shè)置pipeline權(quán)重,也就是那個(gè)pipeline先運(yùn)行,具體代碼如下:

ITEM_PIPELINES = { 'test1.pipelines.jingdongPipeline': 300, 'test1.pipelines.taobaoPipeline': 301, }

數(shù)據(jù)傳輸到pipeline中

在上面我們已經(jīng)提取到想要的數(shù)據(jù),接下來將數(shù)據(jù)傳到pipeline中,傳輸很簡(jiǎn)單,我們只需要使用yield,代碼如下:

yield item

沒錯(cuò),只要在spider爬蟲中寫入這一行代碼即可,那么為什么要使用yield呢?,我用return不能行嗎?

行,但yield是讓整個(gè)函數(shù)變成一個(gè)生成器,每次遍歷的時(shí)候挨個(gè)讀到內(nèi)存中,這樣不會(huì)導(dǎo)致內(nèi)存的占用量瞬間變高。

實(shí)現(xiàn)翻頁

我們成功獲取到了一頁數(shù)據(jù)了,那么問題來了,如何實(shí)現(xiàn)翻頁呢,方法有很多種,我們主要介紹兩種。

第一種:使用start_requests()方法

我們通過在spider爬蟲中,也就是我們創(chuàng)建的firstspider.py中添加以下代碼,具體代碼如下:

def start_requests(self): for i in range(1,3):
        url=f'https://quotes.toscrape.com/page/{i}/' yield scrapy.Request(url=url,callback=self.parse)

第二種:在parse()方法中實(shí)現(xiàn)翻頁

我們可以通過parse()方法中實(shí)現(xiàn)翻頁,具體代碼如下:

for i in range(2,3):
    url = f'https://quotes.toscrape.com/page/{i}/' yield scrapy.Request(url=url,callback=self.parse)

大家可以發(fā)現(xiàn),上面兩種翻頁方式都差不多,只是一個(gè)在start_requests()方法實(shí)現(xiàn),一個(gè)在parse()方法實(shí)現(xiàn)。

但都要使用scrapy.Request()方法,該方法能構(gòu)建一個(gè)requests,同時(shí)指定提取數(shù)據(jù)的callback函數(shù)

scrapy.Requeset(url,callback,method='GET',headers,cookies,meta,dont_filter=False)

其中:

  • url:表示爬取的url鏈接;
  • callback:指定傳入的url交給哪個(gè)解析函數(shù)去處理;
  • headers:請(qǐng)求頭;
  • cookies:用于識(shí)別用戶身份、進(jìn)行回話跟蹤而存儲(chǔ)在用戶本地終端上的數(shù)據(jù);
  • meta:實(shí)現(xiàn)在不同的解析函數(shù)中傳遞數(shù)據(jù);
  • dont_filter:讓scrapy的去重不會(huì)過濾當(dāng)前url,scrapy默認(rèn)有url去重的功能。

保存數(shù)據(jù)

我們已經(jīng)獲取到數(shù)據(jù)而且實(shí)現(xiàn)了翻頁,接下來是保存數(shù)據(jù)。

保存在文件中

當(dāng)我們要把數(shù)據(jù)保存成文件的時(shí)候,不需要任何額外的代碼,只要執(zhí)行如下代碼即可:

scrapy crawl spider爬蟲名 -o xxx.json #保存為JSON文件 scrapy crawl spider爬蟲名 -o xxx.jl或jsonlines #每個(gè)Item輸出一行json scrapy crawl spider爬蟲名 -o xxx.csv #保存為csv文件 scrapy crawl spider爬蟲名 -o xxx.xml #保存為xml文件

想要保存為什么格式的文件,只要修改后綴就可以了,在這里我就不一一例舉了。

保存MongoDB中

當(dāng)我們要把數(shù)據(jù)保存在MongoDB數(shù)據(jù)庫(kù)的時(shí)候,就要使用Item Pipeline模塊了,也就是說要在pipeline.py中編寫代碼,具體代碼如下所示:

from pymongo import  MongoClient
client=MongoClient()
collection=client["test1"]["firstspider"]


class Test1Pipeline:  def process_item(self, item, spider):  collection.insert(item)  return item

首先我們導(dǎo)入MongoClient模塊并實(shí)例化MongoClient,創(chuàng)建一個(gè)集合,然后在process_item()方法中使用insert()方法把數(shù)據(jù)插入MongoDB數(shù)據(jù)庫(kù)中。

好了,Scrapy知識(shí)就講到這里,下一篇文章小編將帶大家爬取北京新發(fā)地價(jià)格行情,順便鞏固我們今天學(xué)的知識(shí)。

總結(jié)

大家好,我是霖hero。這篇文章主要給大家分享了Scrapy框架的條條框框,Scrapy是一個(gè)基于Twisted的異步處理框架,是純Python實(shí)現(xiàn)的爬蟲框架,是提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架,其架構(gòu)清晰,模塊之間的耦合程度低,可擴(kuò)展性極強(qiáng)。

數(shù)據(jù)分析咨詢請(qǐng)掃描二維碼

若不方便掃碼,搜微信號(hào):CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線
立即咨詢
客服在線
立即咨詢
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機(jī)數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進(jìn)行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個(gè)參數(shù)驗(yàn)證碼對(duì)象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個(gè)配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺(tái)檢測(cè)極驗(yàn)服務(wù)器是否宕機(jī) new_captcha: data.new_captcha, // 用于宕機(jī)時(shí)表示是新驗(yàn)證碼的宕機(jī) product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說明請(qǐng)參見:http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計(jì)時(shí)完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請(qǐng)輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請(qǐng)輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }