python爬蟲實戰-抓取同花順股票信息

前言:

     在之前介紹requests的基礎上,現在開始進行實戰。

    目標網站:http://q.10jqka.com.cn//index/index/board/all/field/zdf/order/desc/page/

一  涉及到的技術點:

                         (1)requests: 用於網頁請求

                          (2)beautifulsoup:用於提取數據

                           (3)urllib.parse:用於拼接url

                            (4)cvs:用於保存結果

二 數據結構分析:

首先看下要抓取的數據的外觀:

點擊下一頁發現在搜索框中的url並未發生變化,這個時候可以猜想,其數據加載的方式採用的ajax加載的方式,因此,右鍵—>“查看元素”,點擊網絡—>消息頭,如下圖:

可以發現第二頁的url爲:http://q.10jqka.com.cn/index/index/board/all/field/zdf/order/desc/page/2/ajax/1/

點擊下一頁,可以發現第三頁的url爲:http://q.10jqka.com.cn/index/index/board/all/field/zdf/order/desc/page/3/ajax/1/

從上面可以看出,不同頁面的url的區別在於page後面的數字,可以再多看幾個頁面來驗證這個猜想。

通過上面的分析,可以得出數據是通過ajax的方式加載出來的。接下來查看通過ajax加載的數據格式是什麼樣的。

點擊 網絡下面的響應,可以看到響應載荷,如下圖:

通過url獲取到部分數據如下:

<table class="m-table m-pager-table">
                <thead>
                <tr>
                    <th style="width:4%">序號</th>
                    <th style="width:6%">代碼</th>
                    <th style="width:8%">名稱</th>
                    <th style="width:6%" ><a href="javascript:void(0)" field="xj" >現價<i></i></a></th>
                    <th style="width:8%"  class="cur"><a href="javascript:void(0)" field="zdf" order="desc"  class="desc">漲跌幅(%)<i></i></a></th>
                    <th style="width:6%" ><a href="javascript:void(0)" field="zd" >漲跌<i></i></a></th>
                    <th style="width:8%" ><a href="javascript:void(0)" field="zs" >漲速(%)<i></i></a></th>
                    <th style="width:8%" ><a href="javascript:void(0)" field="hs" >換手(%)<i></i></a></th>
                    <th style="width:6%" ><a href="javascript:void(0)" field="lb" >量比<i></i></a></th>
                    <th style="width:6%" ><a href="javascript:void(0)" field="zf" >振幅(%)<i></i></a></th>
                    <th style="width:7%" ><a href="javascript:void(0)" field="cje" >成交額<i></i></a></th>
                    <th style="width:8%" ><a href="javascript:void(0)" field="ltg" >流通股<i></i></a></th>
                    <th style="width:8%" ><a href="javascript:void(0)" field="ltsz" >流通市值<i></i></a></th>
                    <th style="width:7%" ><a href="javascript:void(0)" field="syl" >市盈率<i></i></a></th>
                    <!--th>概念題材</th-->
                    <th style="width:4%">加自選</th>
                </tr>
                </thead>
                <tbody>
                                <tr>
                    <td>41</td>
                    <td><a href="http://stockpage.10jqka.com.cn/603718/" target="_blank">603718</a></td>
                    <td><a href="http://stockpage.10jqka.com.cn/603718/" target="_blank">海利生物</a></td>
                    <td class="c-rise">13.53</td>
                    <td class="c-rise">6.28</td>
                    <td class="c-rise">0.80</td>
                    <td class="c-fall">-0.15</td>
                    <td>1.53</td>
                    <td class="c-rise">1.75</td>
                    <td class="c-rise">7.86</td>
                    <td>1.32億</td>
                    <td>6.44億</td>
                    <td>87.13億</td>
                    <td>140.56</td>
                    <td><a class="j_addStock" title="加自選" href="javascript:void(0);"><img src="http://i.thsi.cn/images/q/plus_logo.png" alt=""></a></td>
                </tr>
                                <tr>
                    <td>42</td>
                    <td><a href="http://stockpage.10jqka.com.cn/002331/" target="_blank">002331</a></td>
                    <td><a href="http://stockpage.10jqka.com.cn/002331/" target="_blank">皖通科技</a></td>
                    <td class="c-rise">9.15</td>
                    <td class="c-rise">6.15</td>
                    <td class="c-rise">0.53</td>
                    <td class="">--</td>
                    <td>2.66</td>
                    <td class="c-rise">1.51</td>
                    <td class="c-rise">6.15</td>
                    <td>7247.43萬</td>
                    <td>3.03億</td>
                    <td>27.75億</td>
                    <td>41.17</td>
                    <td><a class="j_addStock" title="加自選" href="javascript:void(0);"><img src="http://i.thsi.cn/images/q/plus_logo.png" alt=""></a></td>
                </tr>

可以看到其數據都是包含在html的標籤中。一個<tr>標籤包含一個股票信息。

至此,數據分析部分已經完成。

 

三 需求分析及模塊劃分

同花順股票網站具有很好的反扒能力,因此,需要增加一部分功能來應對這部分問題。在簡單怕爬蟲的基礎上需要增加,改變請求頭和增加代理的功能,因此整個函數的模塊劃分如下:

(1)代理獲取模塊

(2)url構建模塊

(3)失敗無限嘗試模塊

(4)下載模塊

(5)解析及存儲模塊

(6)配置模塊

 

四 模塊設計

4.1 代理獲取模塊

代碼如下:

 def proxy_get(self, num_retries=2):
        """
        #代理獲取模塊
        """
        try:
            r_proxy = requests.get(self.PROXY_POOL_API, timeout = 5)
            proxy = r_proxy.text    #指定代理
            print("代理是", proxy)
            proxies = {
                "http": 'http://' + proxy,
                "https": 'https://' + proxy,
                }
            return proxies
        except:
            if num_retries > 0:
                print("代理獲取失敗,重新獲取")
self.proxy_get(num_retries-1)

爲了防止網絡狀況不佳等其他原因導致獲取代理失敗,增加了超時等待和失敗重試功能

 

4.2  url 構建模塊

    在前文分析的基礎上,得到不同頁面的url的區別是數字這的不同,因此這部分的代碼如下:

def url_yield(self):
        """
        :func 用於生成url
        :yield items
        """
        for i in range(1, self.MAX_PAGE + 1 ):
            self.PAGE_TRACK = i         #頁面追蹤
            self.FLAG += 1              #每次加1
            print('FLAG 是:', self.FLAG)
            url = "{}{}{}".format(self.URL_START, i, self.PARAMS) 
            yield url

使用yield函數,每次只返回一個url

 

4.3 失敗無限嘗試模塊

將抓取失敗的url,再次進行抓取

def url_omi(self):
        print("開始補漏")
        length_pl = len(self.PAGE_LIST) 
        if length_pl != 0:          #判斷是否爲空
            for i in range(length_pl):
                self.PAGE_TRACK = self.PAGE_LIST.pop(0)                  #構造一個動態列表, 彈出第一個元素
                url = "{}{}{}".format(self.URL_START, self.PAGE_TRACK, self.PARAMS) 
                yield url

4.4 下載模塊

包含更換請求頭,失敗重試功能和代理是否變更的功能。

 def downloader(self, url, num_retries=3):
        if self.proxy_con == 0:
            proxies = self.proxy_get()  #獲取代理
        else:
            proxies = self.proxy_save   #繼續使用代理
        self.proxy_save = proxies       #更換代理值
        headers_list = [{
                    'Accept': 'text/html, */*; q=0.01',
                    'Accept-Encoding': 'gzip, deflate, sdch',
                    'Accept-Language': 'zh-CN,zh;q=0.8',
                    'Connection': 'keep-alive',
                    'Cookie':'log=; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1533992361,1533998469,1533998895,1533998953; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1533998953; user=MDrAz9H9akQ6Ok5vbmU6NTAwOjQ2OTU0MjIzNDo3LDExMTExMTExMTExLDQwOzQ0LDExLDQwOzYsMSw0MDs1LDEsNDA7MSwxLDQwOzIsMSw0MDszLDEsNDA7NSwxLDQwOzgsMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEsNDA6Ojo6NDU5NTQyMjM0OjE1MzM5OTkwNzU6OjoxNTMzOTk5MDYwOjg2NDAwOjA6MTZmOGFjOTgwMGNhMjFjZjRkMWZlMjk0NDQ4M2FhNDFkOmRlZmF1bHRfMjox; userid=459542234; u_name=%C0%CF%D1%FDjD; escapename=%25u8001%25u5996jD; ticket=7c92fb758f81dfa4399d0983f7ee5e53; v=Ajz6VIblS6HlDX_9PqmhBV0QDdH4NeBfYtn0Ixa9SCcK4daNPkWw77LpxLZl',
                    'hexin-v': 'AiDRI3i0b1qEZNNemO_FOZlE8SXqKQQBpg9Y4Jox7pbOH8oZQjnUg_YdKIHp',
                    'Host': 'q.10jqka.com.cn',
                    'Referer': 'http://q.10jqka.com.cn/',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
                    },{'Accept': 'text/html, */*; q=0.01', 
                    'Accept-Encoding': 'gzip, deflate, sdch', 
                    'Accept-Language': 'zh-CN,zh;q=0.8', 
                    'Connection': 'keep-alive', 
                    'Cookie': 'user=MDq62tH9NUU6Ok5vbmU6NTAwOjQ2OTU0MjA4MDo3LDExMTExMTExMTExLDQwOzQ0LDExLDQwOzYsMSw0MDs1LDEsNDA7MSwxLDQwOzIsMSw0MDszLDEsNDA7NSwxLDQwOzgsMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEsNDA6Ojo6NDU5NTQyMDgwOjE1MzM5OTg4OTc6OjoxNTMzOTk4ODgwOjg2NDAwOjA6MTEwOTNhMzBkNTAxMWFlOTg0OWM1MzVjODA2NjQyMThmOmRlZmF1bHRfMjox; userid=459542080; u_name=%BA%DA%D1%FD5E; escapename=%25u9ed1%25u59965E; ticket=658289e5730da881ef99b521b65da6af; log=; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1533992361,1533998469,1533998895,1533998953; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1533998953; v=AibgksC3Qd-feBV7t0kbK7PCd5e-B2rBPEueJRDPEskkk8xLeJe60Qzb7jDj', 'hexin-v': 'AiDRI3i0b1qEZNNemO_FOZlE8SXqKQQBpg9Y4Jox7pbOH8oZQjnUg_YdKIHp', 
                    'Host': 'q.10jqka.com.cn', 
                    'Referer': 'http://q.10jqka.com.cn/', 
                    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 
                    },
                    {'Accept': 'text/html, */*; q=0.01', 'Accept-Encoding': 'gzip, deflate, sdch', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Connection': 'keep-alive', 'Cookie': 'user=MDq62sm9wM%2FR%2FVk6Ok5vbmU6NTAwOjQ2OTU0MTY4MTo3LDExMTExMTExMTExLDQwOzQ0LDExLDQwOzYsMSw0MDs1LDEsNDA7MSwxLDQwOzIsMSw0MDszLDEsNDA7NSwxLDQwOzgsMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEsNDA6Ojo6NDU5NTQxNjgxOjE1MzM5OTg0NjI6OjoxNTMzOTk4NDYwOjg2NDAwOjA6MTAwNjE5YWExNjc2NDQ2MGE3ZGYxYjgxNDZlNzY3ODIwOmRlZmF1bHRfMjox; userid=459541681; u_name=%BA%DA%C9%BD%C0%CF%D1%FDY; escapename=%25u9ed1%25u5c71%25u8001%25u5996Y; ticket=4def626a5a60cc1d998231d7730d2947; log=; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1533992361,1533998469; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1533998496; v=AvYwAjBHsS9PCEXLZexL20PSRyfuFzpQjFtutWDf4ll0o5zbyKeKYVzrvsAz', 'hexin-v': 'AiDRI3i0b1qEZNNemO_FOZlE8SXqKQQBpg9Y4Jox7pbOH8oZQjnUg_YdKIHp', 'Host': 'q.10jqka.com.cn', 'Referer': 'http://q.10jqka.com.cn/', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest'},
                    {'Accept': 'text/html, */*; q=0.01', 'Accept-Encoding': 'gzip, deflate, sdch', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Connection': 'keep-alive', 'Cookie': 'Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1533992361; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1533992361; user=MDq62sm9SnpsOjpOb25lOjUwMDo0Njk1NDE0MTM6NywxMTExMTExMTExMSw0MDs0NCwxMSw0MDs2LDEsNDA7NSwxLDQwOzEsMSw0MDsyLDEsNDA7MywxLDQwOzUsMSw0MDs4LDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxLDQwOjo6OjQ1OTU0MTQxMzoxNTMzOTk4MjA5Ojo6MTUzMzk5ODE2MDo4NjQwMDowOjFlYTE2YTBjYTU4MGNmYmJlZWJmZWExODQ3ODRjOTAxNDpkZWZhdWx0XzI6MQ%3D%3D; userid=459541413; u_name=%BA%DA%C9%BDJzl; escapename=%25u9ed1%25u5c71Jzl; ticket=b909a4542156f3781a86b8aaefce3007; v=ApheKMKxdxX9FluRdtjNUdGcac08gfwLXuXQj9KJ5FOGbTKxepHMm671oBoh', 'hexin-v': 'AiDRI3i0b1qEZNNemO_FOZlE8SXqKQQBpg9Y4Jox7pbOH8oZQjnUg_YdKIHp', 'Host': 'q.10jqka.com.cn', 'Referer': 'http://q.10jqka.com.cn/', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36', 'X-Requested-With': 'XMLHttpRequest'},

                    ]

        try:
            time.sleep(random.random()*5)   #設置延時
            headers = random.choice(headers_list)
            r = requests.get(url, headers = headers, proxies=proxies, timeout=4)
        except:
            if num_retries > 0:
                print("重新下載")
                self.proxy_con = 0  #更換代理
                self.downloader(url,num_retries-1)
            else:
                if not self.PAGE_TRACK in self.PAGE_LIST:    #首先應該判斷 該頁是否存在列表中,如果不存在, 則將其加入其中
                        self.PAGE_LIST.append(self.PAGE_TRACK)   #將獲取失敗的url保存起來,後面再次循環利用,將元素添加在末尾,
        else:            
             return r.text

4.5 解析及存儲模塊

這部分用來進行解析並將結果存儲到csv中

 def items_return(self):
        sys.setrecursionlimit(5000)
        count = 0
        while True:
            if self.FLAG < self.MAX_PAGE:
                url_list = self.url_yield()   #獲取url
            else:
                url_list = self.url_omi()
                if len(PAGE_LIST) ==0:
                    break
            print("執行到了獲取模塊")

            for url in url_list:
                html = self.downloader(url)
                #打印提示信息
                print('URL is:', url)
                items = {}   #建立一個空字典,用於信息存儲
                try:                      
                    soup = BeautifulSoup(html, 'lxml')
                    for tr in soup.find('tbody').find_all('tr'):
                        td_list = tr.find_all('td')
                        items['代碼'] = td_list[1].string
                        items['名稱'] = td_list[2].string
                        items['現價'] = td_list[3].string
                        items['漲跌幅'] = td_list[4].string
                        self.writer.writerow(items)
                        print(items)
                        print("保存成功")
                        #如果保存成功,則繼續使用代理
                        self.proxy_con = 1
                        #print("解析成功")
                        #yield items          #將結果返回
                except:
                    print("解析失敗")
                    #解析失敗,則將代理換掉
                    self.proxy_con = 0   
                    #print(html)
                    if not self.PAGE_TRACK in self.PAGE_LIST:
                        self.PAGE_LIST.append(self.PAGE_TRACK)
                    else:
                        count += 1

            if count == 2:
                break

4.5 配置模塊

爲了方便項目的管理,可以將部分參數的配置放到一個setting.py文件中

#必要參數設置
MAX_PAGE = 165   #最大頁數
PAGE_TRACK = 1   #追蹤到了第幾頁
MAX_GET = 1      #獲取最大嘗試次數
MAX_PARSE = 1    #解析嘗試最大次數
MAX_CSV = 1      #文件保存最大次數
MAX_PROXY =1     #獲取代理的最大次數
MAX_START = 1    #MAX_*的初始值
MAX_TRY = 4      #最大嘗試次數
FLAG = 0         #用於標識,是否使用 url_omi() 函數

#初始鏈接
URL_START = "http://q.10jqka.com.cn//index/index/board/all/field/zdf/order/desc/page/"
PARAMS = "/ajax/1/"


#第一次爬取的 html 缺失的頁面 的url 列表
#先進先出的列表
PAGE_LIST = [] 

#代理池接口
PROXY_POOL_API = "http://127.0.0.1:5555/random"  

headers = {
            'Accept': 'text/html, */*; q=0.01',
            'Accept-Encoding': 'gzip, deflate, sdch',
            'Accept-Language': 'zh-CN,zh;q=0.8',
            'Connection': 'keep-alive',
            'Cookie': 'spversion=20130314; __utma=156575163.1163133091.1530233537.1530289428.1530369413.3; __utmz=156575163.1530369413.3.3.utmcsr=stockpage.10jqka.com.cn|utmccn=(referral)|utmcmd=referral|utmcct=/; Hm_lvt_78c58f01938e4d85eaf619eae71b4ed1=1530444468,1530505958,1530506333,1530516152; Hm_lpvt_78c58f01938e4d85eaf619eae71b4ed1=1530516152; historystock=300033%7C*%7C1A0001; v=AiDRI3i0b1qEZNNemO_FOZlE8SXqKQQBpg9Y4Jox7pbOH8oZQjnUg_YdKIHp',
            'hexin-v': 'AiDRI3i0b1qEZNNemO_FOZlE8SXqKQQBpg9Y4Jox7pbOH8oZQjnUg_YdKIHp',
            'Host': 'q.10jqka.com.cn',
            'Referer': 'http://q.10jqka.com.cn/',
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}

至此,整個項目已經完成。

後記:

        源碼獲取可以關注公衆號,發送“ths”即可獲取下載鏈接。另外公衆號還會介紹大數據的相關知識,如hadoop, flink, spark

等,歡迎關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章