├── .gitignore ├── .idea ├── misc.xml ├── modules.xml ├── vcs.xml ├── web.iml └── workspace.xml ├── README.md ├── app ├── __init__.py ├── city │ ├── __init__.py │ └── views.py ├── job │ ├── __init__.py │ └── views.py ├── main │ ├── __init__.py │ └── views.py ├── models.py ├── python │ ├── __init__.py │ ├── geo_info.py │ └── views.py ├── salary │ ├── __init__.py │ └── views.py ├── static │ ├── base.css │ ├── city │ │ ├── city.css │ │ └── city.js │ ├── job │ │ ├── job.css │ │ └── job.js │ ├── main │ │ ├── index.css │ │ ├── index.js │ │ └── index_backup.js │ ├── myicon.ico │ ├── python │ │ ├── python.css │ │ └── python.js │ └── salary │ │ ├── salary.css │ │ └── salary.js └── templates │ ├── base.html │ ├── city │ └── city.html │ ├── job │ └── job.html │ ├── main │ └── index.html │ ├── nav.html │ ├── python │ └── index.html │ └── salary │ └── salary.html ├── config.py ├── manage.py ├── mobile.jpg └── pc.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/web.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | true 54 | DEFINITION_ORDER 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 1475229706346 164 | 165 | 166 | 1475229706346 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # internet_job_analysis 2 | * 基于互联网岗位需求的分析报告。 3 | * 数据来源于拉勾网在2016年8月16日前后的招聘信息,爬虫项目见[Lagou_job](https://github.com/ioiogoo/lagou_spider) 4 | * 支持通过城市、职业、薪资水平任意条件查询报告 5 | 6 | # 前端 7 | * 使用boostrap框架,响应式设计 8 | * 数据图表使用C3.js生成 9 | * C3.js基于D3.js,更方便使用 10 | 11 | # 数据来源 12 | * 储存在mysql数据库中 13 | * [Lagou_job](https://github.com/ioiogoo/lagou_spider) 14 | 15 | # 感谢以下开源项目 16 | * [Bootstrap](http://www.bootcss.com/) 17 | * [C3.js](http://c3js.org/) 18 | * [D3.js](https://github.com/d3/d3) 19 | * [Scrapy](http://scrapy.org/) 20 | 21 | # 展示 22 | * 在线地址 : [互联网行业招聘需求 统计与分析](http://lagou.ioiogoo.cn/) 23 | *  24 | 25 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | # *-* coding:utf-8 *-* 2 | from flask import Flask 3 | from config import config 4 | from flask_bootstrap import Bootstrap 5 | import logging 6 | 7 | # 添加日志信息 8 | logger = logging.getLogger() 9 | fh = logging.FileHandler('./logs/internet_job_website.log') 10 | sh = logging.StreamHandler() 11 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 12 | fh.setFormatter(formatter) 13 | sh.setFormatter(formatter) 14 | logger.addHandler(fh) 15 | logger.addHandler(sh) 16 | logger.setLevel(logging.DEBUG) 17 | 18 | bootstrap = Bootstrap() 19 | 20 | 21 | # 工厂函数 22 | def create_app(ConfigName='DefultConfigName'): 23 | app = Flask(__name__) 24 | 25 | app.config.from_object(config[ConfigName]) 26 | bootstrap.init_app(app) 27 | 28 | from .main import main 29 | app.register_blueprint(main) 30 | from .city import city 31 | app.register_blueprint(city, url_prefix='/city') 32 | from .job import job 33 | app.register_blueprint(job, url_prefix='/keyword') 34 | from .salary import salary 35 | app.register_blueprint(salary, url_prefix='/salary') 36 | from .python import python 37 | app.register_blueprint(python, url_prefix='/python_analysis') 38 | 39 | 40 | return app 41 | -------------------------------------------------------------------------------- /app/city/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | city = Blueprint('city', __name__) 4 | from . import views -------------------------------------------------------------------------------- /app/city/views.py: -------------------------------------------------------------------------------- 1 | from . import city 2 | from flask import render_template, current_app, abort, request 3 | import json 4 | from app import logger 5 | 6 | @city.route('/') 7 | def index(cityname): 8 | try: 9 | sql = 'select keyword,count(*) from job_info where city="%s" group by keyword order by count(*) DESC limit 20' % cityname 10 | current_app.cur.execute(sql) 11 | results = current_app.cur.fetchall() 12 | if not results: 13 | logger.warning('mysqldb has no %s' % cityname) 14 | abort(404) 15 | job_category_counts = [int(x[1]) for x in results] 16 | job_name = [x[0].encode('utf-8') for x in results] 17 | job_name = json.dumps(job_name) 18 | sql = 'select salary,count(*) from job_info where city="%s" group by salary order by count(*) DESC limit 30' % cityname 19 | current_app.cur.execute(sql) 20 | results = current_app.cur.fetchall() 21 | if not results: 22 | logger.warning('mysqldb has no %s' % cityname) 23 | abort(404) 24 | salary_json = {key[0].encode('utf-8'):int(key[1]) for key in results} 25 | salary_json = json.dumps(salary_json) 26 | logger.info('success city.index url: %s ip: %s' % (request.url, request.remote_addr)) 27 | return render_template('/city/city.html', cityname=cityname, job_name=job_name, job_category_counts=job_category_counts, salary_json=salary_json) 28 | except Exception as e: 29 | logger.warning('city.index error: %s url: %s ip: %s' % (e, request.url, request.remote_addr)) 30 | abort(404) -------------------------------------------------------------------------------- /app/job/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | job = Blueprint('job', __name__) 4 | from . import views -------------------------------------------------------------------------------- /app/job/views.py: -------------------------------------------------------------------------------- 1 | from . import job 2 | from flask import url_for, render_template, current_app, abort, request 3 | import json 4 | from app import logger 5 | 6 | @job.route('/') 7 | def index(keywordname): 8 | try: 9 | sql = 'select city,count(*) from job_info where keyword="%s" group by city order by count(*) DESC limit 30' % keywordname 10 | current_app.cur.execute(sql) 11 | results = current_app.cur.fetchall() 12 | if not results: 13 | logger.warning('mysqldb has no %s' % keywordname) 14 | abort(404) 15 | job_category_counts = [int(x[1]) for x in results] 16 | keyword = [x[0].encode('utf-8') for x in results] 17 | keyword = json.dumps(keyword) 18 | 19 | sql = 'select salary,count(*) from job_info where keyword="%s" group by salary order by count(*) DESC limit 30' % keywordname 20 | current_app.cur.execute(sql) 21 | results = current_app.cur.fetchall() 22 | if not results: 23 | logger.warning('mysqldb has no %s' % keywordname) 24 | abort(404) 25 | salary_json = {key[0].encode('utf-8'):int(key[1]) for key in results} 26 | salary_json = json.dumps(salary_json) 27 | logger.info('success job.index url: %s ip: %s' % (request.url, request.remote_addr)) 28 | return render_template('job/job.html', job_category_counts=job_category_counts, keyword=keyword, keywordname=keywordname, salary_json=salary_json) 29 | except Exception as e: 30 | logger.warning('job.index error: %s url: %s ip: %s' % (e, request.url, request.remote_addr)) 31 | abort(404) -------------------------------------------------------------------------------- /app/main/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | main = Blueprint('main', __name__) 4 | 5 | from . import views -------------------------------------------------------------------------------- /app/main/views.py: -------------------------------------------------------------------------------- 1 | # *-* coding: utf-8 *-* 2 | from . import main 3 | from flask import url_for, render_template, current_app, abort, redirect, request 4 | import MySQLdb 5 | import json 6 | from app import logger 7 | from ..python.geo_info import geo_info 8 | 9 | 10 | @main.route('/') 11 | def index(): 12 | try: 13 | # 从数据库查询第一个图表需要的信息 14 | sql = '''select city,count(*) from job_info group by city order by count(*) DESC limit 30 ''' 15 | current_app.cur.execute(sql) 16 | results = current_app.cur.fetchall() 17 | if not results: 18 | logger.warning('main.index mysqldb errer has no city') 19 | abort(404) 20 | # 岗位数量 21 | job_category_counts = [int(x[1]) for x in results] 22 | # 不同城市名称 23 | city_category = [x[0].encode('utf-8') for x in results] 24 | city_category = json.dumps(city_category) 25 | # 从数据库查询第二个图表需要的信息 26 | sql = '''select keyword,count(*) from job_info group by keyword order by count(*) DESC limit 20''' 27 | current_app.cur.execute(sql) 28 | results = current_app.cur.fetchall() 29 | if not results: 30 | logger.warning('main.index mysqldb errer has no keyword') 31 | abort(404) 32 | # 职业饼形图需要json格式 33 | keyword_json = {key[0].encode('utf-8'):int(key[1]) for key in results} 34 | keyword_json = json.dumps(keyword_json) 35 | # 从数据库查询第三个图表需要的信息 36 | sql = 'select salary,count(*) from job_info group by salary order by count(*) DESC limit 30' 37 | current_app.cur.execute(sql) 38 | results = current_app.cur.fetchall() 39 | if not results: 40 | logger.warning('main.index mysqldb errer has no salary') 41 | abort(404) 42 | # 薪资饼形图需要json格式 43 | salary_json = {key[0].encode('utf-8'):int(key[1]) for key in results} 44 | salary_json = json.dumps(salary_json) 45 | logger.info('success main.index url: %s ip: %s' % (request.url, request.remote_addr)) 46 | # 全国分布图需要数据 47 | sql = ''' select city,count(*) from job_info group by city order by count(*) DESC limit 300''' 48 | current_app.cur.execute(sql) 49 | results = current_app.cur.fetchall() 50 | data = [dict(name=x[0].encode('utf-8'), value=int(x[1])) for x in results] 51 | data = json.dumps(data) 52 | geoCoordMap = json.dumps(geo_info) 53 | return render_template('main/index.html', job_category_counts=job_category_counts, city_category=city_category, keyword_json=keyword_json,salary_json=salary_json, data=data, geoCoordMap=geoCoordMap) 54 | except Exception as e: 55 | logger.warning('main.index error: %s url: %s ip: %s' % (e, request.url, request.remote_addr)) 56 | redirect(url_for('main.index')) 57 | 58 | # 在第一个请求发起时连接数据库 59 | @main.before_app_first_request 60 | def mysql_conn(): 61 | current_app.conn = MySQLdb.connect(host='localhost', user='root', passwd='qwer', charset='utf8', db='lagou') 62 | current_app.cur = current_app.conn.cursor() 63 | logger.debug('connect mysql success') 64 | -------------------------------------------------------------------------------- /app/models.py: -------------------------------------------------------------------------------- 1 | from . import db 2 | from peewee import * 3 | 4 | class City(db.model): 5 | """docstring for City""" 6 | class Meta: 7 | db_table = 'city' 8 | 9 | id = PrimaryKeyField() 10 | -------------------------------------------------------------------------------- /app/python/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | python = Blueprint('python', __name__) 4 | 5 | from . import views 6 | -------------------------------------------------------------------------------- /app/python/geo_info.py: -------------------------------------------------------------------------------- 1 | # *-* coding:utf-8 *-* 2 | 3 | # geo_info = { 4 | # '海门':[121.15,31.89], 5 | # '鄂尔多斯':[109.781327,39.608266], 6 | # '招远':[120.38,37.35], 7 | # '舟山':[122.207216,29.985295], 8 | # '齐齐哈尔':[123.97,47.33], 9 | # '盐城':[120.13,33.38], 10 | # '赤峰':[118.87,42.28], 11 | # '青岛':[120.33,36.07], 12 | # '乳山':[121.52,36.89], 13 | # '金昌':[102.188043,38.520089], 14 | # '泉州':[118.58,24.93], 15 | # '莱西':[120.53,36.86], 16 | # '日照':[119.46,35.42], 17 | # '胶南':[119.97,35.88], 18 | # '南通':[121.05,32.08], 19 | # '拉萨':[91.11,29.97], 20 | # '云浮':[112.02,22.93], 21 | # '梅州':[116.1,24.55], 22 | # '文登':[122.05,37.2], 23 | # '上海':[121.48,31.22], 24 | # '攀枝花':[101.718637,26.582347], 25 | # '威海':[122.1,37.5], 26 | # '承德':[117.93,40.97], 27 | # '厦门':[118.1,24.46], 28 | # '汕尾':[115.375279,22.786211], 29 | # '潮州':[116.63,23.68], 30 | # '丹东':[124.37,40.13], 31 | # '太仓':[121.1,31.45], 32 | # '曲靖':[103.79,25.51], 33 | # '烟台':[121.39,37.52], 34 | # '福州':[119.3,26.08], 35 | # '瓦房店':[121.979603,39.627114], 36 | # '即墨':[120.45,36.38], 37 | # '抚顺':[123.97,41.97], 38 | # '玉溪':[102.52,24.35], 39 | # '张家口':[114.87,40.82], 40 | # '阳泉':[113.57,37.85], 41 | # '莱州':[119.942327,37.177017], 42 | # '湖州':[120.1,30.86], 43 | # '汕头':[116.69,23.39], 44 | # '昆山':[120.95,31.39], 45 | # '宁波':[121.56,29.86], 46 | # '湛江':[110.359377,21.270708], 47 | # '揭阳':[116.35,23.55], 48 | # '荣成':[122.41,37.16], 49 | # '连云港':[119.16,34.59], 50 | # '葫芦岛':[120.836932,40.711052], 51 | # '常熟':[120.74,31.64], 52 | # '东莞':[113.75,23.04], 53 | # '河源':[114.68,23.73], 54 | # '淮安':[119.15,33.5], 55 | # '泰州':[119.9,32.49], 56 | # '南宁':[108.33,22.84], 57 | # '营口':[122.18,40.65], 58 | # '惠州':[114.4,23.09], 59 | # '江阴':[120.26,31.91], 60 | # '蓬莱':[120.75,37.8], 61 | # '韶关':[113.62,24.84], 62 | # '嘉峪关':[98.289152,39.77313], 63 | # '广州':[113.23,23.16], 64 | # '延安':[109.47,36.6], 65 | # '太原':[112.53,37.87], 66 | # '清远':[113.01,23.7], 67 | # '中山':[113.38,22.52], 68 | # '昆明':[102.73,25.04], 69 | # '寿光':[118.73,36.86], 70 | # '盘锦':[122.070714,41.119997], 71 | # '长治':[113.08,36.18], 72 | # '深圳':[114.07,22.62], 73 | # '珠海':[113.52,22.3], 74 | # '宿迁':[118.3,33.96], 75 | # '咸阳':[108.72,34.36], 76 | # '铜川':[109.11,35.09], 77 | # '平度':[119.97,36.77], 78 | # '佛山':[113.11,23.05], 79 | # '海口':[110.35,20.02], 80 | # '江门':[113.06,22.61], 81 | # '章丘':[117.53,36.72], 82 | # '肇庆':[112.44,23.05], 83 | # '大连':[121.62,38.92], 84 | # '临汾':[111.5,36.08], 85 | # '吴江':[120.63,31.16], 86 | # '石嘴山':[106.39,39.04], 87 | # '沈阳':[123.38,41.8], 88 | # '苏州':[120.62,31.32], 89 | # '茂名':[110.88,21.68], 90 | # '嘉兴':[120.76,30.77], 91 | # '长春':[125.35,43.88], 92 | # '胶州':[120.03336,36.264622], 93 | # '银川':[106.27,38.47], 94 | # '张家港':[120.555821,31.875428], 95 | # '三门峡':[111.19,34.76], 96 | # '锦州':[121.15,41.13], 97 | # '南昌':[115.89,28.68], 98 | # '柳州':[109.4,24.33], 99 | # '三亚':[109.511909,18.252847], 100 | # '自贡':[104.778442,29.33903], 101 | # '吉林':[126.57,43.87], 102 | # '阳江':[111.95,21.85], 103 | # '泸州':[105.39,28.91], 104 | # '西宁':[101.74,36.56], 105 | # '宜宾':[104.56,29.77], 106 | # '呼和浩特':[111.65,40.82], 107 | # '成都':[104.06,30.67], 108 | # '大同':[113.3,40.12], 109 | # '镇江':[119.44,32.2], 110 | # '桂林':[110.28,25.29], 111 | # '张家界':[110.479191,29.117096], 112 | # '宜兴':[119.82,31.36], 113 | # '北海':[109.12,21.49], 114 | # '西安':[108.95,34.27], 115 | # '金坛':[119.56,31.74], 116 | # '东营':[118.49,37.46], 117 | # '牡丹江':[129.58,44.6], 118 | # '遵义':[106.9,27.7], 119 | # '绍兴':[120.58,30.01], 120 | # '扬州':[119.42,32.39], 121 | # '常州':[119.95,31.79], 122 | # '潍坊':[119.1,36.62], 123 | # '重庆':[106.54,29.59], 124 | # '台州':[121.420757,28.656386], 125 | # '南京':[118.78,32.04], 126 | # '滨州':[118.03,37.36], 127 | # '贵阳':[106.71,26.57], 128 | # '无锡':[120.29,31.59], 129 | # '本溪':[123.73,41.3], 130 | # '克拉玛依':[84.77,45.59], 131 | # '渭南':[109.5,34.52], 132 | # '马鞍山':[118.48,31.56], 133 | # '宝鸡':[107.15,34.38], 134 | # '焦作':[113.21,35.24], 135 | # '句容':[119.16,31.95], 136 | # '北京':[116.46,39.92], 137 | # '徐州':[117.2,34.26], 138 | # '衡水':[115.72,37.72], 139 | # '包头':[110,40.58], 140 | # '绵阳':[104.73,31.48], 141 | # '乌鲁木齐':[87.68,43.77], 142 | # '枣庄':[117.57,34.86], 143 | # '杭州':[120.19,30.26], 144 | # '淄博':[118.05,36.78], 145 | # '鞍山':[122.85,41.12], 146 | # '溧阳':[119.48,31.43], 147 | # '库尔勒':[86.06,41.68], 148 | # '安阳':[114.35,36.1], 149 | # '开封':[114.35,34.79], 150 | # '济南':[117,36.65], 151 | # '德阳':[104.37,31.13], 152 | # '温州':[120.65,28.01], 153 | # '九江':[115.97,29.71], 154 | # '邯郸':[114.47,36.6], 155 | # '临安':[119.72,30.23], 156 | # '兰州':[103.73,36.03], 157 | # '沧州':[116.83,38.33], 158 | # '临沂':[118.35,35.05], 159 | # '南充':[106.110698,30.837793], 160 | # '天津':[117.2,39.13], 161 | # '富阳':[119.95,30.07], 162 | # '泰安':[117.13,36.18], 163 | # '诸暨':[120.23,29.71], 164 | # '郑州':[113.65,34.76], 165 | # '哈尔滨':[126.63,45.75], 166 | # '聊城':[115.97,36.45], 167 | # '芜湖':[118.38,31.33], 168 | # '唐山':[118.02,39.63], 169 | # '平顶山':[113.29,33.75], 170 | # '邢台':[114.48,37.05], 171 | # '德州':[116.29,37.45], 172 | # '济宁':[116.59,35.38], 173 | # '荆州':[112.239741,30.335165], 174 | # '宜昌':[111.3,30.7], 175 | # '义乌':[120.06,29.32], 176 | # '丽水':[119.92,28.45], 177 | # '洛阳':[112.44,34.7], 178 | # '秦皇岛':[119.57,39.95], 179 | # '株洲':[113.16,27.83], 180 | # '石家庄':[114.48,38.03], 181 | # '莱芜':[117.67,36.19], 182 | # '常德':[111.69,29.05], 183 | # '保定':[115.48,38.85], 184 | # '湘潭':[112.91,27.87], 185 | # '金华':[119.64,29.12], 186 | # '岳阳':[113.09,29.37], 187 | # '长沙':[113,28.21], 188 | # '衢州':[118.88,28.97], 189 | # '廊坊':[116.7,39.53], 190 | # '菏泽':[115.480656,35.23375], 191 | # '合肥':[117.27,31.86], 192 | # '武汉':[114.31,30.52], 193 | # '大庆':[125.03,46.58] 194 | # } 195 | 196 | geo_info = { 197 | '郴州': [113.014718,25.770510], 198 | '南充': [106.110698,30.837793], 199 | '朝阳': [120.450372,41.573734], 200 | '漳州': [117.647481,24.512949], 201 | '铁岭': [123.726166,42.223769], 202 | '台州': [121.420757,28.656386], 203 | '清远': [113.056031,23.681764], 204 | '咸阳': [108.708992,34.329605], 205 | '自贡': [104.778442,29.339030], 206 | '云浮': [112.044491,22.915094], 207 | '莆田': [119.007777,25.454085], 208 | '佛山': [113.121416,23.021548], 209 | '十堰': [110.797991,32.629397], 210 | '遵义': [106.927389,27.725654], 211 | '丽江': [100.227751,26.855047], 212 | '北京': [116.407526,39.904030], 213 | '锦州': [121.127004,41.095120], 214 | '塔城': [82.980317,46.745364], 215 | '拉萨': [91.140856,29.645554], 216 | '中山': [113.392782,22.517646], 217 | '西安': [108.940175,34.341568], 218 | '西宁': [101.778228,36.617144], 219 | '丽水': [119.922796,28.467630], 220 | '三亚': [109.511909,18.252847], 221 | '恩施': [109.488172,30.272156], 222 | '湛江': [110.359377,21.270708], 223 | '新余': [114.917347,27.817809], 224 | '莱芜': [117.676724,36.213814], 225 | '澳门特别行政区': [113.549088,22.198950], 226 | '绍兴': [120.580232,30.029753], 227 | '牡丹江': [129.618607,44.582962], 228 | '德州': [116.357465,37.434093], 229 | '湖州': [120.086823,30.894348], 230 | '三门峡': [111.200135,34.772494], 231 | '达州': [107.468023,31.209572], 232 | '湘潭': [112.944049,27.829738], 233 | '宜宾': [104.643215,28.751769], 234 | '安阳': [114.392393,36.097577], 235 | '渭南': [109.509786,34.499995], 236 | '银川': [106.230909,38.487194], 237 | '通辽': [122.243444,43.652890], 238 | '济宁': [116.587099,35.414921], 239 | '呼和浩特': [111.749181,40.842585], 240 | '玉林': [110.164756,22.636379], 241 | '鞍山': [122.994329,41.108647], 242 | '濮阳': [115.029216,35.761829], 243 | '漯河': [114.016539,33.581413], 244 | '金华': [119.647445,29.079059], 245 | '芜湖': [118.432941,31.352859], 246 | '乐山': [103.765572,29.552107], 247 | '阳江': [111.982232,21.857958], 248 | '台南': [121.353897,38.959846], 249 | '郑州': [113.625368,34.746600], 250 | '茂名': [110.925456,21.662999], 251 | '九江': [116.001930,29.705078], 252 | '抚州': [116.358182,27.949217], 253 | '邢台': [114.504844,37.070589], 254 | '廊坊': [116.683752,39.538047], 255 | '厦门': [118.089425,24.479834], 256 | '昆明': [102.832892,24.880095], 257 | '台北': [114.277985,30.597507], 258 | '宿州': [116.964356,33.646373], 259 | '深圳': [114.057868,22.543099], 260 | '宁波': [121.550357,29.874557], 261 | '南宁': [108.366543,22.817002], 262 | '临沂': [118.356448,35.104672], 263 | '泉州': [118.675676,24.874132], 264 | '焦作': [113.241823,35.215893], 265 | '大理': [100.267638,25.606486], 266 | '新乡': [113.926800,35.303004], 267 | '揭阳': [116.372831,23.549993], 268 | '邯郸': [114.538962,36.625657], 269 | '韶关': [113.597522,24.810403], 270 | '南阳': [112.528322,32.990833], 271 | '大同': [113.300129,40.076763], 272 | '运城': [111.007529,35.026412], 273 | '黄石': [115.038520,30.199652], 274 | '宿迁': [118.275198,33.963232], 275 | '合肥': [117.227239,31.820587], 276 | '龙岩': [117.017537,25.075123], 277 | '柳州': [109.415953,24.325502], 278 | '黄冈': [114.872316,30.453906], 279 | '东莞': [113.751765,23.020536], 280 | '孝感': [113.916903,30.924568], 281 | '株洲': [113.134003,27.827550], 282 | '成都': [104.066541,30.572269], 283 | '珠海': [113.576726,22.270715], 284 | '三明': [117.638678,26.263407], 285 | '延边': [129.508946,42.891254], 286 | '通化': [125.939697,41.728401], 287 | '安庆': [117.063755,30.543494], 288 | '桂林': [110.290195,25.273566], 289 | '岳阳': [113.128958,29.357104], 290 | '绥化': [126.968887,46.653845], 291 | '许昌': [113.852640,34.035506], 292 | '温州': [120.699367,27.994267], 293 | '上饶': [117.943436,28.454863], 294 | '上海': [121.473701,31.230416], 295 | '乌兰察布': [113.132585,40.994786], 296 | '鹤壁': [114.297273,35.747225], 297 | '香港特别行政区': [114.173355,22.320047], 298 | '巴中': [106.747478,31.867903], 299 | '张掖': [100.449818,38.925875], 300 | '武汉': [114.305393,30.593099], 301 | '池州': [117.491568,30.664800], 302 | '襄阳': [112.122415,32.008986], 303 | '连云港': [119.221611,34.596653], 304 | '佳木斯': [130.318917,46.799923], 305 | '开封': [114.307582,34.797239], 306 | '长治': [113.116255,36.195386], 307 | '承德': [117.962411,40.954071], 308 | '日照': [119.526888,35.416377], 309 | '泰州': [119.923116,32.455778], 310 | '晋中': [112.752695,37.687024], 311 | '哈尔滨': [126.534967,45.803775], 312 | '呼伦贝尔': [119.765745,49.211575], 313 | '内江': [105.058433,29.580229], 314 | '汕尾': [115.375279,22.786211], 315 | '淄博': [118.054927,36.813487], 316 | '潮州': [116.622604,23.656950], 317 | '周口': [114.696951,33.626149], 318 | '雅安': [103.013261,29.980537], 319 | '鄂尔多斯': [109.781327,39.608266], 320 | '天津': [117.200983,39.084158], 321 | '淮南': [116.999932,32.625478], 322 | '长沙': [112.938814,28.228209], 323 | '大庆': [125.103784,46.589310], 324 | '榆林': [109.734589,38.285390], 325 | '中卫': [105.196902,37.499973], 326 | '烟台': [121.447935,37.463822], 327 | '福州': [119.296494,26.074508], 328 | '南平': [118.177708,26.641769], 329 | '抚顺': [123.957208,41.880872], 330 | '泸州': [105.442261,28.871811], 331 | '济南': [117.120000,36.651216], 332 | '唐山': [118.180194,39.630867], 333 | '广安': [106.633212,30.455962], 334 | '蚌埠': [117.389719,32.916287], 335 | '镇江': [119.425836,32.187849], 336 | '兰州': [103.834304,36.061089], 337 | '石家庄': [114.514862,38.042309], 338 | '南昌': [115.858198,28.682892], 339 | '吉林': [125.325990,43.896536], 340 | '吉安': [114.992509,27.113443], 341 | '肇庆': [112.465091,23.047192], 342 | '信阳': [114.091023,32.146984], 343 | '德阳': [104.397894,31.126856], 344 | '铜仁': [109.189598,27.731515], 345 | '鄂州': [114.894843,30.391940], 346 | '泰安': [117.087614,36.200252], 347 | '南通': [120.894291,31.980172], 348 | '淮北': [116.798265,33.955845], 349 | '广州': [113.264435,23.129163], 350 | '太原': [112.548879,37.870590], 351 | '滨州': [117.970703,37.381990], 352 | '齐齐哈尔': [123.918186,47.354348], 353 | '大连': [121.614682,38.914003], 354 | '景德镇': [117.178420,29.268836], 355 | '徐州': [117.284124,34.205768], 356 | '贵阳': [106.630154,26.647661], 357 | '苏州': [120.585316,31.298886], 358 | '东营': [118.674767,37.434751], 359 | '常州': [119.973987,31.810689], 360 | '马鞍山': [118.506760,31.670452], 361 | '永州': [111.613445,26.420394], 362 | '衢州': [118.859457,28.970080], 363 | '惠州': [114.416196,23.111847], 364 | '亳州': [115.778676,33.844582], 365 | '萍乡': [113.854556,27.622768], 366 | '汕头': [116.681972,23.354091], 367 | '百色': [106.618201,23.902333], 368 | '六安': [116.521855,31.733700], 369 | '荆州': [112.239741,30.335165], 370 | '聊城': [115.985371,36.456704], 371 | '梅州': [116.122239,24.288615], 372 | '和田': [79.922211,37.114157], 373 | '扬州': [119.412966,32.394210], 374 | '海口': [110.198293,20.044002], 375 | '长春': [125.323544,43.817072], 376 | '潍坊': [119.161756,36.706774], 377 | '四平': [124.350398,43.166420], 378 | '娄底': [111.993497,27.700063], 379 | '眉山': [103.848538,30.075440], 380 | '沈阳': [123.431475,41.805698], 381 | '咸宁': [114.322492,29.841443], 382 | '嘉兴': [120.755486,30.746129], 383 | '绵阳': [104.679114,31.467450], 384 | '克拉玛依': [84.889207,45.579889], 385 | '乌鲁木齐': [87.616848,43.825592], 386 | '玉溪': [102.546543,24.352036], 387 | '宝鸡': [107.237974,34.361980], 388 | '赣州': [114.935030,25.831829], 389 | '威海': [122.120420,37.513068], 390 | '江门': [113.081901,22.578738], 391 | '荆门': [112.199265,31.035423], 392 | '海东': [102.104287,36.502040], 393 | '盐城': [120.163562,33.347383], 394 | '凉山彝族自治州': [102.267335,27.881611], 395 | '淮安': [119.015286,33.610354], 396 | '无锡': [120.311910,31.491170], 397 | '遂宁': [105.592898,30.532847], 398 | '辽阳': [123.236944,41.267244], 399 | '秦皇岛': [119.600493,39.935385], 400 | '河源': [114.700447,23.743538], 401 | '营口': [122.235418,40.667012], 402 | '保定': [115.464806,38.873891], 403 | '衡阳': [112.571997,26.893231], 404 | '重庆': [106.551557,29.563010], 405 | '宜昌': [111.286471,30.691967], 406 | '杭州': [120.155070,30.274085], 407 | '赤峰': [118.886856,42.257817], 408 | '滁州': [118.317107,32.301556], 409 | '张家口': [114.887543,40.824418], 410 | '沧州': [116.838835,38.304477], 411 | '怀化': [109.998488,27.554978], 412 | '南京': [118.796877,32.060255], 413 | '菏泽': [115.480656,35.233750], 414 | '舟山': [122.207216,29.985295], 415 | '青岛': [120.382640,36.067082], 416 | '北海': [109.119927,21.481254], 417 | '宜春': [114.416778,27.815619], 418 | '洛阳': [112.454040,34.619683], 419 | '包头': [109.840347,40.657449], 420 | '台中': [119.330345,26.085552] 421 | } 422 | -------------------------------------------------------------------------------- /app/python/views.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | from flask import render_template, current_app 3 | from . import python 4 | from .geo_info import geo_info 5 | import json 6 | 7 | 8 | @python.route('/') 9 | def index(): 10 | # 查询全国分布图所需数据 11 | sql = '''select city,count(*) from job_info where keyword='python' group by city order by count(*) DESC''' 12 | current_app.cur.execute(sql) 13 | results = current_app.cur.fetchall() 14 | data = [dict(name=x[0].encode('utf-8'), value=int(x[1])) for x in results] 15 | data = json.dumps(data) 16 | geoCoordMap = json.dumps(geo_info) 17 | # 查询岗位数量图数据 18 | job_category_counts = [int(x[1]) for x in results] 19 | city_category = [x[0].encode('utf-8') for x in results] 20 | city_category = json.dumps(city_category) 21 | # 查询工资情况分布图 22 | sql = 'select salary,count(*) from job_info where keyword="python" group by salary order by count(*) DESC limit 20' 23 | current_app.cur.execute(sql) 24 | results = current_app.cur.fetchall() 25 | salary_json = {key[0].encode('utf-8'): int(key[1]) for key in results} 26 | salary_json = json.dumps(salary_json) 27 | # 查询公司规模数据 28 | sql = 'select companySize,count(*) from job_info where keyword="python" group by companySize order by count(*) DESC limit 30' 29 | current_app.cur.execute(sql) 30 | results = current_app.cur.fetchall() 31 | companySize_json = {key[0].encode('utf-8'): int(key[1]) for key in results} 32 | companySize_json = json.dumps(companySize_json) 33 | # 查询工作年限数据 34 | sql = 'select workYear,count(*) from job_info where keyword="python" group by workYear order by count(*) DESC limit 30' 35 | current_app.cur.execute(sql) 36 | results = current_app.cur.fetchall() 37 | workYear_json = {key[0].encode('utf-8'): int(key[1]) for key in results} 38 | workYear_json = json.dumps(workYear_json) 39 | # 查询融资情况 40 | sql = 'select financeStage,count(*) from job_info where keyword="python" group by financeStage order by count(*) DESC limit 30' 41 | current_app.cur.execute(sql) 42 | results = current_app.cur.fetchall() 43 | financeStage_json = {key[0].encode('utf-8'): int(key[1]) for key in results} 44 | financeStage_json = json.dumps(financeStage_json) 45 | return render_template('python/index.html', data=data, geoCoordMap=geoCoordMap, job_category_counts=job_category_counts, city_category=city_category, salary_json=salary_json, companySize_json=companySize_json, workYear_json=workYear_json, financeStage_json=financeStage_json) 46 | -------------------------------------------------------------------------------- /app/salary/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | salary = Blueprint('salary', __name__) 4 | from . import views -------------------------------------------------------------------------------- /app/salary/views.py: -------------------------------------------------------------------------------- 1 | from . import salary 2 | from flask import url_for, render_template, current_app, abort, request 3 | import json 4 | from app import logger 5 | 6 | @salary.route('/') 7 | def index(salary_name): 8 | try: 9 | sql = 'select city,count(*) from job_info where salary="%s" group by city order by count(*) DESC limit 30' % salary_name 10 | current_app.cur.execute(sql) 11 | results = current_app.cur.fetchall() 12 | if not results: 13 | logger.warning('mysqldb has no %s' % salary_name) 14 | abort(404) 15 | job_category_counts = [int(x[1]) for x in results] 16 | keyword = [x[0].encode('utf-8') for x in results] 17 | keyword = json.dumps(keyword) 18 | 19 | sql = 'select keyword,count(*) from job_info where salary="%s" group by keyword order by count(*) DESC limit 30' % salary_name 20 | current_app.cur.execute(sql) 21 | results = current_app.cur.fetchall() 22 | if not results: 23 | logger.warning('mysqldb has no %s' % salary_name) 24 | abort(404) 25 | salary_json = {key[0].encode('utf-8'):int(key[1]) for key in results} 26 | salary_json = json.dumps(salary_json) 27 | logger.info('success salary.index url: %s ip: %s' % (request.url, request.remote_addr)) 28 | return render_template('salary/salary.html', job_category_counts=job_category_counts, keyword=keyword, salary_name=salary_name, salary_json=salary_json) 29 | except Exception as e: 30 | logger.warning('salary.index error: %s url: %s ip: %s' % (e, request.url, request.remote_addr)) 31 | abort(404) -------------------------------------------------------------------------------- /app/static/base.css: -------------------------------------------------------------------------------- 1 | /*响应式调整大小*/ 2 | @media screen and (max-width: 768px) { 3 | #container {width: 90%} 4 | div.container-fluid {width: 90%} 5 | } 6 | 7 | @media screen and (min-width: 768px) { 8 | #container {width: 70%} 9 | div.container-fluid {width: 70%} 10 | } 11 | 12 | 13 | 14 | #container { 15 | /*width: 70%;*/ 16 | margin: 0 auto; 17 | } 18 | 19 | 20 | /*设置导航栏宽度*/ 21 | div.container-fluid { 22 | /*width: 70%;*/ 23 | } 24 | 25 | /*设置footer的分割线*/ 26 | div#footer hr { 27 | border-width:1px; 28 | margin-bottom: 10px 29 | } 30 | 31 | /*设置footer字体、居中*/ 32 | div#footer_body { 33 | text-align: center; 34 | font-family: "Helvetica Neue", Helvetica, Microsoft Yahei, Hiragino Sans GB, WenQuanYi Micro Hei, sans-serif 35 | 36 | } 37 | 38 | /*清除p标签的margin*/ 39 | div#footer_body p { 40 | margin: 0 auto; 41 | } 42 | 43 | /*设置github栏目的margin*/ 44 | div#footer_body div#git { 45 | margin: 10px auto; 46 | } -------------------------------------------------------------------------------- /app/static/city/city.css: -------------------------------------------------------------------------------- 1 | /*设置小标题的样式*/ 2 | div#title { 3 | text-align: center; 4 | margin: 0px auto 20px auto; 5 | font-size: 40px; 6 | font-family: '微软雅黑' 7 | 8 | } 9 | 10 | 11 | /*分割线*/ 12 | hr { 13 | border-width:3px; 14 | } -------------------------------------------------------------------------------- /app/static/city/city.js: -------------------------------------------------------------------------------- 1 | // 生成柱状图 第一个参数y轴,第二个参数x轴, 第三个参数绑定元素 2 | function LoadImage_barChart(y, x ,bindto, onclickname){ 3 | var data_list = ['招聘岗位数量'].concat(y) 4 | var chart = c3.generate({ 5 | bindto:bindto, 6 | data : { 7 | columns:[ 8 | data_list, 9 | ], 10 | 11 | type: 'bar', 12 | 13 | labels:{ 14 | format:{ 15 | 招聘岗位数量:d3.format('') 16 | } 17 | }, 18 | onclick: function(d,i){ 19 | console.log(d) 20 | var cityIndex = d.x; 21 | var city = x[cityIndex]; 22 | window.open('/' + onclickname+ '/' + city) 23 | } 24 | }, 25 | 26 | legend: { 27 | show : false, 28 | position: 'right', 29 | }, 30 | bar: { 31 | width: { 32 | ratio: 0.5 33 | } 34 | }, 35 | 36 | axis:{ 37 | x:{ 38 | type:'category', 39 | categories :x 40 | } 41 | } 42 | }) 43 | } 44 | 45 | // 生成饼形图,第一个参数是json数据{'name':'calue', ...},第二个参数绑定元素,第三个参数是点击后参数变化 46 | function LoadImage_pieChart(json, bindto, onclickname){ 47 | var chart = c3.generate({ 48 | bindto: bindto, 49 | data :{ 50 | json:json, 51 | type: 'pie', 52 | onclick : function(d,i) { 53 | var keyword = d.id; 54 | window.open('/' + onclickname + '/' + keyword) 55 | } 56 | } 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /app/static/job/job.css: -------------------------------------------------------------------------------- 1 | /*设置小标题的样式*/ 2 | div#title { 3 | text-align: center; 4 | margin: 0px auto 20px auto; 5 | font-size: 40px; 6 | font-family: '微软雅黑' 7 | 8 | } 9 | 10 | 11 | /*分割线*/ 12 | hr { 13 | border-width:3px; 14 | } -------------------------------------------------------------------------------- /app/static/job/job.js: -------------------------------------------------------------------------------- 1 | // 生成柱状图 第一个参数y轴,第二个参数x轴, 第三个参数绑定元素 2 | function LoadImage_barChart(y, x ,bindto, onclickname){ 3 | var data_list = ['招聘岗位数量'].concat(y) 4 | var chart = c3.generate({ 5 | bindto:bindto, 6 | data : { 7 | columns:[ 8 | data_list, 9 | ], 10 | 11 | type: 'bar', 12 | 13 | labels:{ 14 | format:{ 15 | 招聘岗位数量:d3.format('') 16 | } 17 | }, 18 | onclick: function(d,i){ 19 | console.log(d) 20 | var cityIndex = d.x; 21 | var city = x[cityIndex]; 22 | window.open('/' + onclickname+ '/' + city) 23 | } 24 | }, 25 | 26 | legend: { 27 | show : false, 28 | position: 'right', 29 | }, 30 | bar: { 31 | width: { 32 | ratio: 0.5 33 | } 34 | }, 35 | 36 | axis:{ 37 | x:{ 38 | type:'category', 39 | categories :x 40 | } 41 | } 42 | }) 43 | } 44 | 45 | // 生成饼形图,第一个参数是json数据{'name':'calue', ...},第二个参数绑定元素,第三个参数是点击后参数变化 46 | function LoadImage_pieChart(json, bindto, onclickname){ 47 | var chart = c3.generate({ 48 | bindto: bindto, 49 | data :{ 50 | json:json, 51 | type: 'pie', 52 | onclick : function(d,i) { 53 | var keyword = d.id; 54 | window.open('/' + onclickname + '/' + keyword) 55 | } 56 | } 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /app/static/main/index.css: -------------------------------------------------------------------------------- 1 | /*设置字体*/ 2 | body { 3 | font-family: '微软雅黑细体' 4 | } 5 | 6 | /*设置绿色背景, 四角圆弧*/ 7 | div#tips { 8 | background-color: #AFE4AE; 9 | border-radius: 10px 10px 10px 10px; 10 | padding: 10px; 11 | margin: 5px 0 15px 0; 12 | } 13 | 14 | 15 | /*设置字体大小*/ 16 | div.comment { 17 | font-size: 17px; 18 | } 19 | 20 | /*设置评论栏的阴影、圆弧*/ 21 | div#result { 22 | box-shadow: 0px 0px 10px 0px #E8E1E1; 23 | /*padding: 10px 20px;*/ 24 | border-radius: 10px 10px 10px 10px; 25 | } 26 | 27 | /*设置上下标题间间隔*/ 28 | div#keyword_wrap { 29 | margin-top: 20px; 30 | } 31 | 32 | /*设置小标题的样式*/ 33 | div#title { 34 | text-align: center; 35 | margin: 0px auto 20px auto; 36 | font-size: 40px; 37 | font-family: '微软雅黑' 38 | 39 | } 40 | 41 | /*分割线*/ 42 | hr { 43 | border-width:3px; 44 | } 45 | 46 | /*设置前言容器蓝色背景*/ 47 | div#preface_wrap { 48 | background-color: #6f5499; 49 | height: 100%; 50 | } 51 | 52 | 53 | /*将导航条的下部分距离调整为0*/ 54 | .navbar { 55 | margin-bottom: 0px; 56 | } 57 | 58 | /*设置前言容器样式*/ 59 | div#preface { 60 | width: 67%; 61 | margin: 0 auto; 62 | color: #fff; 63 | font-family: '微软雅黑' 64 | } 65 | 66 | /*设置前言中大标题样式*/ 67 | div#preface h1 { 68 | margin-top: 0px; 69 | padding-top: 32px; 70 | font-size: 50px 71 | } 72 | 73 | /*设置前言正文样式*/ 74 | div#preface div#information { 75 | width: 90%; 76 | font-size: 20px; 77 | color: #CDBFF9; 78 | margin-top: 20px; 79 | margin-bottom: 20px; 80 | } 81 | 82 | /*设置前言栏中a标签样式*/ 83 | div#preface div#information a { 84 | color: #93A1E2; 85 | font-size: '微软雅黑'; 86 | text-decoration: underline; 87 | } 88 | 89 | /*设置前言栏中tips标签*/ 90 | div#preface div#information i { 91 | color: #93A1E2; 92 | font-size: 15px; 93 | } 94 | 95 | /*设置全国分布图大小*/ 96 | div#map_china_wrap { 97 | height: 500px; 98 | margin-top: -30px; 99 | 100 | } 101 | /*以下两个设置作用为保持栅格系统中列高相等*/ 102 | /*.row [class*="col-"]{ 103 | margin-bottom: -99999px; 104 | padding-bottom: 99999px; 105 | } 106 | 107 | .row { 108 | overflow: hidden; 109 | } 110 | -------------------------------------------------------------------------------- /app/static/main/index.js: -------------------------------------------------------------------------------- 1 | // 生成柱状图 第一个参数y轴,第二个参数x轴, 第三个参数绑定元素 2 | function LoadImage_barChart(y, x ,bindto, onclickname){ 3 | var data_list = ['招聘岗位数量'].concat(y) 4 | var chart = c3.generate({ 5 | bindto:bindto, 6 | data : { 7 | columns:[ 8 | data_list, 9 | ], 10 | 11 | type: 'bar', 12 | 13 | labels:{ 14 | format:{ 15 | 招聘岗位数量:d3.format('') 16 | } 17 | }, 18 | onclick: function(d,i){ 19 | console.log(d) 20 | var cityIndex = d.x; 21 | var city = x[cityIndex]; 22 | window.open('/' + onclickname+ '/' + city) 23 | } 24 | }, 25 | 26 | legend: { 27 | show : false, 28 | position: 'right', 29 | }, 30 | bar: { 31 | width: { 32 | ratio: 0.5 33 | } 34 | }, 35 | 36 | axis:{ 37 | x:{ 38 | type:'category', 39 | categories :x 40 | } 41 | } 42 | }) 43 | } 44 | 45 | 46 | // 生成饼形图,第一个参数是json数据{'name':'calue', ...},第二个参数绑定元素,第三个参数是点击后参数变化 47 | function LoadImage_pieChart(json, bindto, onclickname){ 48 | var chart = c3.generate({ 49 | bindto: bindto, 50 | data :{ 51 | json:json, 52 | type: 'pie', 53 | onclick : function(d,i) { 54 | var keyword = d.id; 55 | window.open('/' + onclickname + '/' + keyword) 56 | } 57 | } 58 | }) 59 | } 60 | // 生成全国地图 61 | function LoadMap(data, geoCoordMap) { 62 | var map_chart_wrap = document.getElementById("map_china_wrap"); 63 | var map_chart = echarts.init(map_chart_wrap); 64 | option = null; 65 | // 这个函数可以不要 66 | var convertData = function (data) { 67 | var res = []; 68 | for (var i = 0; i < data.length; i++) { 69 | var geoCoord = geoCoordMap[data[i].name]; 70 | if (geoCoord) { 71 | res.push({ 72 | name: data[i].name, 73 | value: geoCoord.concat(data[i].value) 74 | }); 75 | } 76 | } 77 | return res; 78 | }; 79 | 80 | option = { 81 | backgroundColor: '#fff', 82 | tooltip: { 83 | trigger: 'item', 84 | formatter: function (params) { 85 | return params.name + ' : ' + params.value[2]; 86 | } 87 | }, 88 | // 下面是映射关系 89 | visualMap: { 90 | // 自定义分段 91 | type: 'piecewise', 92 | pieces: [ 93 | {min: 1, max: 10}, 94 | {min: 10, max: 100}, 95 | {min: 100, max: 1000}, 96 | {min: 1000, max:10000}, 97 | {min: 10000}, 98 | // {value: 1, label: '1', color: '#98EFAA'}, 99 | ], 100 | inRange: { 101 | symbolSize: [6, 20], 102 | color: ['#8517A6', '#64117E', '#4B0D5E'] 103 | // color: ['purple'] 104 | }, 105 | outOfRange: { 106 | symbolSize: [5, 30], 107 | color: '#f3efff' 108 | } 109 | }, 110 | // 设置地理位置坐标系 111 | geo: { 112 | map: 'china', 113 | label: { 114 | emphasis: { 115 | show: true 116 | } 117 | }, 118 | itemStyle: { 119 | normal: { 120 | // areaColor: '#f3e1e1', 121 | areaColor: '#F3F3F3', 122 | borderColor: '#111' 123 | }, 124 | emphasis: { 125 | areaColor: '#a7bcd6' 126 | } 127 | } 128 | }, 129 | series: [ 130 | { 131 | name: 'city_China', 132 | type: 'scatter', 133 | coordinateSystem: 'geo', 134 | data: convertData(data), 135 | label: { 136 | normal: { 137 | show: false 138 | }, 139 | emphasis: { 140 | show: false 141 | } 142 | }, 143 | itemStyle: { 144 | emphasis: { 145 | borderColor: '#fff', 146 | borderWidth: 1 147 | } 148 | } 149 | }, 150 | { 151 | name: 'Top 5', 152 | type: 'effectScatter', 153 | coordinateSystem: 'geo', 154 | data: convertData(data.sort(function (a, b) { 155 | return b.value - a.value; 156 | }).slice(0, 6)), 157 | showEffectOn: 'render', 158 | rippleEffect: { 159 | brushType: 'stroke' 160 | }, 161 | hoverAnimation: true, 162 | label: { 163 | normal: { 164 | formatter: '{b}', 165 | position: 'right', 166 | show: true 167 | } 168 | }, 169 | itemStyle: { 170 | normal: { 171 | color: '#f4e925', 172 | shadowBlur: 10, 173 | shadowColor: '#333' 174 | } 175 | }, 176 | zlevel: 1, 177 | }, 178 | ] 179 | }; 180 | // 生成图表 181 | if (option && typeof option === "object") { 182 | map_chart.setOption(option, true); 183 | }; 184 | // 定义点击事件 185 | map_chart.on('click', function (params) { 186 | if (params.componentType == 'series') { 187 | window.open('/city/' + params.name); 188 | } 189 | // window.open('/city/' + params.name); 190 | }); 191 | } 192 | 193 | -------------------------------------------------------------------------------- /app/static/main/index_backup.js: -------------------------------------------------------------------------------- 1 | function LoadImage(){ 2 | var chart = c3.generate({ 3 | bindto : '#job_category', 4 | data: { 5 | columns: [ 6 | ['data1', 30, 200, 100, 400, 150], 7 | ], 8 | names :{ 9 | 'data1': '职业' 10 | }, 11 | type: 'bar', 12 | onclick: function (d, i) { window.open('') }, 13 | labels : { 14 | format : { 15 | data1 : d3.format(''), 16 | } 17 | } 18 | }, 19 | axis: { 20 | x :{ 21 | type : 'category', 22 | categories : ['python', 'c', 'java', 'c++', ',net'] 23 | } 24 | }, 25 | // size: { 26 | // height: 240, 27 | // width: 480 28 | // } 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /app/static/myicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioiogoo/internet_job_analysis/41e2d26e4e4541e805cc08d41c31271da504b9ab/app/static/myicon.ico -------------------------------------------------------------------------------- /app/static/python/python.css: -------------------------------------------------------------------------------- 1 | /*设置字体*/ 2 | body { 3 | font-family: '微软雅黑细体' 4 | } 5 | 6 | /*设置全国地图样式*/ 7 | div#map_chart_wrap { 8 | height: 500px; 9 | } 10 | 11 | 12 | /*设置小标题的样式*/ 13 | div#title { 14 | text-align: center; 15 | margin: 0px auto 20px auto; 16 | font-size: 40px; 17 | font-family: '微软雅黑' 18 | } 19 | 20 | /*设置评论栏样式*/ 21 | div#comment { 22 | box-shadow: 0px 0px 10px 0px #E8E1E1; 23 | padding: 10px 20px; 24 | border-radius: 10px 10px 10px 10px; 25 | margin-top: 30px; 26 | font-size: 17px; 27 | } 28 | -------------------------------------------------------------------------------- /app/static/python/python.js: -------------------------------------------------------------------------------- 1 | // 生成全国地图分布图 2 | function LoadMap(data, geoCoordMap) { 3 | var map_chart_wrap = document.getElementById("map_chart_wrap"); 4 | var map_chart = echarts.init(map_chart_wrap); 5 | option = null; 6 | // 这个函数可以不要 7 | var convertData = function (data) { 8 | var res = []; 9 | for (var i = 0; i < data.length; i++) { 10 | var geoCoord = geoCoordMap[data[i].name]; 11 | if (geoCoord) { 12 | res.push({ 13 | name: data[i].name, 14 | value: geoCoord.concat(data[i].value) 15 | }); 16 | } 17 | } 18 | return res; 19 | }; 20 | 21 | option = { 22 | backgroundColor: '#fff', 23 | tooltip: { 24 | trigger: 'item', 25 | formatter: function (params) { 26 | return params.name + ' : ' + params.value[2]; 27 | } 28 | }, 29 | // 下面是映射关系 30 | visualMap: { 31 | // 自定义分段 32 | type: 'piecewise', 33 | pieces: [ 34 | {min: 1, max: 10}, 35 | {min: 10, max: 100}, 36 | {min: 100, max: 1000}, 37 | // {value: 1, label: '1', color: '#98EFAA'}, 38 | ], 39 | inRange: { 40 | symbolSize: [6, 20], 41 | color: ['#8517A6', '#64117E', '#4B0D5E'] 42 | // color: ['purple'] 43 | }, 44 | outOfRange: { 45 | symbolSize: [5, 30], 46 | color: '#f3efff' 47 | } 48 | }, 49 | // 设置地理位置坐标系 50 | geo: { 51 | map: 'china', 52 | label: { 53 | emphasis: { 54 | show: true 55 | } 56 | }, 57 | itemStyle: { 58 | normal: { 59 | // areaColor: '#f3e1e1', 60 | areaColor: '#F3F3F3', 61 | borderColor: '#111' 62 | }, 63 | emphasis: { 64 | areaColor: '#a7bcd6' 65 | } 66 | } 67 | }, 68 | series: [ 69 | { 70 | name: 'city_China', 71 | type: 'scatter', 72 | coordinateSystem: 'geo', 73 | data: convertData(data), 74 | label: { 75 | normal: { 76 | show: false 77 | }, 78 | emphasis: { 79 | show: false 80 | } 81 | }, 82 | itemStyle: { 83 | emphasis: { 84 | borderColor: '#fff', 85 | borderWidth: 1 86 | } 87 | } 88 | }, 89 | { 90 | name: 'Top 5', 91 | type: 'effectScatter', 92 | coordinateSystem: 'geo', 93 | data: convertData(data.sort(function (a, b) { 94 | return b.value - a.value; 95 | }).slice(0, 6)), 96 | showEffectOn: 'render', 97 | rippleEffect: { 98 | brushType: 'stroke' 99 | }, 100 | hoverAnimation: true, 101 | label: { 102 | normal: { 103 | formatter: '{b}', 104 | position: 'right', 105 | show: true 106 | } 107 | }, 108 | itemStyle: { 109 | normal: { 110 | color: '#f4e925', 111 | shadowBlur: 10, 112 | shadowColor: '#333' 113 | } 114 | }, 115 | zlevel: 1, 116 | }, 117 | ] 118 | }; 119 | // 生成图表 120 | if (option && typeof option === "object") { 121 | map_chart.setOption(option, true); 122 | }; 123 | // 定义点击事件 124 | map_chart.on('click', function (params) { 125 | if (params.componentType == 'series') { 126 | window.open('/city/' + params.name); 127 | } 128 | // window.open('/city/' + params.name); 129 | }); 130 | } 131 | 132 | // 生成柱状图 第一个参数y轴,第二个参数x轴, 第三个参数绑定元素 133 | function LoadImage_barChart(y, x ,bindto, onclickname){ 134 | var data_list = ['招聘岗位数量'].concat(y) 135 | var chart = c3.generate({ 136 | bindto:bindto, 137 | data : { 138 | columns:[ 139 | data_list, 140 | ], 141 | 142 | type: 'bar', 143 | 144 | labels:{ 145 | format:{ 146 | 招聘岗位数量:d3.format('') 147 | } 148 | }, 149 | onclick: function(d,i){ 150 | console.log(d) 151 | var cityIndex = d.x; 152 | var city = x[cityIndex]; 153 | window.open('/' + onclickname+ '/' + city) 154 | } 155 | }, 156 | 157 | legend: { 158 | show : false, 159 | position: 'right', 160 | }, 161 | bar: { 162 | width: { 163 | ratio: 0.5 164 | } 165 | }, 166 | 167 | axis:{ 168 | x:{ 169 | type:'category', 170 | categories :x 171 | } 172 | } 173 | }) 174 | } 175 | 176 | 177 | // 生成饼形图,第一个参数是json数据{'name':'calue', ...},第二个参数绑定元素,第三个参数是点击后参数变化 178 | function LoadImage_pieChart(json, bindto, onclickname){ 179 | var chart = c3.generate({ 180 | bindto: bindto, 181 | data :{ 182 | json:json, 183 | type: 'pie', 184 | onclick : function(d,i) { 185 | var keyword = d.id; 186 | window.open('/' + onclickname + '/' + keyword) 187 | } 188 | } 189 | }) 190 | } 191 | -------------------------------------------------------------------------------- /app/static/salary/salary.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioiogoo/internet_job_analysis/41e2d26e4e4541e805cc08d41c31271da504b9ab/app/static/salary/salary.css -------------------------------------------------------------------------------- /app/static/salary/salary.js: -------------------------------------------------------------------------------- 1 | // 生成柱状图 第一个参数y轴,第二个参数x轴, 第三个参数绑定元素 2 | function LoadImage_barChart(y, x ,bindto, onclickname){ 3 | var data_list = ['招聘岗位数量'].concat(y) 4 | var chart = c3.generate({ 5 | bindto:bindto, 6 | data : { 7 | columns:[ 8 | data_list, 9 | ], 10 | 11 | type: 'bar', 12 | 13 | labels:{ 14 | format:{ 15 | 招聘岗位数量:d3.format('') 16 | } 17 | }, 18 | onclick: function(d,i){ 19 | console.log(d) 20 | var cityIndex = d.x; 21 | var city = x[cityIndex]; 22 | window.open('/' + onclickname+ '/' + city) 23 | } 24 | }, 25 | 26 | legend: { 27 | show : false, 28 | position: 'right', 29 | }, 30 | bar: { 31 | width: { 32 | ratio: 0.5 33 | } 34 | }, 35 | 36 | axis:{ 37 | x:{ 38 | type:'category', 39 | categories :x 40 | } 41 | } 42 | }) 43 | } 44 | 45 | 46 | // 生成饼形图,第一个参数是json数据{'name':'calue', ...},第二个参数绑定元素,第三个参数是点击后参数变化 47 | function LoadImage_pieChart(json, bindto, onclickname){ 48 | var chart = c3.generate({ 49 | bindto: bindto, 50 | data :{ 51 | json:json, 52 | type: 'pie', 53 | onclick : function(d,i) { 54 | var keyword = d.id; 55 | window.open('/' + onclickname + '/' + keyword) 56 | } 57 | } 58 | }) 59 | } 60 | 61 | -------------------------------------------------------------------------------- /app/templates/base.html: -------------------------------------------------------------------------------- 1 | {% extends "bootstrap/base.html" %} 2 | {%block head %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {%endblock%} 15 | {%block body%} 16 | {%block scripts%}{%endblock%} 17 | {{super()}} 18 | 19 | {% include "nav.html" %} 20 | 21 | {%block preface%} 22 | {%endblock%} 23 | 24 | 25 | {%block container%} 26 | {%endblock%} 27 | 28 | 29 | 30 | 49 | {%endblock%} 50 | -------------------------------------------------------------------------------- /app/templates/city/city.html: -------------------------------------------------------------------------------- 1 | {%extends 'base.html'%} 2 | {%block head%} 3 | {{super()}} 4 | {%block title%} 5 | {{cityname}}市 | 统计信息 6 | {%endblock%} 7 | 8 | 9 | {%endblock%} 10 | 11 | 12 | {%block container%} 13 | {{cityname}}市 各类岗位需求数量 14 | 15 | 16 | 17 | 18 | 19 | {{cityname}}市 薪资占比 20 | 21 | 22 | 23 | 24 | 25 | 34 | {%endblock%} -------------------------------------------------------------------------------- /app/templates/job/job.html: -------------------------------------------------------------------------------- 1 | {%extends 'base.html'%} 2 | {%block head%} 3 | {{super()}} 4 | {%block title%} 5 | {{keywordname}} | 统计信息 6 | {%endblock%} 7 | 8 | 9 | {%endblock%} 10 | 11 | {%block container%} 12 | {{keywordname}}需求量 城市排行 13 | 14 | 15 | 16 | 17 | 18 | {{keywordname}} 薪资占比 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | {%endblock%} -------------------------------------------------------------------------------- /app/templates/main/index.html: -------------------------------------------------------------------------------- 1 | {%extends 'base.html'%} 2 | {%block head%} 3 | {{super()}} 4 | {%block title%} 5 | 互联网行业招聘需求 统计与分析 6 | {%endblock%} 7 | 8 | 9 | 10 | 11 | {%endblock%} 12 | 13 | {%block preface%} 14 | 15 | 16 | 17 | 写在前面 18 | 19 | 本网站全部招聘数据来源于拉勾网在网上的公开信息,所有招聘信息的日期在2016年8月16日左右,由于招聘信息具有时效性,请各位仔细把握信息的准确性。本站只做数据统计与分析,不会用于商业目的。 20 | 21 | · 更多详情请看About this 22 | · 点击具体图表可以进入相应模块的具体分析 23 | 24 | 25 | 26 | 27 | 28 | {%endblock%} 29 | 30 | {%block container%} 31 | 全国互联网职位 需求分布图 32 | 33 | 34 | 35 | 36 | 37 | 不同城市 岗位需求排行 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | · 本次统计结果包含了北上广深等226个城市,按岗位需求量排序,取前30位。 48 | · PS.点击城市柱状图可以进入相应城市查询针对该城市的具体统计信息。 49 | 50 | 51 | 从统计结果可以看出来,目前岗位需求比较高的城市还是集中在北、上、广、深四大城市中,除此之外杭州也有很多高科技产业相关的公司,所以需求量也比较高。成都作为中西部的枢纽,这几年互联网企业也逐渐多了起来,求职的话,成都也是个不错的城市。 52 | 53 | 54 | 55 | 56 | 不同职业 岗位需求比例 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | · 本次统计结果包含了Java、Python、IOS、PHP等152个职位的结果,按职业需求量排序,取前30位。 65 | · PS.点击饼形图可以进入相应职业查询针对该职业的具体统计信息。 66 | 67 | 68 | 排名结果前五的职位有点意外,分别是Java、技术经理、PHP、web前端、网络工程师。 69 | 我大Python居然只排到了第十八位,我不服! 70 | 71 | 72 | 73 | 74 | 75 | 76 | 不同薪资 所占比例 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | · 本次统计结果包含了所有薪资种类,按薪资种类数量排序,取前30位。 85 | · PS.点击薪资饼形图可以进入相应薪资查询针对该薪资的具体统计信息。 86 | 87 | 88 | 排名结果前五的薪资种类平均水平都在10k以上,这还是说明了互联网行业薪资水平还是很不错的,我相信互联网的未来会更加值得期待! 89 | 90 | 91 | 92 | 93 | 111 | {%endblock%} 112 | 113 | -------------------------------------------------------------------------------- /app/templates/nav.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Toggle navigation 6 | 7 | 8 | 9 | 10 | 互联网行业招聘需求分析 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 首页 19 | 20 | 21 | 22 | 23 | 24 | Python报告 25 | 26 | 27 | 28 | 29 | 30 | About this(暂无) 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/templates/python/index.html: -------------------------------------------------------------------------------- 1 | {%extends 'base.html'%} 2 | {%block head%} 3 | {{super()}} 4 | {%block title%} 5 | Python 分析报告 6 | {%endblock%} 7 | 8 | 9 | 10 | 11 | {%endblock%} 12 | 13 | {%block container%} 14 | 15 | Python岗位 需求分布图 16 | 17 | 18 | 19 | 20 | 21 | Python需求量 城市排行 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | Python 薪资占比 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Python 公司规模 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 要求工作年限 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 融资情况 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 90 | 91 | {%endblock%} 92 | -------------------------------------------------------------------------------- /app/templates/salary/salary.html: -------------------------------------------------------------------------------- 1 | {%extends 'base.html'%} 2 | {%block head%} 3 | {{super()}} 4 | {%block title%} 5 | {{salary_name}} | 统计信息 6 | {%endblock%} 7 | 8 | 9 | {%endblock%} 10 | 11 | {%block container%} 12 | {{salary_name}}薪资数量 城市排行 13 | 14 | 15 | 16 | 17 | 18 | {{salary_name}} 职业占比 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | {%endblock%} -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | basedir = os.path.abspath(os.path.dirname(__file__)) 3 | 4 | 5 | class Config: 6 | """docstring for Config""" 7 | SECRET_KEY = 'asdafdsfasdasd' 8 | 9 | # @staticmethod 10 | # def init_app(app): 11 | # pass 12 | 13 | 14 | class Default_config(Config): 15 | """docstring for Default_config""" 16 | DEBUG = True 17 | DATABASE = { 18 | 'name': 'lagou', 19 | 'engine': 'peewee.MySQLDatabase', 20 | 'user': 'root', 21 | 'passwd': 'qwer' 22 | } 23 | 24 | 25 | config = { 26 | "DefultConfigName": Default_config 27 | } 28 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | from app import create_app 2 | from flask_script import Manager 3 | 4 | app = create_app() 5 | # manager = Manager(app) 6 | 7 | if __name__ == '__main__': 8 | app.run(debug=True, host='0.0.0.0') 9 | # manager.run() 10 | -------------------------------------------------------------------------------- /mobile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioiogoo/internet_job_analysis/41e2d26e4e4541e805cc08d41c31271da504b9ab/mobile.jpg -------------------------------------------------------------------------------- /pc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioiogoo/internet_job_analysis/41e2d26e4e4541e805cc08d41c31271da504b9ab/pc.jpg --------------------------------------------------------------------------------
48 | · PS.点击城市柱状图可以进入相应城市查询针对该城市的具体统计信息。 49 |