1. 程式人生 > >【python學習筆記】37:認識Scrapy爬蟲,爬取滬深A股資訊

【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

會讓Scrapy啟動後先訪問網站的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"])

執行結果

在這裡插入圖片描述