Python爬蟲——js逆向爬取某證信股票行情

最好的掙錢方式是錢生錢,怎樣錢生錢呢,錢生錢可以通過投資,例如買股票、基金等方式,有人可能說買股票基金發財,我沒這樣的命和運氣。買股票基金靠的不只有命運和運氣,更多靠的是長期的經驗和對股票基金數據的分析,今天我們使用scrapy框架來js逆向爬取某證信數據平臺的國內指數成分股行情數據。

網頁分析

首先進入某證信數據平臺國內指數成分股行情數據并打開開發者模式,經過簡單查找發現國內指數成分股行情的數據存放在如下圖的URL鏈接中:

這樣一看,很明顯,該網絡請求是POST請求,URL鏈接、請求表單沒什么加密,那么是不是獲取該URL鏈接的數據就很簡單了呢,這里我們簡單的編寫代碼來請求該url鏈接的數據,具體代碼如下所示:

import requests
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'
}
data={
    'tdate':'2021-11-2',
    'scode':'399001'
}
response=requests.post(url,headers=headers,data=data)
print(response.json())

按照上圖的內容信息,我們這樣編寫爬蟲是沒有問題的,是可以獲取到數據的,但運行這段代碼就出了如下問題:

{'resultmsg': '未經授權的訪問', 'resultcode': 401}

那么我們在請求頭中添加Cookie、Host、referer等參數,運行結果如下所示:

{'resultmsg': '無授權訪問,請聯系*********', 'resultcode': 401}

又出現了問題,這時,我們要觀察一下requests 請求頭中有哪些可疑的請求參數沒有添加到代碼中的headers中,如下圖所示:

有兩個比較可疑的參數,首先我們添加第一個mcode參數到headers中,運行結果如下圖所示:

我們發現在headers中添加mcode參數就可以獲取到數據,那么問題來了,mcode參數的值沒有規律可言,而且每次刷新網頁,mcode的值都會發現改變,怎么辦好呢,這時我們可以通過js逆向來找出mcode值的生成方式。

js逆向加密

找出加密參數的生成方式大致可以分為四步:

  1. 尋找加密參數的方法位置找出來;

  2. 設置斷點找加密方法;

  3. 把加密方法寫入js文件;

  4. 調試js文件。

尋找加密參數位置

打開開發者模式,點擊右上角三個小點,選擇Search,搜索mcode,如下圖所示:

搜索結果如下圖所示:

我們發現有三個js有mcode參數內容,那該怎么辦呢,這時我們可以精確一點搜索,在mcode后面就英文狀態的:,這時就只剩下第一個js了,雙擊該js文件,如下圖所示:

在該js文件中,我們搜索mcode,返回的結果有75個那么多,該怎么辦呢,這時我們發現在mcode上面一部分與我們要爬取的url有點關聯,那么我們可以在該js文件中搜索url中最后的p_sysapi1015,如下圖所示:

這時我們發現搜索結果只有一個了,我們發現mcode是通過indexcode.getResCode()方法生成的,那么該方法有什么作用 呢,我們還不知道,這時就需要通過設置斷點來找出加密方法函數。

設置斷點

我們在上面的mcode代碼行中設置斷點并刷新網頁,如下圖所示:

點擊上圖中的紅框釋放,如下圖所示:

剛好出現了indexcode和getResCode,很明顯var time是和時間有關的,而返回值 window.JSonToCSV.missjson()要調用time,但我們不知道.missjson()方法的作用是什么,這時我們搜索一下missjson,如下圖所示:

搜索結果有兩個,其中一個是剛才調用的missjson()函數,另外一個是missjson函數的具體代碼??床欢@代碼的作用是什么,也沒關系,我們直接把這missjson()函數全部保存在一個js文件中。

寫js文件

加密參數的方法已經知道了,接下來我們將把加密參數missjson()函數寫入js文件中,這里我js文件名為mcode.js,如下圖所示:

這里需要注意的是:function必須要在missjson前面,這和JavaScript的語法有關。

好了,js文件已經寫好了,接下來我們調試一下js文件。

調試js文件

這里我們編寫程序來調試js文件,主要代碼如下圖所示:

import execjs
from os.path import realpath,dirname
import js2py
def get_js():
    path = dirname(realpath(__file__)) + '/js/' + 'mcode' + '.js'
    with open(path,'r',encoding='utf-8')as f:
        read_js=f.read()
        return_js=execjs.compile(read_js)
        print(return_js)
if __name__ == '__main__':
    get_js()

首先導入execjs、js2py這兩個調試js文件的庫,再自定義方法get_js()來讀取js文件,并調用execjs.compile()方法來執行js程序。運行結果如下圖所示:

發現沒有報錯,但沒有得到我們想要的參數值,這是因為我們還沒有編寫time時間的參數進去。主要代碼如下圖所示:

time1 = js2py.eval_js('Math.floor(new Date().getTime()/1000)')
mcode=return_js.call('missjson','{a}'.format(a=time1))
print(mcode)
return mcode

首先調用js2py.eval_js()方法來處理獲取的時間,在通過.call()方法將return_js加密數據和時間結合在一起,最后返回mcode。

運行結果如下圖所示:

好了,mcode參數成功獲取下來了,接下來將正式編寫代碼來爬取國內指數成分股行情數據。

實戰演練

scrapy框架爬蟲

創建scrapy框架爬蟲很簡單,執行如下代碼即可:

scrapy startproject <Scrapy項目名>
cd <Scrapy項目名>
scrapy genspider <爬蟲名字> <允許爬取的域名>

其中,我們的Scrapy項目名為Shares,爬蟲名字為:shares,允許爬取的域名為:網站URL。

好了創建Scrapy項目后,接下來我們創建一個名為js的文件夾來存放剛才編寫的js文件,并把調試js文件的Read_js.py文件放在Scrapy項目中,項目目錄如下圖所示:

這樣我們的爬蟲準備工作就做好了,接下來正式編寫代碼來獲取數據。

itmes.py文件

在獲取數據前,我們先在items.py文件中,定義爬取數據的字段,具體代碼如下所示:

import scrapy
class SharesItem(scrapy.Item):
    # define the fields for your item here like:
    Transaction_date=scrapy.Field()      #交易日期          
    Opening_price=scrapy.Field()        #開盤價
    Number_of_transactions=scrapy.Field()#成交數量
    Closing_price=scrapy.Field()        #收盤價
    minimum_price=scrapy.Field()        #最低價
    Highest_price=scrapy.Field()        #最高價
    Securities_code=scrapy.Field()      #證券代碼
    Securities_abbreviation=scrapy.Field()  #證券簡稱

這里我們只定義了網頁展示給我們數據的字段,要想獲取更多數據,可以根據下圖自行定義字段:

發送網絡請求

定義好字段后,我們要在spiders爬蟲文件中的shares.py文件中編寫start_requests()方法來發送網絡請求,主要代碼如下所示:

def start_requests(self):
    data1 = {
        'tdate': '2021/10/11',
        'scode': '399001'
    }
    url='網站URL/api/sysapi/p_sysapi1015'
    yield scrapy.FormRequest(url,formdata=data1,callback=self.parse)

通過創建的data1字典來構造Form Data表單數據,由于是POST請求,所以我們要使用scrapy.FormRequest()方法來發送網絡請求,發送網絡請求后,通過回調函數callback來將響應內容返回給parse()方法。

提取數據

在上一步中,我們成功獲取到了響應內容,接下來我們繼續編寫把響應內容解析并提取我們想要的數據,主要代碼如下所示:

def parse(self, response):
    p=response.json()
    if p!=None:
        pda=p.get('records')
        for i in pda:
            item=SharesItem()
            item['Transaction_date']=i.get('交易日期')
            item['Opening_price']=i.get('開盤價')
            item['Number_of_transactions']=i.get('成交數量')
            item['Closing_price']=i.get('收盤價')
            item['minimum_price']=i.get('最低價')
            item['Highest_price']=i.get('最高價')
            item['Securities_code']=i.get('證券代碼')
            item['Securities_abbreviation']=i.get('證券簡稱')
            yield item

我們把響應內容通過json()的格式來獲取下來,再通過.get()方法把我們想要的數據提取出來,最后通過yield生成器將數據返回給引擎。

保存數據

在上一步中,我們成功把數據提取出來并返回給引擎了,接下來在piplines.py文件中保存數據在MySQL數據庫中,主要代碼如下所示:

class mysqlPipeline:
    conn = None
    cursor = None
    def open_spider(self,spider):
        print('爬蟲開始?。?!')
        self.conn=pymysql.Connection(host='localhost',user='root',passwd='123456',port=3306,db='commtent1')
    def process_item(self,item,spider):
        self.cursor=self.conn.cursor()
        sql2 = 'insert into data(Transaction_date,Opening_price,Number_of_transactions,Closing_price,minimum_price,Highest_price,Securities_code,Securities_abbreviation) value(%s,%s,%s,%s,%s,%s,%s,%s)'
        print(list(item.values()))
        self.cursor.execute(sql2,list(item.values()))
        self.conn.commit()
    def close_spider(self,spider):
        print('爬蟲結束?。?!')
        self.cursor.close()

首先我們自定義pysqlPipeline類,然后編寫open_spider()方法來連接mysql數據庫,再通過process_item()方法來將數據存放在數據庫中,然后通過編寫close_spider()方法將數據庫關閉。

請求頭headers

接下來開始編寫請求頭headers,headers請求頭一般是在settings.py文件中編寫,首先在settings.py文件中找到DEFAULT_REQUEST_HEADERS代碼行并將注釋去掉,主要代碼如下圖所示:

from Shares.Read_js import get_js
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'
mcode=get_js()
DEFAULT_REQUEST_HEADERS = {
      'Referer': '網站url',
      'Cookie':'Hm_lvt_489bd07e99fbfc5f12cbb4145adb0a9b=1635913057,1635925804,1635991345,1636252368; JSESSIONID=584FD4CCC7E980CAE09908DC0EF835FF; Hm_lpvt_489bd07e99fbfc5f12cbb4145adb0a9b=1636252373',
      'mcode': mcode
}
LOG_LEVEL="WARNING"
ITEM_PIPELINES = {
   # 'Shares.pipelines.SharesPipeline': 300,
   'Shares.pipelines.mysqlPipeline': 301,
}

首先導入Shares.Read_js中的get_js方法,并通過變量mcode來接收get_js()方法的返回值,最后通過LOG_LEVEL="WARNING"把運行爬蟲程序的日志屏蔽,在setting.py文件中找到我們的ITEM_PIPELINES代碼行并將其注釋去掉,開啟我們的項目管道。

執行爬蟲

好了,所有代碼已經編寫完畢了,接下來將執行如下代碼即可運行爬蟲程序:

scrapy crawl shares

運行結果如下圖所示:

這里我們只獲取到了一天的數據,當我們要獲取多天的數據怎么辦呢?

獲取多天數據

獲取多天數據很簡單,只需要調用pandas.period_range()方法即可,將發送網絡請求中的代碼修改為如下代碼即可:

datatime = pd.period_range('2021/10/11', '2021/10/12', freq='B')
for i in datatime:
    data1 = {
        'tdate': str(i),
        'scode': '399001'
    }
    url='網站URL/api/sysapi/p_sysapi1015'
    yield scrapy.FormRequest(url,formdata=data1,callback=self.parse)

其中freq='B'表示工作日,運行結果如下圖所示:

好了,爬取某證信股票行情就講到這里了,感謝觀看?。。?/p>

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容