• 使用flask和BeautifulSoup展示关注的雪球组合信息


      使用flask和BeautifulSoup开发的单页面应用,获取雪球ID关注的组合的调仓信息和关注组合的累计股票仓位。可以在github下载调试。

      页面加载后显示效果:

      后端部分:

      1 # -*- coding: utf-8 -*-
      2 
      3 import json
      4 import re
      5 import urllib.request
      6 from bs4 import BeautifulSoup
      7 from flask import Flask, render_template, request, jsonify
      8 import time
      9 import pandas
     10 
     11 app = Flask(__name__)
     12 app.config['JSON_AS_ASCII'] = False
     13 projects = {}
     14 ZHs0={}
     15 ZHs1={}
     16 cookie = 's=7017rril9u; xq_a_token=c4a084fe79a31a6ead299d4d49d622cab3b3b65e; xqat=c4a084fe79a31a6ead299d4d49d622cab3b3b65e; xq_r_token=fc287dc024ce197b1a0e1def6f674c260357bebf; xq_is_login=1; u=1180102135; xq_token_expire=Tue%20Mar%2014%202017%2016%3A56%3A10%20GMT%2B0800%20(CST); bid=45efaa8643ba70c7f4357d0930ff99d4_iz9kzepe'
     17 
     18 def prof(url_ap0):
     19     url = 'https://xueqiu.com/cubes/rebalancing/history.json?cube_symbol='+url_ap0+'&count=20&page=1'
     20     req = urllib.request.Request(url,headers = {
     21            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36',
     22            'cookie':cookie
     23            })
     24     html = urllib.request.urlopen(req).read().decode('utf-8')
     25     data = json.loads(html)
     26 
     27     for i in range (len(data['list'])):
     28         for j in range(len(data['list'][i]['rebalancing_histories'])):
     29             if pandas.isnull(data['list'][i]['rebalancing_histories'][j]['prev_weight_adjusted']):
     30                 data['list'][i]['rebalancing_histories'][j]['prev_weight_adjusted'] = str(0)
     31             else:
     32                 data['list'][i]['rebalancing_histories'][j]['prev_weight_adjusted'] = str(data['list'][i]['rebalancing_histories'][j]['prev_weight_adjusted'])
     33             if pandas.isnull(data['list'][i]['rebalancing_histories'][j]['target_weight']):
     34                 data['list'][i]['rebalancing_histories'][j]['target_weight'] = str(0)
     35             else:
     36                 data['list'][i]['rebalancing_histories'][j]['target_weight'] = str(data['list'][i]['rebalancing_histories'][j]['target_weight'])
     37     try:
     38         for i in range(len(data['list'])):
     39             localtime = time.strftime("%y-%m-%d %H:%M:%S", time.localtime(data['list'][i]['updated_at'] / 1000))
     40             if (time.time() - (data['list'][i]['updated_at'] / 1000)) < 86400*20:
     41                 for j in range(len(data['list'][i]['rebalancing_histories'])):
     42                     ZHs1[j-data['list'][i]['updated_at']]=(localtime,url_ap0,ZHs0[url_ap0],data['list'][i]['rebalancing_histories'][j]['stock_name'] + ': '+ data['list'][i]['rebalancing_histories'][j]['prev_weight_adjusted'] + '% ' + ("" if float(data['list'][i]['rebalancing_histories'][j]['prev_weight_adjusted'])<float(data['list'][i]['rebalancing_histories'][j]['target_weight']) else "") + ' '+ data['list'][i]['rebalancing_histories'][j]['target_weight'] + '%')
     43     except:
     44         print("exception occured")
     45 
     46 
     47 def get_xueqiu_hold(url):
     48     req = urllib.request.Request(url,headers = {
     49                 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36',
     50                 'cookie':cookie
     51                })
     52     soup = urllib.request.urlopen(req).read().decode('utf-8')
     53     soup = BeautifulSoup(soup, 'lxml')
     54     script = soup.find('script', text=re.compile('SNB.cubeInfo'))
     55     json_text = re.search(r'^s*SNB.cubeInfos*=s*({.*?})s*;s*$',
     56                       script.string, flags=re.DOTALL | re.MULTILINE).group(1)
     57     data = json.loads(json_text)
     58     for d in data["view_rebalancing"]["holdings"]:
     59         if d['stock_name'] in projects.keys():
     60             projects[d['stock_name']] += d['weight']            
     61         else:
     62             projects[d['stock_name']]= d['weight']
     63     
     64 
     65 @app.route("/", methods=['GET', 'POST'])
     66 def index():
     67     return render_template("index.html")
     68 
     69 
     70 @app.route('/start', methods=['POST'])
     71 def post_url():
     72     # get url
     73     projects.clear()
     74     ZHs0.clear()
     75     ZHs1.clear()    
     76     data = json.loads(request.data.decode())
     77     url = data["url"]
     78     url0 = 'https://xueqiu.com/stock/portfolio/stocks.json?size=1000&pid=-1&tuid='+url+'&cuid=1180102135&_=1477728185503'
     79     req = urllib.request.Request(url0,headers = {
     80            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36',
     81            'cookie':cookie
     82            })
     83     html = urllib.request.urlopen(req).read().decode('utf-8')
     84     data = json.loads(html)
     85     for item in data["stocks"]:
     86         if re.search('ZHd{6}',str(item)):
     87             ZHs0[item["code"]]=item["stockName"]
     88     for ZH0 in ZHs0:
     89         prof(ZH0)
     90     ZHs = re.findall('ZHd{6}',data["portfolios"][0]["stocks"])
     91     for ZH in ZHs:
     92         get_xueqiu_hold("https://xueqiu.com/P/"+ZH)
     93     return url
     94 
     95 @app.route("/data")
     96 def data():
     97     projects0 = sorted(projects.items(), key=lambda x: (-x[1],x[0]))[:12]
     98     return jsonify(dict(projects0))
     99 
    100 @app.route("/trans0")
    101 def trans0():
    102     return jsonify(ZHs0)
    103 
    104 @app.route("/trans1")
    105 def trans1():
    106     return jsonify(ZHs1)
    107 
    108 if __name__ == "__main__":
    109     app.run(host='0.0.0.0',port=5000,debug=True)

      前端html:

     1 <!DOCTYPE html>
     2 <html ng-app="XueqiuholdApp">
     3   <head>
     4     <link rel="shortcut icon" href="https://assets.imedao.com/images/vipicon_4@2x.png" />
     5     <title>你关注的雪球组合持仓</title>
     6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     7     <!-- styles -->
     8 <!-- 新 Bootstrap 核心 CSS 文件 -->
     9 <link href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    10 
    11 <!-- 可选的Bootstrap主题文件(一般不使用) -->
    12 <script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap-theme.min.css"></script>
    13 <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}">
    14 
    15 
    16   </head>
    17     <body ng-controller="XueqiuholdController">
    18     <div class="container">
    19       <div class="row">
    20         <div class="col-sm-7">
    21           <div class="row-sm-7 col-sm-offset-1">
    22           <h2>你的雪球ID</h2>
    23           <br>
    24           <form role="form" ng-submit="getResults()">
    25             <div class="form-group">
    26               <input type="text" name="url" class="form-control" id="url-box" placeholder="雪球ID" style="max- 300px;" ng-model="url" required>
    27             </div>
    28             {% raw %}
    29               <button type="submit" class="btn btn-primary" ng-disabled="loading">{{ submitButtonText }}</button>
    30             {% endraw %}
    31           </form>
    32           <div class="alert alert-danger" role="alert" ng-show='urlError'>
    33             <span aria-hidden="true"></span>
    34             <span class="sr-only">Error:</span>
    35             <span>There was an error submitting your URL.<br>
    36             Please check to make sure it is valid before trying again.</span>
    37           </div> 
    38         </div>
    39           {% raw %}
    40           <br>
    41           <h4>关注组合最新调仓:</h4>
    42           <div style="overflow: auto;">
    43           <table class="table table-fixed table-striped">
    44           <tbody>
    45             <tr class="table-row" ng-repeat="(key, val) in trans">
    46               <td Style="text-align:middle" item-width="162">{{val[0]}}</td>
    47               <td Style="text-align:left" item-width="132"> {{val[2]}} </td>
    48               <td Style="text-align:left" item-width="132"> {{val[1]}} </td>
    49               <td Style="text-align:right" item-width="256"> {{val[3]}}</td>
    50             </tr>
    51           </tbody>
    52           </table> 
    53           </div>
    54           {% endraw %}
    55         </div>
    56         <div class="col-sm-3 col-sm-offset-1">
    57           <br><br>
    58           <h4>关注组合累计仓位</h4>
    59           <div id="results">
    60             <table class="table table-striped">
    61               <thead>
    62                 <tr>
    63                   <th>Stock</th>
    64                   <th>Percent</th>
    65                 </tr>
    66               </thead>
    67               {% raw %}
    68               <tbody>
    69                 <tr ng-repeat="(key, val) in xueqiuhold | orderBy:'key'">
    70                   <td>{{key}}</td>
    71                   <td>{{val.toFixed(2)}}%</td>
    72                 </tr>
    73               </tbody>
    74               {% endraw %}
    75             </table>
    76           </div>
    77           <img class="col-sm-3 col-sm-offset-4" src="{{ url_for('static',
    78             filename='spinner.gif') }}" ng-show="loading">
    79         </div>
    80       </div>
    81       <br>
    82       <div class="row-sm-12 row-sm-offset-1">      
    83       <wordcount-chart data="xueqiuhold"></wordcount-chart>
    84       </div>
    85     </div>
    86     <br><br>
    87     <!-- scripts -->
    88     <script src="{{ url_for('static', filename='d3.js') }}" charset="utf-8"></script>
    89     <script src="{{ url_for('static', filename='angular.js') }}"></script>
    90     <script src="{{ url_for('static', filename='main.js') }}"></script>
    91     <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
    92 <script src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>
    93 
    94 <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    95 <script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    96   </body>
    97 </html>

      前端JavaScript部分:

      1 (function () {
      2 
      3   'use strict';
      4 
      5   angular.module('XueqiuholdApp', [])
      6 
      7   .controller('XueqiuholdController', ['$scope', '$log', '$http', '$timeout',
      8     function($scope, $log, $http, $timeout) {
      9 
     10     $scope.submitButtonText = 'Submit';
     11     $scope.loading = false;
     12     $scope.urlerror = false;
     13 
     14     $scope.getResults = function() {
     15 
     16       $log.log('test');
     17 
     18       // get the URL from the input
     19       var userInput = $scope.url;
     20 
     21       // fire the API request
     22       $http.post('/start', {'url': userInput}).
     23         success(function(results) {
     24           $log.log(results);
     25           getXueqiuHold();
     26           $scope.xueqiuhold = null;
     27           $scope.trans = null;
     28           $scope.loading = true;
     29           $scope.submitButtonText = 'Loading...';
     30           $scope.urlerror = false;
     31         }).
     32         error(function(error) {
     33           $log.log(error);
     34         });
     35 
     36     };
     37 
     38     function getXueqiuHold() {
     39 
     40       var timeout = '';
     41 
     42       var poller = function() {
     43         // fire another request
     44         $http.get('/data').
     45           success(function(data, status, headers, config) {
     46             if(status === 202) {
     47               $log.log(data, status);
     48             } else if (status === 200){
     49               $log.log(data);
     50               $scope.loading = false;
     51               $scope.submitButtonText = "Submit";
     52               $scope.xueqiuhold = data;
     53                 $http.get('/trans1').
     54                   success(function(data, status, headers, config) {
     55                     if(status === 202) {
     56                       $log.log(data, status);
     57                     } else if (status === 200){
     58                       $log.log(data);
     59                       $scope.trans = data;
     60               }});
     61               $timeout.cancel(timeout);
     62               return false;
     63             }
     64             // continue to call the poller() function every 2 seconds
     65             // until the timeout is cancelled
     66             timeout = $timeout(poller, 2000);
     67           }).
     68           error(function(error) {
     69             $log.log(error);
     70             $scope.loading = false;
     71             $scope.submitButtonText = "Submit";
     72             $scope.urlerror = true;
     73           });
     74       };
     75 
     76       poller();
     77 
     78     }
     79 
     80   }])
     81 
     82   .directive('wordcountChart', ['$parse', function ($parse) {
     83     return {
     84       restrict: 'E',
     85       replace: true,
     86       template: '<div id="chart"></div>',
     87       link: function (scope) {
     88         scope.$watch('xueqiuhold', function() {
     89           d3.select('#chart').selectAll('*').remove();
     90           var data = scope.xueqiuhold;
     91           for (var word in data) {
     92             d3.select('#chart')
     93               .append('div')
     94               .selectAll('div')
     95               .data(word[0])
     96               .enter()
     97               .append('div')
     98               .style('width', function() {
     99                 return (data[word]*3) + 'px';
    100               })
    101               .text(function(d){
    102                 return word;
    103               });
    104           }
    105         }, true);
    106       }
    107      };
    108   }]);
    109 
    110 }());
  • 相关阅读:
    Entity Framework EF6使用 MySql创建数据库异常解决办法
    在c#中使用bitblt显示图片
    百度指数完美采集器
    使用HtmlAgilityPack解析Html(非常好用)
    线程的暂停与继续
    webform 中使用ajax
    OkHttp+Stetho+Chrome调试android网络部分(原创)
    android最佳实践的建议(翻译自android-best-practices)
    Android最流行的网络框架(原创)
    android studio 中配置androidAnnotation 的新版正确配置
  • 原文地址:https://www.cnblogs.com/newer027/p/6418821.html
Copyright © 2020-2023  润新知