【python學習筆記】37:認識Scrapy爬蟲,爬取滬深A股資訊
學習《Python3爬蟲、資料清洗與視覺化實戰》時自己的一些實踐。
認識Scrapy爬蟲
安裝
書上說在pip安裝會有問題,直接在Anaconda裡安裝。
建立Scrapy專案
PyCharm裡沒有直接的建立入口,在命令列建立(從Anaconda安裝後似乎自動就在環境變數裡了,可以直接用Scrapy命令):
然後從PyCharm專案選擇頁面裡Open一下即可。右擊專案的同名子目錄,將其Mark As為Source Root,即將其標記為專案原始碼的根目錄。
Scrapy專案結構
爬取滬深A股資訊
設定settings.py
該檔案裡的ROBOTSTXT_OBEY = True
robots.txt
,然後根據其內容決定爬取範圍,即遵循了Robot協議,這裡將它設定為False
。
ROBOTSTXT_OBEY = False
設定爬取間隔。由於選項RANDOMIZE_DOWNLOAD_DELAY
預設是啟用的,那麼Scrapy就會在0.5倍的DOWNLOAD_DELAY
和1.5倍之間取隨機值,作為相鄰兩次傳送請求之間的間隔。
DOWNLOAD_DELAY = 0.25
設定自定義的Exporter類,用於匯出支援中文的JSON檔案。
from scrapy.exporters import JsonLinesItemExporter
# 自定義Exporter類,繼承父類
class CustomJsonLinesItemExporter(JsonLinesItemExporter):
def __init__(self, file, **kwargs):
# 直接呼叫父類的構造,只是將其ensure_ascii設定為False,這樣就可以支援中文
super(CustomJsonLinesItemExporter, self).__init__(file, ensure_ascii=False, **kwargs)
# 啟用自定義的Exporter
FEED_EXPORTERS = {
# 當匯出到json檔案時使用該類
'json': 'scrapylzh.settings.CustomJsonLinesItemExporter'
}
在items.py
設定Item容器和ItemLoader填充器
Item容器的欄位一定要設定,填充器這裡是自定義的,為了把輸出處理器改成TakeFirst()
,它從接收到的值中返回第一個非null非empty的值(空字串就算empty值)。
import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst
# Itemloader提供的是填充Item容器的機制
# (自定義Itemloader只需要繼承scrapy.loader.Itemloader類,並選擇性地宣告其[預設]輸入/輸出處理器等)
class ScrapylzhItemLoader(ItemLoader):
default_output_processor = TakeFirst() # 宣告預設輸出處理器
# Item提供儲存抓取到資料的容器
# (定義Item只需要繼承scrapy.Item類,並將所有欄位都定義為scrapy.Field型別)
class ScrapylzhItem(scrapy.Item):
code = scrapy.Field() # 股票程式碼
abbr = scrapy.Field() # 股票簡稱
last_trade = scrapy.Field() # 最新價
chg_ratio = scrapy.Field() # 漲跌幅
chg_amt = scrapy.Field() # 漲跌額
chg_ratio_5min = scrapy.Field() # 5分鐘漲幅
volumn = scrapy.Field() # 成交量
turn_over = scrapy.Field() # 成交額
生成和完善spiders/
下的爬蟲檔案
在同名子目錄(也即標了原始檔根的目錄,即spiders/
的上級目錄)下執行:
scrapy genspider stock quote.stockstar.com
這表示:呼叫Scrapy的genspider
命令生成爬蟲檔案,檔名是stock
,該爬蟲的活動域名和初始頁面都設定為quote.stockstar.com
。
要爬取的頁面上資訊在HTML裡,可以觀察到它可以由某個簡易的CSS選擇器在頁面中進行唯一表示,那麼就可以用Scrapy的CSS方式來對Response進行解析。
生成的class裡會有一個名為parse
的generator,在那裡寫爬蟲邏輯。所有的結果放到Item容器後都yield起來,最終由Scrapy呼叫前面定義的Exporter來處理。
# -*- coding: utf-8 -*-
import scrapy
from items import ScrapylzhItem, ScrapylzhItemLoader
'''
證券之星 quote.stockstar.com
證券之星-滬深市場-A股市場(第一頁) http://quote.stockstar.com/stock/ranklist_a_3_1_1.html
'''
# 用命令生成的完成爬蟲功能的類
class StockSpider(scrapy.Spider):
# 爬蟲名稱
name = 'stock'
# 爬蟲活動的域.這裡就是指定的那個域名
allowed_domains = ['quote.stockstar.com']
# 爬蟲的活動起點,是活動域下的一個頁面.這裡就是要爬取的若干頁面的第一頁
start_urls = ['http://quote.stockstar.com/stock/ranklist_a_3_1_1.html']
# 在這裡完成爬蟲邏輯,這是一個generator(在裡面可見yield)
def parse(self, response):
# 從url中獲取頁碼
# 觀察url可以看到第一頁是'_a_3_1_1.html'結尾,而第二頁是'_a_3_1_2.html'結尾
# 先按'_'分割成list,取最後一個也就是'頁碼.html',然後再按'.'分割再取第一個即可
page = int(response.url.split('_')[-1].split('.')[0])
# 用CSS選擇器選取頁面中股票的所有項
# 觀察可以看到它們都在id為datalist的tbody裡,以tr元素的形式存在,兩者間用後代選擇器(空格)
item_nodes = response.css('#datalist tr')
# 對其中的每一項(一支股票)
for item in item_nodes:
# 根據item.py中所定義的欄位內容,進行欄位內容的抓取
# 宣告一個Item填充器,接收Item例項和轉化前的Item原型
item_loader = ScrapylzhItemLoader(item=ScrapylzhItem(), selector=item)
# 向Item中的各個欄位寫資料,用CSS方式解析值
item_loader.add_css('code', 'td:nth-child(1) a::text') # 這表示第一個td標籤下a標籤內的文字
item_loader.add_css('abbr', 'td:nth-child(2) a::text')
item_loader.add_css('last_trade', 'td:nth-child(3) span::text')
item_loader.add_css('chg_ratio', 'td:nth-child(4) span::text')
item_loader.add_css('chg_amt', 'td:nth-child(5) span::text')
item_loader.add_css('chg_ratio_5min', 'td:nth-child(6) span::text')
item_loader.add_css('volumn', 'td:nth-child(7)::text')
item_loader.add_css('turn_over', 'td:nth-child(8)::text')
stock_item = item_loader.load_item()
# 用yield使當執行到這裡時返回一個迭代值
yield stock_item
# 如果當前item_nodes非空,說明這一頁是有東西的,那麼可能存在下一頁
if item_nodes:
# 下一頁頁碼
next_page = page + 1
# 替換url中的頁碼部分
next_url = response.url.replace("{0}.html".format(page), "{0}.html".format(next_page))
# 向該url發出請求,在獲取響應後回撥到這個函式,並將結果繼續投入迭代
yield scrapy.Request(url=next_url, callback=self.parse)
# 所以這個generator將返回一個generator物件,其中每次獲取的都是下一支股票的ScrapylzhItem形式
執行爬蟲
可以直接用命令列方式,這裡按書上的方式在程式中執行命令以執行。
from scrapy.cmdline import execute
# 相當於在本目錄下執行'scrapy crawl stock -o items.json'
# 即讓scrapy呼叫spiders下的stock.py爬蟲,將執行結果儲存到items.json中
execute(["scrapy", "crawl", "stock", "-o", "items.json"])