標籤:

小白進階之Scrapy第一篇

這博文寫得我懶癌犯了,最後的那個章節內容排序,我沒有實驗是否是正確的,不過這只是個教大家用Scrapy的教程,正確與否並不重要··· 如果不正確,記得留言;等我懶癌過了,我再改改······

還有其它的問題也是一樣··· ,把問題留言下; 等我懶癌過了·· 我改回來!嗯!是等我懶癌結束了,再改。

前面幾篇博文,給大家從頭到尾做了一個比較高效的爬蟲,從這篇起來說說Python的爬蟲框架Scrapy;

至於為什麼要說框架呢?因為啊,框架可以幫我們處理一部分事情,比如下載模塊不用我們自己寫了,我們只需專註於提取數據就好了;

最重要的一點啊!框架使用了非同步的模式;可以加快我們的下載速度,而不用自己去實現非同步框架;畢竟實現非同步爬蟲是一件比較麻煩的事情。

不過啊!反爬蟲這個坎還是要我們自己邁過去啊!這是後話,以後再說。我們先來讓Scrapy能跑起來,並提取出我們需要的數據,再解決其它問題。

官方文檔在這兒:點我

環境搭建:

關於這一點,對在Windows環境下使用的小夥伴來說,請務必使用我之前提到的 Anaconda 這個Python的發行版本,不然光環境的各種報錯就能消磨掉你所有的學習興趣!

下載地址在這兒:pan.baidu.com/s/1pLgySa

安裝完成之後,在cmd中執行:conda install Scrapy (如果需要使用特定版本,請在Scrapy後面加上 ==XXXX XXXX代表你需要的版本號)

下面是安裝示意圖:

So Easy@@!環境搭建完成!是不是超簡單?全程無痛啊!

下面開始踏上新的征程!Go Go Go!!

使用Scrapy第一步:創建項目;CMD進入你需要放置項目的目錄 輸入:

1

scrapy startproject XXXXX XXXXX代表你項目的名字

OK項目創建完成。現在可以開始我們的爬取之旅了! 下面是目錄中各個文件的作用

好了,目錄我們認識完了,在開始之前給大家一個小技巧,Scrapy默認是不能在IDE中調試的,我們在根目錄中新建一個py文件叫:entrypoint.py;在裡面寫入以下內容:

from scrapy.cmdline import executeexecute([scrapy, crawl, dingdian])

注意!第二行中代碼中的前兩個參數是不變的,第三個參數請使用自己的spider的名字。稍後我會講到!!

現在整個目錄看起來是這樣:

基礎工作準備完畢!我們來說說基本思路。

上面的準備工作完成之後,我們先不要著急開始工作,畢竟作為一個框架,還是很複雜的;貿然上手 開整,很容易陷入懵逼狀態啊!一團漿糊,理不清思路,後面的事情做起來很很麻煩啦!

我們來看看下面這張圖:

這就是整個Scrapy的架構圖了;

Scrapy Engine: 這是引擎,負責Spiders、ItemPipeline、Downloader、Scheduler中間的通訊,信號、數據傳遞等等!(像不像人的身體?)

Scheduler(調度器): 它負責接受引擎發送過來的requests請求,並按照一定的方式進行整理排列,入隊、並等待Scrapy Engine(引擎)來請求時,交給引擎。

Downloader(下載器):負責下載Scrapy Engine(引擎)發送的所有Requests請求,並將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spiders來處理,

Spiders:它負責處理所有Responses,從中分析提取數據,獲取Item欄位需要的數據,並將需要跟進的URL提交給引擎,再次進入Scheduler(調度器),

Item Pipeline:它負責處理Spiders中獲取到的Item,並進行處理,比如去重,持久化存儲(存資料庫,寫入文件,總之就是保存數據用的)

Downloader Middlewares(下載中間件):你可以當作是一個可以自定義擴展下載功能的組件

Spider Middlewares(Spider中間件):你可以理解為是一個可以自定擴展和操作引擎和Spiders中間『通信『的功能組件(比如進入Spiders的Responses;和從Spiders出去的Requests)

數據在整個Scrapy的流向:

程序運行的時候,

引擎:Hi!Spider, 你要處理哪一個網站?

Spiders:我要處理23wx.com

引擎:你把第一個需要的處理的URL給我吧。

Spiders:給你第一個URL是XXXXXXX.com

引擎:Hi!調度器,我這有request你幫我排序入隊一下。

調度器:好的,正在處理你等一下。

引擎:Hi!調度器,把你處理好的request給我,

調度器:給你,這是我處理好的request

引擎:Hi!下載器,你按照下載中間件的設置幫我下載一下這個request

下載器:好的!給你,這是下載好的東西。(如果失敗:不好意思,這個request下載失敗,然後引擎告訴調度器,這個request下載失敗了,你記錄一下,我們待會兒再下載。)

引擎:Hi!Spiders,這是下載好的東西,並且已經按照Spider中間件處理過了,你處理一下(注意!這兒responses默認是交給def parse這個函數處理的

Spiders:(處理完畢數據之後對於需要跟進的URL),Hi!引擎,這是我需要跟進的URL,將它的responses交給函數 def xxxx(self, responses)處理。還有這是我獲取到的Item。

引擎:Hi !Item Pipeline 我這兒有個item你幫我處理一下!調度器!這是我需要的URL你幫我處理下。然後從第四步開始循環,直到獲取到你需要的信息,

注意!只有當調度器中不存在任何request了,整個程序才會停止,(也就是說,對於下載失敗的URL,Scrapy會重新下載。)

以上就是Scrapy整個流程了。

大家將就著看看。

建立一個項目之後:

第一件事情是在items.py文件中定義一些欄位,這些欄位用來臨時存儲你需要保存的數據。方便後面保存數據到其他地方,比如資料庫 或者 本地文本之類的。

第二件事情在spiders文件夾中編寫自己的爬蟲

第三件事情在pipelines.py中存儲自己的數據

還有一件事情,不是非做不可的,就settings.py文件 並不是一定要編輯的,只有有需要的時候才會編輯。

建議一點:在大家調試的時候建議大家在settings.py中取消下面幾行的注釋:

這幾行注釋的作用是,Scrapy會緩存你有的Requests!當你再次請求時,如果存在緩存文檔則返回緩存文檔,而不是去網站請求,這樣既加快了本地調試速度,也減輕了 網站的壓力。一舉多得

第一步定義欄位:

好了,我們來做 第一步 定義一些欄位;那具體我們要定義那些欄位呢?

這個根據自己需要的提取的內容來定義。

比如:我們爬取小說站點都需要提取些什麼數據啊?

小說名字、作者、小說地址、連載狀態、連載字數、文章類別

就像下面這樣:

這樣我們第一步就完成啦!是不是So Easy?ヾ(′▽『)? ; 下面開始重點了哦!編寫spider(就是我們用來提取數據的爬蟲了)

第二步編寫Spider:

在spiders文件中新建一個dingdian.py文件

並導入我們需用的模塊

PS:Scrapy中Response可以直接使用Xpath來解析數據;不過大家也可以使用自己習慣的包,比如我導入的BS4 、re ;當然也可以使其他比如pyquery之類的。這個並沒有什麼限制

另外或許個別小夥伴會遇到 from dingdian.items import DingdianItem這個導入失敗的情況;可以試試把項目文件移動到根目錄。

Request這個模塊可以用來重寫單獨請求一個URL,用於我們後面跟進URL。

好了開整;首先我們需要什麼?

我們需要從一個地址入手開始爬取,我在頂點小說上沒有發現有全站小說地址,但是我找到每個分類地址全部小說:

玄幻魔幻:23wx.com/class/1_1.html

武俠修真:23wx.com/class/2_1.html

都市言情:23wx.com/class/3_1.html

歷史軍事:23wx.com/class/4_1.html

偵探推理:23wx.com/class/5_1.html

網遊動漫:23wx.com/class/6_1.html

科幻小說:23wx.com/class/7_1.html

恐怖靈異:23wx.com/class/8_1.html

散文詩詞:23wx.com/class/9_1.html

其他:23wx.com/class/10_1.htm

全本:23wx.com/quanben/1

好啦!入口地址我們找到了,現在開始寫第一部分代碼:

當然對於上面的地址,我們是可以直接全使用Start_urls這種列表全部請求,不過並不太美觀,我需要把其中,有規律的部分,單獨其他方式實現,比如字典之類的:

第十行:首先我們創建一個類 Myspider;這個類繼承自scrapy.Spider(當然還有一些其他父類,繼承各個父類後能實現的功能不一樣);

第十二行:我們定義name:dingdian (請注意,這name就是我們在entrypoint.py文件中的第三個參數!)!!!!請務必注意:此Name的!名字!在整個項目中有且只能有一個、名字不可重複!!!!

第十一行:我們定義了一個allowed_domains;這個不是必須的;但是在某寫情況下需要用得到,比如使用爬取規則的時候就需要了;它的作用是只會跟進存在於allowed_domains中的URL。不存在的URL會被忽略。

第十七行到第十九行:我們使用字元串拼接的方式實現了我們上面發現的全部URL。

第二十行和二十一行:我們使用了導入的Request包,來跟進我們的URL(並將返回的response作為參數傳遞給self.parse, 嗯!這個叫回調函數!)

第二十三行:使用parse函數接受上面request獲取到的response。(請務必注意:不要輕易改寫parse函數(意思就是不要把parse函數用作它用);因為這樣request的回調函數被你用了,就沒誰接受request返回的response啦!如果你非要用作它用,則需要自己給request一個回調函數哦!)

import reimport scrapy #導入scrapy包from bs4 import BeautifulSoupfrom scrapy.http import Request ##一個單獨的request的模塊,需要跟進URL的時候,需要用它from dingdian.items import DingdianItem ##這是我定義的需要保存的欄位,(導入dingdian項目中,items文件中的DingdianItem類)class Myspider(scrapy.Spider): name = dingdian allowed_domains = [23wx.com] bash_url = http://www.23wx.com/class/ bashurl = .html def start_requests(self): for i in range(1, 11): url = self.bash_url + str(i) + _1 + self.bashurl yield Request(url, self.parse) yield Request(http://www.23wx.com/quanben/1, self.parse) def parse(self, response): print(response.text)

我們測試一下是否正常工作:在IDE中運行我們之前創建的entrypoint.py文件(如果沒有這個文件是不能在IDE中運行的哦!ヽ(=^?ω?^=)丿)

然後會像這樣:

你會發現在紅色狀態報告之後,所有頁面幾乎是一瞬間出現的;那是因為Scrapy使用了非同步啦!ヽ(°◇° )ノ

另外因為Scrapy遵循了robots規則,如果你想要獲取的頁面在robots中被禁止了,Scrapy是會忽略掉的哦!!ヾ(。 ̄□ ̄)?゜゜゜

請求就這麼輕而易舉的實現了啊!簡直So Easy!

繼續 繼續!

我們需要歷遍所有頁面才能取得所有的小說頁面連接:

每個頁面的這個位置都是最後一個頁面,我們提取出它,歷遍就可以拼接出一個完整的URL了ヾ§  ̄▽)ゞ2333333

Go Go

第二十三行:def parse(self, response)這個函數接受來在二十一行返回的response,並處理。

第二十四行:我們使用BS4從response中獲取到了最大頁碼。

第二十五行至二十七行:我們照例拼接了一個完整的URL(response.url:就是這個response的URL地址)

第二十八行:功能和第二十行一樣,callback= 是指定回調函數,不過不寫callback=也沒有什麼影響! 注意我只是說的callback=這個幾個;不是後面的self.get_name.

看清楚了response是怎麼用的沒?ヾ§  ̄▽)ゞ2333333是不是So Easy?

如果不清楚那個拼接URL的小夥伴可以列印出來,看看哦··· 再去觀察一下網頁,就很明白啦

上面兩個函數就徹底的把整個網站的所有小說的頁面URL的提取出來了,並將每個頁面的response交給了get_name函數處理哦!

現在我們的爬蟲就開始處理具體的小說了哦:

瞅見沒 我們需要的東西,快用F12工具看一下吧,在什麼位置有什麼標籤,可以方便我們提取數據。還不知道怎麼看的小夥伴,去看看妹子圖那個教程,有教哦;實在不行百度一下也行!

過程忽略了,直接上代碼(主要是懶癌來了):

前面三行不說了,

第三十七和三十八行:是我們的小說名字和URL

第三十九行和第四十行;大伙兒可能會發現,多了個一個meta這麼一個字典,這是Scrapy中傳遞額外數據的方法。因我們還有一些其他內容需要在下一個頁面中才能獲取到。

下面我的爬蟲進入了這個頁面:

這個頁面就有很多我們需要的信息了:廢話不說了代碼上來:

第四十行:將我們導入的item文件進行實例化,用來存儲我們的數據。

後面全部:將需要的數據,複製給item[key] (注意這兒的Key就是我們前面在item文件中定義的那些欄位。)

注意!response.meta[key]:這個是提取從上一個函數傳遞下來的值。

return item 就是返回我們的字典了,然後Pipelines就可以開始對這些數據進行處理了。比如 存儲之類的。

好啦,Spiders我們先編寫到這個地方。(是不是有小夥伴發現我還有幾個欄位沒有取值?當然留著你們自己試試了,哈哈哈ヽ(=^?ω?^=)丿)後面再繼續。

我現在教教大家怎麼處理這些數據:對頭就是說說Pipeline了:

對於基本的Pipeline存儲方式,網上有很多教程了,今天我們做一個自定義的MySQL的Pipeline:

首先為了能好區分框架自帶的Pipeline,我們把MySQL的Pipeline單獨放到一個目錄裡面。

我們在項目中新建了一個mysqlpipelines的文件夾,我們所有的MySQL文件都放在這個目錄。

init.py 這個文件不需要我說了吧,不知道做啥的小哥兒自己百度。

pipelines.py 這個是我們寫存放數據的文件

sql.py 看名字就知道,需要的sql語句。

首先是需要的MySQL表,(MySQL都沒有的小哥兒 自己百度裝一個啊,我就不教了)

DROP TABLE IF EXISTS `dd_name`;CREATE TABLE `dd_name` ( `id` int(11) NOT NULL AUTO_INCREMENT, `xs_name` varchar(255) DEFAULT NULL, `xs_author` varchar(255) DEFAULT NULL, `category` varchar(255) DEFAULT NULL, `name_id` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8mb4;

首先我們再settings.py文件中定義好MySQL的配置文件(當然你也可以直接定義在sql.py文件中):

PS :注意MySQL的默認埠是 3306;我自己的MySQL改成了3389。這兒各位酌情自己更改。

在開始寫sql.py之前,我們需要安裝一個Python操作MySQL的包,來自MySQL官方的一個包:點我下載

下載完成後解壓出來,從CMD進入該目錄的絕對路徑,然後 Python setup.py install ;即可完成安裝

下面是我們的sql.py文件:

第一行至第二行分別導入了:我的MySQL操作包,settings配置文件

第四行至第八行 : 從settings配置文件中獲取到了,我們的MySQL配置文件

第十行至第十一行: 初始化了一個MySQL的操作游標

第十三行: 定義了一個Sql的類

第十六行至第二十五行:定義了一個函數,將函數中的四個變數寫入資料庫(這四個變數就是我們後面傳進來的需要存儲的數據。)

關於@classmethod這個是一個修飾符;作用是我們不需要初始化類就可以直接調用類中的函數使用(具體說起來麻煩,知道作用就好啦)

好了第一部分寫完了,我們還需要一個能夠去重的:

這一段代碼會查找name_id這個欄位,如果存在則會返回 1 不存在則會返回0

Nice!sqi.py這一部分我們完成,來開始寫pipeline吧:

第一行至第二行:我們導入了之前編寫的sql.py中的Sql類,和我們建立的item

第六行:建立了一個DingdianPipeline的類(別忘了一定要繼承object這個類啊,這是做啥的不用多了解,說多了你們頭暈,我也懶)

第八行:我們定義了一個process_item函數並有,item和spider這兩個參數(請注意啊!這兩玩意兒 務必!!!要加上!!千萬不能少!!!!務必!!!務必!!!)

第十行:你這樣理解如果在 item中存在DingdianItem;就執行下面的。

第十一行:從item中取出 name_id的值。

第十二行:調用Sql中的select_name函數獲得返回值

第十三行:判斷ret是否等於1 ,是的話證明已經存了

第二十行:調用Sql中的 insert_dd_name函數,存儲上面幾個值。

搞完!下面我們啟用這個Pipeline在settings中作如下設置:

PS: dingdian(項目目錄).mysqlpipelines(自己建立的MySQL目錄).pipelines(自己建立的pipelines文件).DingdianPipeline(其中定義的類) 後面的 1 是優先順序程度(1-1000隨意設置,數值越低,組件的優先順序越高)

好!我們來運行一下試試!!Go Go Go!

Nice!!完美!!我之前運行過了 所以提示已經存在。

下面我們開始還剩下的一些內容獲取:小說章節 和章節內容

首先我們在item中新定義一些需要獲取內容的欄位:

代碼不解釋了哦!(懶癌來了,寫不下去了)

繼續編寫Spider文件:

請注意我圖中畫紅框的的地方,這個地方返回item是不能用return的哦!用了就結束了,程序就不會繼續下去了,得用yield(你知道就行,這玩意兒說起來麻煩。)

第五十八行: num這個變數的作用是 因為Scrapy是非同步的方式運作,你採集到的章節順序都是混亂的,需要給它有序的序列,我們按照這個排序就能得到正確的章節順序啦

請注意在頂部導入定義的第二個item類!

下面我們來寫存儲這部分spider的Pipeline:

數據表:

DROP TABLE IF EXISTS `dd_chaptername`;CREATE TABLE `dd_chaptername` ( `id` int(11) NOT NULL AUTO_INCREMENT, `xs_chaptername` varchar(255) DEFAULT NULL, `xs_content` text, `id_name` int(11) DEFAULT NULL, `num_id` int(11) DEFAULT NULL, `url` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2726 DEFAULT CHARSET=gb18030;SET FOREIGN_KEY_CHECKS=1;

Sql.py:

不解釋了哦!

下面是Pipeline:

有小夥伴注意,這兒比上面一個Pipeline少一個判斷,因為我把判斷移動到Spider中去了,這樣就可以減少一次Request,減輕伺服器壓力。

改變後的Spider長這樣:

別忘了在spider中導入Sql哦!ヾ(。 ̄□ ̄)?゜゜゜

到此收工!!!!

至於小說圖片,因為Scrapy的圖片下載管道,是自動以md5命名,而且感覺不爽··· 後面單獨寫一個非同步下載的腳本···

作者:崔慶才

出處:崔慶才的個人博客

最近很多人私信問我問題,平常知乎評論看到不多,如果沒有及時回復,大家也可以加小編微信:tszhihu,進知乎大數據分析挖掘交流群,可以跟各位老師互相交流。謝謝。


推薦閱讀:

Python數據採集Selenium、PantomJS淺談
【Tips】如何用python讀取csv文件中的數據以及如何繪製離散點
Python3 里 7/3 的值為什麼會是 2.3333333333333335,末位為什麼會是 5?
seaborn可視化之heatmap & time series & regression
自學php或python到什麼程度才能找到工作?

TAG:Python | scrapy |