Scrapy 爬蟲(chóng)框架的基本使用
一、scrapy 爬蟲(chóng)框架介紹
在編寫(xiě)爬蟲(chóng)的時(shí)候,如果我們使用 requests、aiohttp 等庫(kù),需要從頭至尾把爬蟲(chóng)完整地實(shí)現(xiàn)一遍,比如說(shuō)異常處理、爬取調(diào)度等,如果寫(xiě)的多了,的確會(huì)比較麻煩。利用現(xiàn)有的爬蟲(chóng)框架,可以提高編寫(xiě)爬蟲(chóng)的效率,而說(shuō)到 Python 的爬蟲(chóng)框架,Scrapy 當(dāng)之無(wú)愧是最流行最強(qiáng)大的爬蟲(chóng)框架了。
scrapy 介紹
Scrapy 是一個(gè)基于 Twisted 的異步處理框架,是純 Python 實(shí)現(xiàn)的爬蟲(chóng)框架,其架構(gòu)清晰,模塊之間的耦合程度低,可擴(kuò)展性極強(qiáng),可以靈活完成各種需求。我們只需要定制開(kāi)發(fā)幾個(gè)模塊就可以輕松實(shí)現(xiàn)一個(gè)爬蟲(chóng)。
scrapy 爬蟲(chóng)框架的架構(gòu)如下圖所示:

它有如下幾個(gè)部分:
Scrapy Engine(引擎):用來(lái)處理整個(gè)系統(tǒng)的數(shù)據(jù)流處理、觸發(fā)事務(wù),是整個(gè)框架的核心。 Item(項(xiàng)目):定義了爬取結(jié)果的數(shù)據(jù)結(jié)構(gòu),爬取的數(shù)據(jù)會(huì)被賦值成該對(duì)象。 Scheduler(調(diào)度器):用來(lái)接受引擎發(fā)過(guò)來(lái)的請(qǐng)求并加入隊(duì)列中,并在引擎再次請(qǐng)求的時(shí)候提供給引擎。 Item Pipeline(項(xiàng)目管道):負(fù)責(zé)處理由蜘蛛從網(wǎng)頁(yè)中抽取的項(xiàng)目,它的主要任務(wù)是清洗、驗(yàn)證和存儲(chǔ)數(shù)據(jù)。 Downloader(下載器):用于下載網(wǎng)頁(yè)內(nèi)容,并將網(wǎng)頁(yè)內(nèi)容返回給 Spiders。 Spiders(蜘蛛):其內(nèi)定義了爬取的邏輯和網(wǎng)頁(yè)的解析規(guī)則,它主要負(fù)責(zé)解析響應(yīng)并生成提取結(jié)果和新的請(qǐng)求。 Downloader Middlewares(下載器中間件):位于引擎和下載器之間的鉤子框架,主要是處理引擎與下載器之間的請(qǐng)求及響應(yīng)。 Spider Middlewares(Spiders 中間件):位于引擎和蜘蛛之間的鉤子框架,主要工作是處理蜘蛛輸入的響應(yīng)和輸出的結(jié)果及新的請(qǐng)求。
Scrapy 數(shù)據(jù)流機(jī)制
scrapy 中的數(shù)據(jù)流由引擎控制,其過(guò)程如下:
Engine 首先打開(kāi)一個(gè)網(wǎng)站,找到處理該網(wǎng)站的 Spider 并向該 Spider 請(qǐng)求第一個(gè)要爬取的 URL。 Engine 從 Spider 中獲取到第一個(gè)要爬取的 URL 并通過(guò) Scheduler 以 Request 的形式調(diào)度。 Engine 向 Scheduler 請(qǐng)求下一個(gè)要爬取的 URL。 Scheduler 返回下一個(gè)要爬取的 URL 給 Engine,Engine 將 URL 通過(guò) Downloader Middlewares 轉(zhuǎn)發(fā)給 Downloader 下載。 一旦頁(yè)面下載完畢, Downloader 生成一個(gè)該頁(yè)面的 Response,并將其通過(guò) Downloader Middlewares 發(fā)送給 Engine。 Engine 從下載器中接收到 Response 并通過(guò) Spider Middlewares 發(fā)送給 Spider 處理。 Spider 處理 Response 并返回爬取到的 Item 及新的 Request 給 Engine。 Engine 將 Spider 返回的 Item 給 Item Pipeline,將新的 Request 給 Scheduler。 重復(fù)第二步到最后一步,直到 Scheduler 中沒(méi)有更多的 Request,Engine 關(guān)閉該網(wǎng)站,爬取結(jié)束。
通過(guò)多個(gè)組件的相互協(xié)作、不同組件完成工作的不同、組件很好地支持異步處理,scrapy 最大限度地利用了網(wǎng)絡(luò)帶寬,大大提高了數(shù)據(jù)爬取和處理的效率。
二、scrapy 的安裝和創(chuàng)建項(xiàng)目
pip?install?Scrapy?-i?http://pypi.douban.com/simple?--trusted-host?pypi.douban.com
安裝方法參考官方文檔:https://docs.scrapy.org/en/latest/intro/install.html
安裝完成之后,如果可以正常使用 scrapy 命令,那就是安裝成功了。
Scrapy 是框架,已經(jīng)幫我們預(yù)先配置好了很多可用的組件和編寫(xiě)爬蟲(chóng)時(shí)所用的腳手架,也就是預(yù)生成一個(gè)項(xiàng)目框架,我們可以基于這個(gè)框架來(lái)快速編寫(xiě)爬蟲(chóng)。
Scrapy 框架是通過(guò)命令行來(lái)創(chuàng)建項(xiàng)目的,創(chuàng)建項(xiàng)目的命令如下:
scrapy?startproject?practice
命令執(zhí)行后,在當(dāng)前運(yùn)行目錄下便會(huì)出現(xiàn)一個(gè)文件夾,叫作 practice ,這就是一個(gè)Scrapy 項(xiàng)目框架,我們可以基于這個(gè)項(xiàng)目框架來(lái)編寫(xiě)爬蟲(chóng)。
project/
?__pycache__
??spiders/
???__pycache__
????????__init__.py
????????spider1.py
????????spider2.py
????????...
????__init__.py
????items.py
????middlewares.py
????pipelines.py
????settings.py
scrapy.cfg
各個(gè)文件的功能描述如下:
scrapy.cfg:它是 Scrapy 項(xiàng)目的配置文件,其內(nèi)定義了項(xiàng)目的配置文件路徑、部署相關(guān)信息等內(nèi)容。 items.py:它定義 Item 數(shù)據(jù)結(jié)構(gòu),所有的 Item 的定義都可以放這里。 pipelines.py:它定義 Item Pipeline 的實(shí)現(xiàn),所有的 Item Pipeline 的實(shí)現(xiàn)都可以放這里。 settings.py:它定義項(xiàng)目的全局配置。 middlewares.py:它定義 Spider Middlewares 和 Downloader Middlewares 的實(shí)現(xiàn)。 spiders:其內(nèi)包含一個(gè)個(gè) Spider 的實(shí)現(xiàn),每個(gè) Spider 都有一個(gè)文件。
三、scrapy 的基本使用
實(shí)例 1:爬取 Quotes
創(chuàng)建一個(gè) Scrapy 項(xiàng)目。 創(chuàng)建一個(gè) Spider 來(lái)抓取站點(diǎn)和處理數(shù)據(jù)。 通過(guò)命令行運(yùn)行,將抓取的內(nèi)容導(dǎo)出。
目標(biāo)URL:http://quotes.toscrape.com/
創(chuàng)建項(xiàng)目
創(chuàng)建一個(gè) scrapy 項(xiàng)目,項(xiàng)目文件可以直接用 scrapy 命令生成,命令如下所示:
scrapy?startproject?practice?
創(chuàng)建 Spider
Spider 是自己定義的類(lèi),scrapy 用它從網(wǎng)頁(yè)里抓取內(nèi)容,并解析抓取的結(jié)果。這個(gè)類(lèi)必須繼承 Scrapy 提供的Spider 類(lèi) scrapy.Spider ,還要定義 Spider 的名稱(chēng)和起始請(qǐng)求,以及怎樣處理爬取后的結(jié)果的方法。
使用命令行創(chuàng)建一個(gè) Spider,命令如下:
cd?practice
scrapy?genspider?quotes?quotes.toscrape.com
切換路徑到剛才創(chuàng)建的 practice 文件夾,然后執(zhí)行 genspider 命令。第一個(gè)參數(shù)是 Spider 的名稱(chēng),第二個(gè)參數(shù)是網(wǎng)站域名。執(zhí)行完畢之后,spiders 文件夾中多了一個(gè)quotes.py,它就是剛剛創(chuàng)建的 Spider,內(nèi)容如下:
import?scrapy
class?QuotesSpider(scrapy.Spider):
????name?=?"quotes"
????allowed_domains?=?["quotes.toscrape.com"]
????start_urls?=?['http://quotes.toscrape.com/']
????def?parse(self,?response):
????????pass
可以看到 quotes.py 里有三個(gè)屬性—— name、allowed_domains 和 start_urls,還有一個(gè)方法 parse。
name:它是每個(gè)項(xiàng)目唯一的名字,用來(lái)區(qū)分不同的 Spider。 allowed_domains:它是允許爬取的域名,如果初始或后續(xù)的請(qǐng)求鏈接不是這個(gè)域名下的,則請(qǐng)求鏈接會(huì)被過(guò)濾掉。 start_urls:它包含了 Spider 在啟動(dòng)時(shí)爬取的 url 列表,初始請(qǐng)求是由它來(lái)定義的。 parse:它是 Spider 的一個(gè)方法。默認(rèn)情況下,被調(diào)用時(shí) start_urls 里面的鏈接構(gòu)成的請(qǐng)求完成下載執(zhí)行后,返回的響應(yīng)就會(huì)作為唯一的參數(shù)傳遞給這個(gè)函數(shù)。該方法負(fù)責(zé)解析返回的響應(yīng)、提取數(shù)據(jù)或者進(jìn)一步生成要處理的請(qǐng)求。
創(chuàng)建 Item
Item 是保存爬取數(shù)據(jù)的容器,它的使用方法和字典類(lèi)似。不過(guò),相比字典,Item 多了額外的保護(hù)機(jī)制,可以避免拼寫(xiě)錯(cuò)誤或者定義字段錯(cuò)誤。
創(chuàng)建 Item 需要繼承 scrapy.Item 類(lèi),并且定義類(lèi)型為 scrapy.Field 的字段。觀(guān)察目標(biāo)網(wǎng)站,我們可以獲取到的內(nèi)容有 text、author、tags。
定義 Item ,此時(shí)進(jìn)入 items.py 修改如下:
import?scrapy
class?QuoteItem(scrapy.Item):
????text?=?scrapy.Field()
????author?=?scrapy.Field()
????tags?=?scrapy.Field()
定義了三個(gè)字段,并將類(lèi)的名稱(chēng)修改為 QuoteItem ,接下來(lái)爬取時(shí)會(huì)使用到這個(gè) Item。
解析 Response
parse 方法的參數(shù) response 是 start_urls 里面的鏈接爬取后的結(jié)果。所以在 parse 方法中,我們可以直接對(duì) response 變量包含的內(nèi)容進(jìn)行解析,比如瀏覽請(qǐng)求結(jié)果的網(wǎng)頁(yè)源代碼,或者進(jìn)一步分析源代碼內(nèi)容,或者找出結(jié)果中的鏈接而得到下一個(gè)請(qǐng)求。

可以看到網(wǎng)頁(yè)中既有想要提取的數(shù)據(jù),又有下一頁(yè)的鏈接,這兩部分內(nèi)容都可以進(jìn)行處理。
首先看看網(wǎng)頁(yè)結(jié)構(gòu),如圖所示。每一頁(yè)都有多個(gè) class 為 quote 的區(qū)塊,每個(gè)區(qū)塊內(nèi)都包含 text、author、tags。那么我們先找出所有的 quote,然后提取每一個(gè) quote 中的內(nèi)容。
提取數(shù)據(jù)的方式可以是 CSS 選擇器 或 XPath 選擇器
使用 Item
上文定義了 Item,接下來(lái)就要使用它了。Item 可以理解為一個(gè)字典,不過(guò)在聲明的時(shí)候需要實(shí)例化。然后依次用剛才解析的結(jié)果賦值 Item 的每一個(gè)字段,最后將 Item 返回即可。
import?scrapy
from?practice.items?import?QuoteItem
class?QuotesSpider(scrapy.Spider):
????name?=?'quotes'
????allowed_domains?=?['quotes.toscrape.com']
????start_urls?=?['http://quotes.toscrape.com/']
????def?parse(self,?response,?**kwargs):
????????quotes?=?response.css('.quote')
????????for?quote?in?quotes:
????????????item?=?QuoteItem()
????????????item['text']?=?quote.css('.text::text').extract_first()
????????????item['author']?=?quote.css('.author::text').extract_first()
????????????item['tags']?=?quote.css('.tags?.tag::text').extract()
????????????yield?item
后續(xù) Request
上面的操作實(shí)現(xiàn)了從初始頁(yè)面抓取內(nèi)容。實(shí)現(xiàn)翻頁(yè)爬取,這就需要從當(dāng)前頁(yè)面中找到信息來(lái)生成下一個(gè)請(qǐng)求,然后在下一個(gè)請(qǐng)求的頁(yè)面里找到信息再構(gòu)造下一個(gè)請(qǐng)求。這樣循環(huán)往復(fù)迭代,從而實(shí)現(xiàn)整站的爬取。

查看網(wǎng)頁(yè)源代碼,可以發(fā)現(xiàn)下一頁(yè)的鏈接是 /page/2/,但實(shí)際上全鏈接為:http://quotes.toscrape.com/page/2/,通過(guò)這個(gè)鏈接就可以構(gòu)造下一個(gè)請(qǐng)求。
構(gòu)造請(qǐng)求時(shí)需要用到 scrapy.Request。這里我們傳遞兩個(gè)參數(shù)——url 和 callback,這兩個(gè)參數(shù)的說(shuō)明如下:
url:它是請(qǐng)求鏈接 callback:它是回調(diào)函數(shù)。當(dāng)指定了該回調(diào)函數(shù)的請(qǐng)求完成之后,獲取到響應(yīng),引擎會(huì)將該響應(yīng)作為參數(shù)傳遞給這個(gè)回調(diào)函數(shù)。回調(diào)函數(shù)進(jìn)行解析或生成下一個(gè)請(qǐng)求,回調(diào)函數(shù)如上文的 parse() 所示。
由于 parse 就是解析 text、author、tags 的方法,而下一頁(yè)的結(jié)構(gòu)和剛才已經(jīng)解析的頁(yè)面結(jié)構(gòu)是一樣的,所以我們可以再次使用 parse 方法來(lái)做頁(yè)面解析。
"""
@Author ?:葉庭云
@Date ???:2020/10/2 11:40
@CSDN ???:https://blog.csdn.net/fyfugoyfa
"""
import?scrapy
from?practice.items?import?QuoteItem
class?QuotesSpider(scrapy.Spider):
????name?=?'quotes'
????allowed_domains?=?['quotes.toscrape.com']
????start_urls?=?['http://quotes.toscrape.com/']
????def?parse(self,?response,?**kwargs):
????????quotes?=?response.css('.quote')
????????for?quote?in?quotes:
????????????item?=?QuoteItem()
????????????item['text']?=?quote.css('.text::text').extract_first()
????????????item['author']?=?quote.css('.author::text').extract_first()
????????????item['tags']?=?quote.css('.tags?.tag::text').extract()
????????????yield?item
????????next_page?=?response.css('.pager?.next?a::attr("href")').extract_first()
????????next_url?=?response.urljoin(next_page)
????????yield?scrapy.Request(url=next_url,?callback=self.parse)
運(yùn)行
接下來(lái),進(jìn)入目錄,運(yùn)行如下命令:
scrapy?crawl?quotes?-o?quotes.csv
命令運(yùn)行后,項(xiàng)目?jī)?nèi)多了一個(gè) quotes.csv 文件,文件包含了剛才抓取的所有內(nèi)容。

輸出格式還支持很多種,例如 json、xml、pickle、marshal 等,還支持 ftp、s3 等遠(yuǎn)程輸出,另外還可以通過(guò)自定義 ItemExporter 來(lái)實(shí)現(xiàn)其他的輸出。
scrapy?crawl?quotes?-o?quotes.json
scrapy?crawl?quotes?-o?quotes.xml
scrapy?crawl?quotes?-o?quotes.pickle
scrapy?crawl?quotes?-o?quotes.marshal
scrapy?crawl?quotes?-o?ftp://user:pass@ftp.example.com/path/to/quotes.csv
其中,ftp 輸出需要正確配置用戶(hù)名、密碼、地址、輸出路徑,否則會(huì)報(bào)錯(cuò)。
通過(guò) scrapy 提供的 Feed Exports,我們可以輕松地輸出抓取結(jié)果到文件,對(duì)于一些小型項(xiàng)目來(lái)說(shuō),這應(yīng)該足夠了。不過(guò)如果想要更復(fù)雜的輸出,如輸出到數(shù)據(jù)庫(kù)等,可以靈活使用 Item Pileline 來(lái)完成。
實(shí)例 2:爬取圖片
目標(biāo)URL:http://sc.chinaz.com/tupian/dangaotupian.html
創(chuàng)建項(xiàng)目
scrapy?startproject?get_img
cd?get_img
scrapy?genspider?img_spider?sc.chinaz.com
構(gòu)造請(qǐng)求
img_spider.py 中定義 start_requests() 方法,比如爬取這個(gè)網(wǎng)站里的蛋糕圖片,爬取頁(yè)數(shù)為 10 ,生成 10 次請(qǐng)求,如下所示:
????def?start_requests(self):
????????for?i?in?range(1,?11):
????????????if?i?==?1:
????????????????url?=?'http://sc.chinaz.com/tupian/dangaotupian.html'
????????????else:
????????????????url?=?f'http://sc.chinaz.com/tupian/dangaotupian_{i}.html'
????????????yield?scrapy.Request(url,?self.parse)
編寫(xiě) items.py
import?scrapy
class?GetImgItem(scrapy.Item):
????img_url?=?scrapy.Field()
????img_name?=?scrapy.Field()
編寫(xiě) img_spider.py
Spider 類(lèi)定義了如何爬取某個(gè)(或某些)網(wǎng)站,包括了爬取的動(dòng)作(例如:是否跟進(jìn)鏈接)以及如何從網(wǎng)頁(yè)的內(nèi)容中提取結(jié)構(gòu)化數(shù)據(jù)(抓取item)
"""
@Author ?:葉庭云
@Date ???:2020/10/2 11:40
@CSDN ???:https://blog.csdn.net/fyfugoyfa
"""
import?scrapy
from?get_img.items?import?GetImgItem
class?ImgSpiderSpider(scrapy.Spider):
????name?=?'img_spider'
????def?start_requests(self):
????????for?i?in?range(1,?11):
????????????if?i?==?1:
????????????????url?=?'http://sc.chinaz.com/tupian/dangaotupian.html'
????????????else:
????????????????url?=?f'http://sc.chinaz.com/tupian/dangaotupian_{i}.html'
????????????yield?scrapy.Request(url,?self.parse)
????def?parse(self,?response,?**kwargs):
????????src_list?=?response.xpath('//div[@id="container"]/div/div/a/img/@src2').extract()
????????alt_list?=?response.xpath('//div[@id="container"]/div/div/a/img/@alt').extract()
????????for?alt,?src?in?zip(alt_list,?src_list):
????????????item?=?GetImgItem()???????#?生成item對(duì)象
????????????#?賦值
????????????item['img_url']?=?src
????????????item['img_name']?=?alt
????????????yield?item
編寫(xiě)管道文件 pipelines.py
Scrapy 提供了專(zhuān)門(mén)處理下載的 Pipeline ,包括文件下載和圖片下載。下載文件和圖片的原理與抓取頁(yè)面的原理一樣,因此下載過(guò)程支持異步和多線(xiàn)程,十分高效。
from?scrapy.pipelines.images?import?ImagesPipeline??#?scrapy圖片下載器
from?scrapy?import?Request
from?scrapy.exceptions?import?DropItem
class?GetImgPipeline(ImagesPipeline):
????#?請(qǐng)求下載圖片
????def?get_media_requests(self,?item,?info):
????????yield?Request(item['img_url'],?meta={'name':?item['img_name']})
????def?item_completed(self,?results,?item,?info):
????????#?分析下載結(jié)果并剔除下載失敗的圖片
????????image_paths?=?[x['path']?for?ok,?x?in?results?if?ok]
????????if?not?image_paths:
????????????raise?DropItem("Item?contains?no?images")
????????return?item
????#?重寫(xiě)file_path方法,將圖片以原來(lái)的名稱(chēng)和格式進(jìn)行保存
????def?file_path(self,?request,?response=None,?info=None):
????????name?=?request.meta['name']??#?接收上面meta傳遞過(guò)來(lái)的圖片名稱(chēng)
????????file_name?=?name?+?'.jpg'????#?添加圖片后綴名
????????return?file_name
在這里實(shí)現(xiàn)了 GetImagPipeline,繼承 Scrapy 內(nèi)置的 ImagesPipeline,重寫(xiě)了下面幾個(gè)方法:
get_media_requests()。它的第一個(gè)參數(shù) item 是爬取生成的 Item 對(duì)象。我們將它的 url 字段取出來(lái),然后直接生成 Request 對(duì)象。此 Request 加入調(diào)度隊(duì)列,等待被調(diào)度,執(zhí)行下載。 item_completed(),它是當(dāng)單個(gè) Item 完成下載時(shí)的處理方法。因?yàn)榭赡苡袀€(gè)別圖片未成功下載,所以需要分析下載結(jié)果并剔除下載失敗的圖片。該方法的第一個(gè)參數(shù) results 就是該 Item 對(duì)應(yīng)的下載結(jié)果,它是一個(gè)列表形式,列表每一個(gè)元素是一個(gè)元組,其中包含了下載成功或失敗的信息。這里我們遍歷下載結(jié)果找出所有成功的下載列表。如果列表為空,那么說(shuō)明該 Item 對(duì)應(yīng)的圖片下載失敗了,隨即拋出異常DropItem,該 Item 忽略。否則返回該 Item,說(shuō)明此 Item 有效。 file_path(),它的第一個(gè)參數(shù) request 就是當(dāng)前下載對(duì)應(yīng)的 Request 對(duì)象。這個(gè)方法用來(lái)返回保存的文件名,接收上面meta傳遞過(guò)來(lái)的圖片名稱(chēng),將圖片以原來(lái)的名稱(chēng)和定義格式進(jìn)行保存。
配置文件 settings.py
#?setting.py
BOT_NAME?=?'get_img'
SPIDER_MODULES?=?['get_img.spiders']
NEWSPIDER_MODULE?=?'get_img.spiders'
#?Crawl?responsibly?by?identifying?yourself?(and?your?website)?on?the?user-agent
USER_AGENT?=?'Mozilla/5.0?(Windows?NT?6.1;?Win64;?x64)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/58.0.3029.110?Safari/537.36'
#?Obey?robots.txt?rules
ROBOTSTXT_OBEY?=?False
#?Configure?maximum?concurrent?requests?performed?by?Scrapy?(default:?16)
CONCURRENT_REQUESTS?=?32
#?Configure?a?delay?for?requests?for?the?same?website?(default:?0)
#?See?https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
#?See?also?autothrottle?settings?and?docs
DOWNLOAD_DELAY?=?0.25
#?Configure?item?pipelines
#?See?https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES?=?{
???'get_img.pipelines.GetImgPipeline':?300,
}
IMAGES_STORE?=?'./images'???#?設(shè)置保存圖片的路徑?會(huì)自動(dòng)創(chuàng)建
運(yùn)行程序:
#?切換路徑到img_spider的目錄
scrapy?crawl?img_spider
scrapy 框架爬蟲(chóng)一邊爬取一邊下載,下載速度非常快。
查看本地 images 文件夾,發(fā)現(xiàn)圖片都已經(jīng)成功下載,如圖所示:

到現(xiàn)在為止我們就大體知道了 Scrapy 的基本架構(gòu)并實(shí)操創(chuàng)建了一個(gè) Scrapy 項(xiàng)目,編寫(xiě)代碼進(jìn)行了實(shí)例抓取,熟悉了 scrapy 爬蟲(chóng)框架的基本使用。之后還需要更加詳細(xì)地了解和學(xué)習(xí) scrapy 的用法,感受它的強(qiáng)大。

我們的知識(shí)星球【Python大數(shù)據(jù)分析】
限時(shí)優(yōu)惠中(還有4天)!掃碼領(lǐng)券
年費(fèi)立減20,僅需59元~
快來(lái)一起玩轉(zhuǎn)數(shù)據(jù)分析吧???
Python大數(shù)據(jù)分析
data creates?value
掃碼關(guān)注我們
