從原理到實戰,一份詳實的 Scrapy 爬蟲教程
大家好,我是早起。
之前分享了很多 requests
、selenium
的 Python 爬蟲文章,本文將從原理到實戰帶領大家入門另一個強大的框架 Scrapy
。如果對Scrapy
感興趣的話,不妨跟隨本文動手做一遍!
一、Scrapy 框架簡介
Scrapy
是: 由Python
語言開發的一個快速、高層次的屏幕抓取和 web 抓取框架,用於抓取 web 站點並從頁面中提取結構化的數據,只需要實現少量的代碼,就能夠快速的抓取。
二、運行原理
Scrapy 框架的運行原理看下面一張圖就夠了(事實上原理是比較複雜的,也不是三言兩語能夠說清楚的,因此感興趣的讀者可以進一步閱讀更多的相關文章來了解,本文不做過多講解)
Scrapy 主要包括了以下組件:
-
引擎 (Scrapy Engine)
-
Item 項目
-
調度器 (Scheduler)
-
下載器 (Downloader)
-
爬蟲 (Spiders)
-
項目管道 (Pipeline)
-
下載器中間件 (Downloader Middlewares)
-
爬蟲中間件 (Spider Middlewares)
-
調度中間件 (Scheduler Middewares)
三. 入門
3.1 安裝
第一種:在命令行模式下使用 pip 命令即可安裝:
$ pip install scrapy
第二種:首先下載,然後再安裝:
$ pip download scrapy -d ./
# 通過指定國內鏡像源下載
$pip download -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy -d ./
進入下載目錄後執行下面命令安裝:
$ pip install Scrapy-1.5.0-py2.py3-none-any.whl
3.2 使用
使用大概分爲下面四步 1 創建一個 scrapy 項目
scrapy startproject mySpider
2 生成一個爬蟲
scrapy genspider demo "demo.cn"
3 提取數據
完善spider 使用xpath等
4 保存數據
pipeline中保存數據
3.3 程序運行
在命令中運行爬蟲
scrapy crawl qb # qb爬蟲的名字
在 pycharm 中運行爬蟲
from scrapy import cmdline
cmdline.execute("scrapy crawl qb".split())
四、基本步驟
Scrapy
爬蟲框架的具體使用步驟如下:
“
選擇目標網站
定義要抓取的數據(通過 Scrapy Items 來完成的)
編寫提取數據的 spider
執行 spider,獲取數據
數據存儲
”
五. 目錄文件說明
當我們創建了一個 scrapy 項目後, 繼續創建了一個 spider, 目錄結構是這樣的:
下面來簡單介紹一下各個主要文件的作用:
“
scrapy.cfg :項目的配置文件
mySpider/ :項目的 Python 模塊,將會從這裏引用代碼
mySpider/items.py :項目的目標文件
mySpider/pipelines.py :項目的管道文件
mySpider/settings.py :項目的設置文件
mySpider/spiders/ :存儲爬蟲代碼目錄
”
5.1 scrapy.cfg 文件
項目配置文件。這個是文件的內容:
# Automatically created by: scrapy startproject
#
# For more information about the [deploy] section see:
# https://scrapyd.readthedocs.io/en/latest/deploy.html
[settings]
default = mySpider.settings
[deploy]
#url = http://localhost:6800/
project = mySpider
5.2 mySpider**/**
項目的 Python 模塊,將會從這裏引用代碼
5.3 mySpider/items.py
項目的目標文件
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class MyspiderItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
定義 scrapy items 的模塊, 示例: name = scrapy.Field()
5.4 mySpider/pipelines.py
項目的管道文件
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
from itemadapter import ItemAdapter
class MyspiderPipeline:
def process_item(self, item, spider):
return item
這個文件也就是我們說的管道, 當 Item 在 Spider 中被收集之後,它將會被傳遞到 Item Pipeline(管道),這些 Item Pipeline 組件按定義的順序處理 Item。每個 Item Pipeline 都是實現了簡單方法的 Python 類,比如決定此 Item 是丟棄而存儲。以下是 item pipeline 的一些典型應用:
-
驗證爬取的數據 (檢查 item 包含某些字段,比如說 name 字段)
-
查重 (並丟棄)
-
將爬取結果保存到文件或者數據庫中
5.5 mySpider/settings.py
項目的設置文件
# Scrapy settings for mySpider project
...
BOT_NAME = 'mySpider' # scrapy項目名
SPIDER_MODULES = ['mySpider.spiders']
NEWSPIDER_MODULE = 'mySpider.spiders'
.......
# Obey robots.txt rules
ROBOTSTXT_OBEY = False # 是否遵守協議,一般給位false,但是創建完項目是是True,我們把它改爲False
# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32 # 最大併發量 默認16
......
#DOWNLOAD_DELAY = 3 # 下載延遲 3秒
# Override the default request headers: # 請求報頭,我們打開
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
# 爬蟲中間件
#SPIDER_MIDDLEWARES = {
# 'mySpider.middlewares.MyspiderSpiderMiddleware': 543,
#}
# 下載中間件
#DOWNLOADER_MIDDLEWARES = {
# 'mySpider.middlewares.MyspiderDownloaderMiddleware': 543,
#}
......
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
# 'mySpider.pipelines.MyspiderPipeline': 300, # 管道
#}
.......
省略號省略代碼, 一般重要點, 給了註釋
6.mySpider/spiders/ :存儲爬蟲代碼目錄
import scrapy
class DbSpider(scrapy.Spider):
name = 'db'
allowed_domains = ['douban.com'] # 可以修改
start_urls = ['http://douban.com/'] # 開始的url也可以修改
def parse(self, response):
# pass
六. Scrapy shell
Scrapy 終端是一個交互終端,我們可以在未啓動 spider 的情況下嘗試及調試代碼,也可以用來測試 XPath 或 CSS 表達式,查看他們的工作方式,方便我們爬取的網頁中提取的數據, 但是一般使用的不多。感興趣的查看官方文檔:
官方文檔
http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/shell.html
Scrapy Shell 根據下載的頁面會自動創建一些方便使用的對象,例如 Response 對象,以及 Selector 對象 (對HTML及XML內容)
。
-
當 shell 載入後,將得到一個包含 response 數據的本地 response 變量,輸入
response.body
將輸出 response 的包體,輸出response.headers
可以看到 response 的包頭。 -
輸入
response.selector
時, 將獲取到一個 response 初始化的類 Selector 的對象,此時可以通過使用response.selector.xpath()
或response.selector.css()
來對 response 進行查詢。 -
Scrapy 也提供了一些快捷方式, 例如
response.xpath()
或response.css()
同樣可以生效(如之前的案例)。
Selectors 選擇器
“
Scrapy Selectors 內置 XPath 和 CSS Selector 表達式機制
”
Selector 有四個基本的方法,最常用的還是 xpath:
-
xpath(): 傳入 xpath 表達式,返回該表達式所對應的所有節點的 selector list 列表
-
extract(): 序列化該節點爲字符串並返回 list
-
css(): 傳入 CSS 表達式,返回該表達式所對應的所有節點的 selector list 列表,語法同 BeautifulSoup4
-
re(): 根據傳入的正則表達式對數據進行提取,返回字符串 list 列表
七、案例實戰
本節,我將使用 Scrapy 爬取站酷數據作爲示例
7.1 案例說明
既然已經初步瞭解了 scrapy 的工作流程以及原理, 我們來做一個入門的小案例, 爬取站酷首頁推薦的 item 信息。如下圖所示,一個小方框就是一個 item 信息。我們要提取每一個 item 的六個組成部分:
-
imgLink(封面圖片鏈接);
-
title(標題);
-
types(類型);
-
vistor(人氣);
-
comment(評論數);
-
likes(推薦人數)
然後只是一個頁面的 item,我們還要通過翻頁實現批量數據採集。
7.2 文件配置
目錄結構
在上一篇中我們說明了新建 scrapy 項目(zcool)和 spider 項目(zc),這裏不再贅述,然後得到我們的目錄結構如下圖所示:
start.py 文件
然後爲了方便運行,在 zcool 目錄下新建 start 文件。並進行初始化設置。
from scrapy import cmdline
cmdline.execute('scrapy crawl zc'.split())
settings.py 文件
在這個文件裏我們需要做幾樣設置👇
避免在程序運行的時候打印 log 日誌信息
LOG_LEVEL = 'WARNING'
ROBOTSTXT_OBEY = False
添加請求頭:
打開管道:
item.py 文件
import scrapy
class ZcoolItem(scrapy.Item):
# define the fields for your item here like:
imgLink = scrapy.Field() # 封面圖片鏈接
title = scrapy.Field() # 標題
types = scrapy.Field() # 類型
vistor = scrapy.Field() # 人氣
comment = scrapy.Field() # 評論數
likes = scrapy.Field() # 推薦人數
7.3 頁面數據提取
首先我們在站酷頁面使用 xpath-helper 測試一下:
然後 zc.py 文件裏面初步測試一下:
def parse(self, response):
divList = response.xpath('//div[@class="work-list-box"]/div')
print(len(divList))
運行結果如下圖所示:
沒有問題,然後我們對各種信息分別解析提取,
def parse(self, response):
divList = response.xpath('//div[@class="work-list-box"]/div')
for div in divList:
imgLink = div.xpath("./div[1]/a/img/@src").extract()[0] # 1.封面圖片鏈接
... 2.title(標題);3 types(類型);4vistor(人氣);5comment(評論數) ....
likes = div.xpath("./div[2]/p[3]/span[3]/@title").extract_first() # 6likes(推薦人數)
item = ZcoolItem(imgLink=imgLink,title=title,types=types,vistor=vistor,comment=comment,likes=likes)
yield item
解釋: xpath 提取數據方法:
注意:
“
get() 、getall() 方法是新的方法,extract() 、extract_first() 方法是舊的方法。extract() 、extract_first() 方法取不到就返回 None。get() 、getall() 方法取不到就 raise 一個錯誤。
”
item 實例創建(yield 上面一行代碼)
這裏我們之前在目錄文件配置的 item 文件中已經進行了設置,對於數據存儲,我們在爬蟲文件中開頭要導入這個類:
from zcool.items import ZcoolItem
然後使用 yield 返回數據。
爲什麼使用 yield 而不是 return
不能使用 return 這個無容置疑,因爲要翻頁,使用 return 直接退出函數;而對於 yield: 在調用 for 的時候,函數內部不會立即執行,只是返回了一個生成器對象。在迭代的時候函數會開始執行,當在 yield 的時候,會返回當前值 (i)。之後的這個函數會在循環中進行,直到沒有下一個值。
7.4 翻頁實現批量數據採集
通過上面的代碼已經可以初步實現數據採集,只不過只有第一頁的,如下圖所示:
但是我們的目標是 100 個頁面的批量數據採集,所以代碼還需要修改。針對翻頁這裏介紹兩種方式:
方式一:我們首先在頁面中定位到下一頁的按鈕,如下圖所示:
然後編寫如下代碼,在 for 循環完畢後。
next_href = response.xpath("//a[@class='laypage_next']/@href").extract_first()
if next_href:
next_url = response.urljoin(next_href)
print('*' * 60)
print(next_url)
print('*' * 60)
request = scrapy.Request(next_url)
yield request
scrapy.Request(): 把下一頁的 url 傳遞給 Request 函數, 進行翻頁循環數據採集。
https://www.cnblogs.com/heymonkey/p/11818495.html # scrapy.Request()參考鏈接
注意方式一隻有下一頁按鈕它的 href 對應屬性值和下一頁的 url 一致纔行。
方式二:定義一個全局變量 count = 0, 每爬取一頁數據,令其加一,構建新的 url, 再使用 scrapy.Request() 發起請求。
如下圖所示:
count = 1
class ZcSpider(scrapy.Spider):
name = 'zc'
allowed_domains = ['zcool.com.cn']
start_urls = ['https://www.zcool.com.cn/home?p=1#tab_anchor'] # 第一頁的url
def parse(self, response):
global count
count += 1
for div in divList:
# ...xxx...
yield item
next_url = 'https://www.kuaikanmanhua.com/tag/0?state=1&sort=1&page={}'.format(count)
yield scrapy.Request(next_url)
這兩種方式在實際案例中擇機採用。
7.5 數據存儲
數據存儲是在 pipline.py 中進行的, 代碼如下:
from itemadapter import ItemAdapter
import csv
class ZcoolPipeline:
def __init__(self):
self.f = open('Zcool.csv','w',encoding='utf-8',newline='') # line1
self.file_name = ['imgLink', 'title','types','vistor','comment','likes'] # line2
self.writer = csv.DictWriter(self.f, fieldnames=self.file_name) # line3
self.writer.writeheader() # line4
def process_item(self, item, spider):
self.writer.writerow(dict(item)) # line5
print(item)
return item # line6
def close_spider(self,spider):
self.f.close()
解釋:
-
line1: 打開文件,指定方式爲寫,利用第 3 個參數把 csv 寫數據時產生的空行消除
-
line2: 設置文件第一行的字段名,注意要跟 spider 傳過來的字典 key 名稱相同
-
line3: 指定文件的寫入方式爲 csv 字典寫入,參數 1 爲指定具體文件,參數 2 爲指定字段名
-
line4: 寫入第一行字段名,因爲只要寫入一次,所以文件放在__init__裏面
-
line5: 寫入 spider 傳過來的具體數值, 注意在 spider 文件中 yield 的 item, 是一個由類創建的實例對象,我們寫入數據時,寫入的是 字典,所以這裏還要轉化一下。
-
line6: 寫入完返回
7.6 程序運行
因爲之前創建了 start.py 文件, 並且對它就行了初始化設置,現在運行爬蟲程序不需要在控制檯中輸入命令:
scrapy crawl zc(爬蟲項目名)
直運行 start.py 文件:得到如下結果:
對應於頁面:
打開 csv 文件如下圖所示:(由於 csv 文件在 word 中亂碼了,此處我是用 Notepad++ 打開)
沒有問題,數據採集完畢。
7.7. 總結
入門案例,需要細心,主要是基礎知識的鞏固,以便於爲進階學習做好準備。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/0dF8lQftelkDjd2_SKKAlQ