MATLAB爬蟲爬取股票數據


近年來,大數據盛行,有關爬蟲的教程層次不窮。那么,爬蟲到底是什么呢?

什么是爬蟲?

百度百科是這樣定義的:

網絡爬蟲(又被稱為網頁蜘蛛,網絡機器人,在FOAF社區中間,更經常的稱為網頁追逐者),是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。另外一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲更多解釋

就我個人理解,所謂的爬蟲,就是代替人工復制粘貼去獲取網絡資源。平常我們需要批量下載圖片、下載表格數據時,在沒有爬蟲的幫助下,只能借助CTRL+C 、CTRL+V 了,非常的繁瑣,還容易出錯。但是,你會發現,這些資源呈現出來,都是經過整理的。圖片的鏈接是有規律的字符串,數據的網頁源碼是有規律的標簽包住的(比如用的是同一個Class,同一種標簽。)。這些都是可以程序化的東西。我們通過編程,將這些有規律的東西,用正則表達式來表達出來,然后交給代碼去提取內容,這樣就是爬蟲爬取數據的具體表現了。

MATLAB爬取股票數據

相信大家聽的比較多的應該是用 Python 來爬取網頁數據了,但其實,Matlab 也是可以的,這里我們來具體實現一下。場景是這樣的:

1565940537135

鏈接:http://vip.stock.finance.sina.com.cn/corp/go.php/vMS_MarketHistory/stockid/000001/type/S.phtml?year=2017&jidu=4

新浪財經提供了歷年各個季度的各股票數據,今天我們的任務就是,將上證綜合指數(000001)1991年到1992年的數據爬取到,然后整理出來,保存到兩個excel中,每個excel包括當年四個季度的數據,數據如上圖所示,包括日期,開盤價、最高價、收盤價、最低價、交易量、交易金額。

爬取流程

本次爬取股票數據的流程是這樣的:

觀察網址規律

首先,觀察當選擇不同的年份與季度時,網頁鏈接是有規律的:

http://vip.stock.finance.sina.com.cn/corp/go.php/vMS_MarketHistory/stockid/000001/type/S.phtml?year=2017&jidu=4
  • stockid/000001 指明了所選股票代碼
  • year=2017 指明了所選的年份
  • jidu=4 指明了所選季度

那么,通過觀察,我們就可以知道,當修改對應的數字,就可以獲取到不同年份和季度下的數據網頁了。在代碼里設置兩層循環就可以搞定了。

提取網頁內容

確定網址后,我們可以利用函數獲取到當前網址的源碼,什么是源碼?在網頁里右鍵,查看源碼你就知道了,長這樣:

1565941468623

在Matlab里,提供了urlread函數來獲取源碼,語法參考如下:

str = urlread(URL)
str = urlread(URL,Name,Value)
[str,status] = urlread(___)
  • str = urlread(URL) 將 HTML 網頁內容從指定的 URL 下載到字符向量 str 中。urlread 不檢索超鏈接目標和圖像。
  • str = urlread(URL,Name,Value) 使用一個或多個 Name,Value 對組參數指定的其他選項。
  • [str,status] = urlread(___) 禁止顯示錯誤消息,並使用先前語法中的任何輸入參數。當操作成功時,status 為 1。否則,status 為 0

也就是說,我們利用urlread函數會得到源碼的文本。就像上圖所示的那樣,全是字符串。

觀察提取內容的規律

我們提取的是股票的日期,開盤價、最高價、收盤價、最低價、交易量、交易金額。並且這些內容全部在源碼里面了。源碼是一堆亂七八糟的html標簽還有Js等等。如何提取出我們想要的東西呢?這需要我們去觀察源碼。

匹配日期

首先定位到表格,通過F12,查看源碼后,點擊左下角的箭頭,將箭頭放到表格附近,就可以定位到元素的源碼位置了。

1565942251893

其中日期的附近的源碼是這樣的:

<div align="center">
					<a target="_blank" href="http://vip.stock.finance.sina.com.cn/quotes_service/view/vMS_tradehistory.php?symbol=sh000001&amp;date=2017-12-28">
			2017-12-28			</a>
						</div>

仔細觀察,在 2017-12-28 的前后都存在大量的空格,通過正則表達式,我們可以將其表述出來:

\s+(\d\d\d\d-\d\d-\d\d)\s*

怎么理解?\s+ 表示可以出現空格、換行、制表符等一次或者多次,(\d\d\d\d-\d\d-\d\d) 表示所有滿足形如 2017-12-28這樣的數字組合,\d代表0~9的阿拉伯數字,括號則表示所有滿足這一組表達式匹配到的字符集合。最后\s*則表示末尾可以出現空格、換行、制表符等零次或者多次。通過正則表達式,可以提取到當前源碼里所有滿足這個規律的日期,從而返回相應的數據,這里使用matlab自帶的regexp函數,具體語法如下:

startIndex = regexp(str,expression)
[startIndex,endIndex] = regexp(str,expression)
out = regexp(str,expression,outkey)
[out1,...,outN] = regexp(str,expression,outkey1,...,outkeyN)
___ = regexp(___,option1,...,optionM)
___ = regexp(___,'forceCellOutput')
  • startIndex = regexp(str,expression) 返回 str 中與該正則表達式指定的字符模式匹配的每個子字符串的起始索引。如果沒有匹配項,則 startIndex 為空數組。
  • [startIndex,endIndex] = regexp(str,expression) 返回所有匹配項的開始和結束索引。
  • out = regexp(str,expression,outkey) 返回 outkey 指定的輸出。例如,如果 outkey 為 'match',則 regexp 返回與該表達式匹配的子字符串而非其開始索引。
  • [out1,...,outN] = regexp(str,expression,outkey1,...,outkeyN) 按指定的順序返回多個輸出關鍵字指定的輸出。例如,如果您指定 'match'、'tokens',則 regexp 返回與整個表達式匹配的子字符串以及與部分表達式匹配的標文。
  • ___ = regexp(___,option1,...,optionM) 使用指定的選項標志修改搜索。例如,指定 'ignorecase' 以執行不區分大小寫的匹配。您可以包括任何輸入並請求之前語法中的任何輸出。
  • ___ = regexp(___,'forceCellOutput') 以標量元胞的形式返回每個輸出參數。元胞包含被描述為上述語法輸出的數值數組或子字符串。您可以包括任何輸入並請求之前語法中的任何輸出。

匹配數據

同理,我們也可以觀察剩下的數據源碼:

<td><div align="center">3295.246</div></td>

觀察可以發現,數據都被<div align="center">xxx</div>所包住,所以正則表達式為:

<div align="center">(\d*\.?\d*)</div>

即被標簽包住,且數據滿足整數或者小數。

數據整理與導出

通過上面的正則表達提取字符串后,進行一些數據的整理,例如,字符串轉數字,行列重排等等,然后將其寫入到excel中。這里的步驟就不細說了。

完整源碼

最后貼出源碼(源碼是在CSDN找到的,原鏈接:https://blog.csdn.net/miscclp/article/details/26839095)

% 本程序用於獲取網站中的表格
 
 
% written by longwen36
% all rights reserved 
 
 
clc,clear;
 
 
warning off;
 
 
for year = 1991:1992 %年份
    for jidu = 1:4
        
        fprintf('%d年%d季度的數據...', year, jidu)
        [sourcefile, status] = urlread(sprintf('http://vip.stock.finance.sina.com.cn/corp/go.php/vMS_MarketHistory/stockid/000001/type/S.phtml?year=%d&jidu=%d', year,jidu));
        
        if ~status
            error('讀取出錯!\n')
        end
        
        expr1 = '\s+(\d\d\d\d-\d\d-\d\d)\s*'; %獲取日期
        [datefile, date_tokens]= regexp(sourcefile, expr1, 'match', 'tokens');
        
        
        date = cell(size(date_tokens));
        
        for idx = 1:length(date_tokens)
            date{idx} = date_tokens{idx}{1};
        end
          
        expr2 = '<div align="center">(\d*\.?\d*)</div>'; %獲取數據
        
        [datafile, data_tokens] = regexp(sourcefile, expr2, 'match', 'tokens');
        
        data = zeros(size(data_tokens));
        
        for idx = 1:length(data_tokens)
            data(idx) = str2double(data_tokens{idx}{1});
        end
        
        data = reshape(data, 6, length(data)/6 )'; %重排
        
        filename = sprintf('%d年',year);
        pathname = [pwd '\data'];
        
        if ~exist(pathname,'dir')
            mkdir(pathname);
        end
        
        fullfilepath = [pwd '\data\' filename];
        % 保存數據到Excel
        sheet = sprintf('第%d季度', jidu);
        xlswrite(fullfilepath, date' , sheet);
        range = sprintf('B1:%s%d',char(double('B')+size(data,2)-1), size(data,1));
        xlswrite(fullfilepath, data, sheet, range);
        fprintf('OK!\n')
        
    end
end
 
 
fprintf('全部完成!\n')

運行結果展示

點擊運行后,命令行窗口會提示當前狀態:

1565943200627

每寫入一個季度的數據,就會提示一次OK,直到全部完成。

同時,在當前運行的文件下,會多出一個data文件夾,里面包括了1991和1992兩個excel,打開后表格里有四個季度的數據:

1565943307831

1565943317010

有關正則表達式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM