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

最好的掙錢方式是錢生錢,怎樣錢生錢呢霹期,錢生錢可以通過投資叶组,例如買股票、基金等方式历造,有人可能說買股票基金發(fā)財(cái)甩十,我沒這樣的命和運(yùn)氣。買股票基金靠的不只有命運(yùn)和運(yùn)氣吭产,更多靠的是長(zhǎng)期的經(jīng)驗(yàn)和對(duì)股票基金數(shù)據(jù)的分析侣监,今天我們使用scrapy框架來js逆向爬取某證信數(shù)據(jù)平臺(tái)的國內(nèi)指數(shù)成分股行情數(shù)據(jù)。

網(wǎng)頁分析

首先進(jìn)入某證信數(shù)據(jù)平臺(tái)國內(nèi)指數(shù)成分股行情數(shù)據(jù)并打開開發(fā)者模式臣淤,經(jīng)過簡(jiǎn)單查找發(fā)現(xiàn)國內(nèi)指數(shù)成分股行情的數(shù)據(jù)存放在如下圖的URL鏈接中:

這樣一看橄霉,很明顯,該網(wǎng)絡(luò)請(qǐng)求是POST請(qǐng)求邑蒋,URL鏈接姓蜂、請(qǐng)求表單沒什么加密,那么是不是獲取該URL鏈接的數(shù)據(jù)就很簡(jiǎn)單了呢医吊,這里我們簡(jiǎn)單的編寫代碼來請(qǐng)求該url鏈接的數(shù)據(jù)钱慢,具體代碼如下所示:

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())

按照上圖的內(nèi)容信息,我們這樣編寫爬蟲是沒有問題的卿堂,是可以獲取到數(shù)據(jù)的束莫,但運(yùn)行這段代碼就出了如下問題:

{'resultmsg': '未經(jīng)授權(quán)的訪問', 'resultcode': 401}

那么我們?cè)谡?qǐng)求頭中添加Cookie懒棉、Host、referer等參數(shù)览绿,運(yùn)行結(jié)果如下所示:

{'resultmsg': '無授權(quán)訪問策严,請(qǐng)聯(lián)系*********', 'resultcode': 401}

又出現(xiàn)了問題,這時(shí)挟裂,我們要觀察一下requests 請(qǐng)求頭中有哪些可疑的請(qǐng)求參數(shù)沒有添加到代碼中的headers中享钞,如下圖所示:

有兩個(gè)比較可疑的參數(shù)揍诽,首先我們添加第一個(gè)mcode參數(shù)到headers中诀蓉,運(yùn)行結(jié)果如下圖所示:

我們發(fā)現(xiàn)在headers中添加mcode參數(shù)就可以獲取到數(shù)據(jù),那么問題來了暑脆,mcode參數(shù)的值沒有規(guī)律可言渠啤,而且每次刷新網(wǎng)頁,mcode的值都會(huì)發(fā)現(xiàn)改變添吗,怎么辦好呢沥曹,這時(shí)我們可以通過js逆向來找出mcode值的生成方式。

js逆向加密

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

  1. 尋找加密參數(shù)的方法位置找出來碟联;

  2. 設(shè)置斷點(diǎn)找加密方法妓美;

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

  4. 調(diào)試js文件鲤孵。

尋找加密參數(shù)位置

打開開發(fā)者模式壶栋,點(diǎn)擊右上角三個(gè)小點(diǎn),選擇Search普监,搜索mcode贵试,如下圖所示:

搜索結(jié)果如下圖所示:

我們發(fā)現(xiàn)有三個(gè)js有mcode參數(shù)內(nèi)容,那該怎么辦呢凯正,這時(shí)我們可以精確一點(diǎn)搜索毙玻,在mcode后面就英文狀態(tài)的:,這時(shí)就只剩下第一個(gè)js了廊散,雙擊該js文件桑滩,如下圖所示:

在該js文件中,我們搜索mcode允睹,返回的結(jié)果有75個(gè)那么多运准,該怎么辦呢,這時(shí)我們發(fā)現(xiàn)在mcode上面一部分與我們要爬取的url有點(diǎn)關(guān)聯(lián)擂找,那么我們可以在該js文件中搜索url中最后的p_sysapi1015戳吝,如下圖所示:

這時(shí)我們發(fā)現(xiàn)搜索結(jié)果只有一個(gè)了,我們發(fā)現(xiàn)mcode是通過indexcode.getResCode()方法生成的贯涎,那么該方法有什么作用 呢听哭,我們還不知道,這時(shí)就需要通過設(shè)置斷點(diǎn)來找出加密方法函數(shù)。

設(shè)置斷點(diǎn)

我們?cè)谏厦娴膍code代碼行中設(shè)置斷點(diǎn)并刷新網(wǎng)頁陆盘,如下圖所示:

點(diǎn)擊上圖中的紅框釋放普筹,如下圖所示:

剛好出現(xiàn)了indexcode和getResCode,很明顯var time是和時(shí)間有關(guān)的隘马,而返回值 window.JSonToCSV.missjson()要調(diào)用time太防,但我們不知道.missjson()方法的作用是什么,這時(shí)我們搜索一下missjson酸员,如下圖所示:

搜索結(jié)果有兩個(gè)蜒车,其中一個(gè)是剛才調(diào)用的missjson()函數(shù),另外一個(gè)是missjson函數(shù)的具體代碼幔嗦∧鹄ⅲ看不懂這代碼的作用是什么,也沒關(guān)系邀泉,我們直接把這missjson()函數(shù)全部保存在一個(gè)js文件中嬉挡。

寫js文件

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

這里需要注意的是:function必須要在missjson前面,這和JavaScript的語法有關(guān)因谎。

好了基括,js文件已經(jīng)寫好了,接下來我們調(diào)試一下js文件蓝角。

調(diào)試js文件

這里我們編寫程序來調(diào)試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()

首先導(dǎo)入execjs、js2py這兩個(gè)調(diào)試js文件的庫使鹅,再自定義方法get_js()來讀取js文件揪阶,并調(diào)用execjs.compile()方法來執(zhí)行js程序。運(yùn)行結(jié)果如下圖所示:

發(fā)現(xiàn)沒有報(bào)錯(cuò)患朱,但沒有得到我們想要的參數(shù)值鲁僚,這是因?yàn)槲覀冞€沒有編寫time時(shí)間的參數(shù)進(jìn)去。主要代碼如下圖所示:

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

首先調(diào)用js2py.eval_js()方法來處理獲取的時(shí)間裁厅,在通過.call()方法將return_js加密數(shù)據(jù)和時(shí)間結(jié)合在一起冰沙,最后返回mcode。

運(yùn)行結(jié)果如下圖所示:

好了执虹,mcode參數(shù)成功獲取下來了拓挥,接下來將正式編寫代碼來爬取國內(nèi)指數(shù)成分股行情數(shù)據(jù)。

實(shí)戰(zhàn)演練

scrapy框架爬蟲

創(chuàng)建scrapy框架爬蟲很簡(jiǎn)單袋励,執(zhí)行如下代碼即可:

scrapy startproject <Scrapy項(xiàng)目名>
cd <Scrapy項(xiàng)目名>
scrapy genspider <爬蟲名字> <允許爬取的域名>

其中侥啤,我們的Scrapy項(xiàng)目名為Shares叉存,爬蟲名字為:shares荚坞,允許爬取的域名為:網(wǎng)站URL曼尊。

好了創(chuàng)建Scrapy項(xiàng)目后届囚,接下來我們創(chuàng)建一個(gè)名為js的文件夾來存放剛才編寫的js文件,并把調(diào)試js文件的Read_js.py文件放在Scrapy項(xiàng)目中赁炎,項(xiàng)目目錄如下圖所示:

這樣我們的爬蟲準(zhǔn)備工作就做好了醉箕,接下來正式編寫代碼來獲取數(shù)據(jù)。

itmes.py文件

在獲取數(shù)據(jù)前徙垫,我們先在items.py文件中讥裤,定義爬取數(shù)據(jù)的字段,具體代碼如下所示:

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

這里我們只定義了網(wǎng)頁展示給我們數(shù)據(jù)的字段松邪,要想獲取更多數(shù)據(jù)坞琴,可以根據(jù)下圖自行定義字段:

發(fā)送網(wǎng)絡(luò)請(qǐng)求

定義好字段后哨查,我們要在spiders爬蟲文件中的shares.py文件中編寫start_requests()方法來發(fā)送網(wǎng)絡(luò)請(qǐng)求逗抑,主要代碼如下所示:

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

通過創(chuàng)建的data1字典來構(gòu)造Form Data表單數(shù)據(jù),由于是POST請(qǐng)求寒亥,所以我們要使用scrapy.FormRequest()方法來發(fā)送網(wǎng)絡(luò)請(qǐng)求邮府,發(fā)送網(wǎng)絡(luò)請(qǐng)求后,通過回調(diào)函數(shù)callback來將響應(yīng)內(nèi)容返回給parse()方法溉奕。

提取數(shù)據(jù)

在上一步中褂傀,我們成功獲取到了響應(yīng)內(nèi)容,接下來我們繼續(xù)編寫把響應(yīng)內(nèi)容解析并提取我們想要的數(shù)據(jù)加勤,主要代碼如下所示:

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('開盤價(jià)')
            item['Number_of_transactions']=i.get('成交數(shù)量')
            item['Closing_price']=i.get('收盤價(jià)')
            item['minimum_price']=i.get('最低價(jià)')
            item['Highest_price']=i.get('最高價(jià)')
            item['Securities_code']=i.get('證券代碼')
            item['Securities_abbreviation']=i.get('證券簡(jiǎn)稱')
            yield item

我們把響應(yīng)內(nèi)容通過json()的格式來獲取下來仙辟,再通過.get()方法把我們想要的數(shù)據(jù)提取出來,最后通過yield生成器將數(shù)據(jù)返回給引擎鳄梅。

保存數(shù)據(jù)

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

class mysqlPipeline:
    conn = None
    cursor = None
    def open_spider(self,spider):
        print('爬蟲開始K诤浮!孙蒙!')
        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('爬蟲結(jié)束?媛汀O阕贰!')
        self.cursor.close()

首先我們自定義pysqlPipeline類坦胶,然后編寫open_spider()方法來連接mysql數(shù)據(jù)庫透典,再通過process_item()方法來將數(shù)據(jù)存放在數(shù)據(jù)庫中歪玲,然后通過編寫close_spider()方法將數(shù)據(jù)庫關(guān)閉。

請(qǐng)求頭headers

接下來開始編寫請(qǐng)求頭headers掷匠,headers請(qǐng)求頭一般是在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': '網(wǎng)站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,
}

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

執(zhí)行爬蟲

好了,所有代碼已經(jīng)編寫完畢了才菠,接下來將執(zhí)行如下代碼即可運(yùn)行爬蟲程序:

scrapy crawl shares

運(yùn)行結(jié)果如下圖所示:

這里我們只獲取到了一天的數(shù)據(jù)茸时,當(dāng)我們要獲取多天的數(shù)據(jù)怎么辦呢?

獲取多天數(shù)據(jù)

獲取多天數(shù)據(jù)很簡(jiǎn)單赋访,只需要調(diào)用pandas.period_range()方法即可可都,將發(fā)送網(wǎng)絡(luò)請(qǐng)求中的代碼修改為如下代碼即可:

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

其中freq='B'表示工作日,運(yùn)行結(jié)果如下圖所示:

好了蚓耽,爬取某證信股票行情就講到這里了渠牲,感謝觀看!2接啤签杈!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鼎兽,隨后出現(xiàn)的幾起案子答姥,更是在濱河造成了極大的恐慌,老刑警劉巖谚咬,帶你破解...
    沈念sama閱讀 212,294評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹦付,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡序宦,警方通過查閱死者的電腦和手機(jī)睁壁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來互捌,“玉大人潘明,你說我怎么就攤上這事★踉耄” “怎么了钳降?”我有些...
    開封第一講書人閱讀 157,790評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)腌巾。 經(jīng)常有香客問我遂填,道長(zhǎng)铲觉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,595評(píng)論 1 284
  • 正文 為了忘掉前任吓坚,我火速辦了婚禮撵幽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘礁击。我一直安慰自己盐杂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,718評(píng)論 6 386
  • 文/花漫 我一把揭開白布哆窿。 她就那樣靜靜地躺著链烈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挚躯。 梳的紋絲不亂的頭發(fā)上强衡,一...
    開封第一講書人閱讀 49,906評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音码荔,去河邊找鬼漩勤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛目胡,可吹牛的內(nèi)容都是我干的锯七。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼誉己,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了域蜗?” 一聲冷哼從身側(cè)響起巨双,我...
    開封第一講書人閱讀 37,797評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霉祸,沒想到半個(gè)月后筑累,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,250評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丝蹭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,570評(píng)論 2 327
  • 正文 我和宋清朗相戀三年慢宗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奔穿。...
    茶點(diǎn)故事閱讀 38,711評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡镜沽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贱田,到底是詐尸還是另有隱情缅茉,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評(píng)論 4 332
  • 正文 年R本政府宣布男摧,位于F島的核電站蔬墩,受9級(jí)特大地震影響译打,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拇颅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,018評(píng)論 3 316
  • 文/蒙蒙 一奏司、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧樟插,春花似錦结澄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猜扮,卻和暖如春勉吻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背旅赢。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評(píng)論 1 266
  • 我被黑心中介騙來泰國打工齿桃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煮盼。 一個(gè)月前我還...
    沈念sama閱讀 46,461評(píng)論 2 360
  • 正文 我出身青樓短纵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親僵控。 傳聞我的和親對(duì)象是個(gè)殘疾皇子香到,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,595評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容