Coder Social home page Coder Social logo

fe-advanced-interview's Introduction

fe-advanced-interview's People

Contributors

huzhengen avatar ph87 avatar shengxinjing avatar woniuppp avatar zhengbangbo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fe-advanced-interview's Issues

论left-pad函数的实现

论left-pad的实现

这两天微博上看到左耳朵耗子吐槽了一下node社区的left-pad的代码,原po链接

01

我也思考了一下 怎么用实现一个left-pad比较合适,上图代码确实比较搓
leftpad功能,就是字符串前面拼指定字符到固定长度,比如
leftpad('hello',20,'1'),就要返回'111111111111111hello'

版本1 用数组+join


function leftpad(str, len, ch) {
    if (!ch && ch !== 0) ch = ' ';
    var len = len - str.length;
    return Array(len).join(ch) + str;
}

版本2 用一个带length属性的对象去实现join,免去了创建arr的步骤,性能应该回好点


function leftpad(str, len, ch) {
    if (!ch && ch !== 0) ch = ' ';
    var len = len - str.length;
    return Array.prototype.join.call({
        length:len
    },ch)+str;
}

如果把Array.prototype.join缓存到外部变量里,多次使用速度更快

var _join = Array.prototype.join
function leftpad(str, len, ch) {
    if (!ch && ch !== 0) ch = ' ';
    var len = len - str.length;
    return _join.call({
        length:len
    },ch)+str;
}


版本3 二分法

上面复杂度都是O(N)的,既然核心思路是把字符串重复n次,可以用二分法,比如把s,重复20次,拼在str前面,大概过程如下

total = ''
ch = 's'
20是偶数
    ch变成ss
    长度变成10
        10是偶数
            ch变成ssss
            长度变成5
                5是奇数
                    total += ch(total变成ssss)
                    ch变成ssssssss(8个)
                    长度变成2
                        2是偶数
                            ch继续变成(ssssssssssssssss)(16个s)
                            长度变成1
                                total= total+ch(4个加16个)
                                结束代码 拼接str 返回

代码如下

function leftpad(str, len, ch) {
    if (!ch && ch !== 0) ch = ' ';
    var len = len - str.length,
        total = ''
    while (true) {
        // 如果len是基数,total上就加一个ch
        if (len % 2 == 1) total += ch;
        if (len == 1) return total + str;;
        // 每次ch都变成chch
        ch += ch;
        //长度减半
        len = parseInt(len / 2);
    }

}

最后写完这些,看了耗子大神微博贴的代码,突然想起求余和除以二取整,可以用 按位与len&1 和右移len>>1代替,囧,还是代码写的太少,没想到

最后代码


function leftpad(str, len, ch) {
    if (!ch && ch !== 0) ch = ' ';
    var len = len - str.length,
        total = ''
    while (true) {
        // 如果len是基数,total上就加一个ch
        if (len & 1 == 1) total += ch;
        if (len == 1) return total + str;;
        // 每次ch都变成chch
        ch += ch;
        //长度减半
        len = len>>1;

    }

}


最后再优化一下循环


function leftpad(str, len, ch) {
    if (!ch && ch !== 0) ch = ' ';
    var len = len - str.length,
        total = ''
    while (len) {
        // 如果len是基数,total上就加一个ch
        (len & 1) && (total += ch)
        // 每次ch都变成chch
        ch += ch;
        //长度减半
        len = len >> 1;

    }
    return total + str

}


大家可以尝试用python实现一下(不要用自带的rjust),本文仅提供一个思路,很小的一个功能函数,可能还会有很多更好的优化和实现,欢迎大家多指教写代码过程中还是要多思考,共勉
喜欢请star

python10min系列之多线程下载器

今天群里看到有人问关于python多线程写文件的问题,联想到这是reboot的架构师班的入学题,我想了一下,感觉坑和考察的点还挺多,可以当成一个面试题来问,简单说一下我的想法和思路吧,涉及的代码和注释在github 跪求star

本文需要一定的python基础,希望大家对下面几个知识点有所了解

python文件处理,open write
简单了解http协议头信息
os,sys模块
threading模块多线程
requests模块发请求

题目既然是多线程下载,首先要解决的就是下载问题,为了方便测试,我们先不用QQ安装包这么大的,直接用pc大大英明神武又很内涵的头像举例,大概是这个样子(http://51reboot.com/blogimg/pc.jpg)

下载

python的requests模块很好的封装了http请求,我们选择用它来发送http的get请求,然后写入本地文件即可(关于requests和http,以及python处理文件的具体介绍,可以百度或者持续关注,后面我会写),思路既然清楚了,代码就呼之欲出了

# 简单粗暴的下载
import requests

res=requests.get('http://51reboot.com/blogimg/pc.jpg')
with open('pc.jpg','w') as f:
    f.write(res.content)

运行完上面的代码,文件夹下面多了个pc.jpg 就是你想要的图片了

上面代码功能太少了,注意 ,我们的要求是多线程下载,这种简单粗暴的下载完全不符合要求,所谓多线程,你可以理解为仓库里有很多很多袋奥利奥饼干,老板让我去都搬到公司来放好,而且要按照原顺序放好

上面的代码,大概就是我一个人去仓库,把所有奥利奥一次性拿回来,大概流程如下(图不清戳大)

我们如果要完成题目多线程的要求,首先就要把任务拆解,拆成几个子任务,子任务之间可以并行执行,并且执行结果可以汇总成最终结果

拆解任务

为了完成这个任务,我们首先要知道数据到底有多大,然后把数据分块去取就OK啦,我们要对http协议有一个很好的了解

  • 用head方法请求数据,返回只有http头信息,没有主题部分
    • 我们从头信息Content-length的值,知道资源的大小,比如有50字节
  • 比如我们要分四个线程,每个线程去取大概1/4即可
    • 50/4=12,所以前几个线程每人取12个字节,最后一个现成取剩下的即可
  • 每个线程取到相应的内容,文件中seek到相应的位置再写入即可
    • file.seek
  • 为了方便理解,一开始我们先用单线程的跑通 流程图大概如下(图不清戳大)

思路清晰了,代码也就呼之欲出了,我们先测试一下range头信息

http头信息中的Range信息,用于请求头中,指定第一个字节的位置和最后一个字节的位置,如1-12,如果省略第二个数,就认为取到最后,比如36-

# range测试代码
import requests
# http头信息,指定获取前15000个字节
headers={'Range':'Bytes=0-15000','Accept-Encoding':'*'}
res=requests.get('http://51reboot.com/blogimg/pc.jpg',headers=headers)

with open('pc.jpg','w') as f:
    f.write(res.content)

我们得到了头像的前15000个字节,如下图,目测range是对的

继续丰富我们的代码

  • 要先用requests.head方法去获取数据的长度
  • 确认开几个线程后,给每个线程确认要获取的数据区间,即Range字段的值
  • seek写文件
  • 功能比较复杂了,我们需要用面向对象来组织一下代码
  • 先写单线程,逐步优化
  • 代码呼之欲出了
import requests
# 下载器的类
class downloader:
    # 构造函数
    def __init__(self):
        # 要下载的数据连接
        self.url='http://51reboot.com/blogimg/pc.jpg'
        # 要开的线程数
        self.num=8
        # 存储文件的名字,从url最后面取
        self.name=self.url.split('/')[-1]
        # head方法去请求url
        r = requests.head(self.url)
        # headers中取出数据的长度
        self.total = int(r.headers['Content-Length'])
        print type('total is %s' % (self.total))
    def get_range(self):
        ranges=[]
        # 比如total是50,线程数是4个。offset就是12
        offset = int(self.total/self.num)
        for i in  range(self.num):
            if i==self.num-1:
                # 最后一个线程,不指定结束位置,取到最后
                ranges.append((i*offset,''))
            else:
                # 每个线程取得区间
                ranges.append((i*offset,(i+1)*offset))
        # range大概是[(0,12),(12,24),(25,36),(36,'')]
        return ranges
    def run(self):

        f = open(self.name,'w')
        for ran in self.get_range():
            # 拼出Range参数 获取分片数据
            r = requests.get(self.url,headers={'Range':'Bytes=%s-%s' % ran,'Accept-Encoding':'*'})
            # seek到相应位置
            f.seek(ran[0])
            # 写数据
            f.write(r.content)
        f.close()

if __name__=='__main__':
    down = downloader()
    down.run()

多线程

多线程和多进程是啥在这就不多说了,要说明白还得专门写个文章,大家知道threading模块是专门解决多线程的问题就OK了,大概的使用方法如下,更详细的请百度或者关注后续文章

  • threading.Thread创建线程,设置处理函数
  • start启动
  • setDaemon 设置守护进程
  • join设置线程等待
  • 代码如下
import requests
import threading

class downloader:
    def __init__(self):
        self.url='http://51reboot.com/blogimg/pc.jpg'
        self.num=8
        self.name=self.url.split('/')[-1]
        r = requests.head(self.url)
        self.total = int(r.headers['Content-Length'])
        print 'total is %s' % (self.total)
    def get_range(self):
        ranges=[]
        offset = int(self.total/self.num)
        for i in  range(self.num):
            if i==self.num-1:
                ranges.append((i*offset,''))
            else:
                ranges.append((i*offset,(i+1)*offset))
        return ranges
    def download(self,start,end):
        headers={'Range':'Bytes=%s-%s' % (start,end),'Accept-Encoding':'*'}
        res = requests.get(self.url,headers=headers)
        print '%s:%s download success'%(start,end)
        self.fd.seek(start)
        self.fd.write(res.content)
    def run(self):
        self.fd =  open(self.name,'w')
        thread_list = []
        n = 0
        for ran in self.get_range():
            start,end = ran
            print 'thread %d start:%s,end:%s'%(n,start,end)
            n+=1
            thread = threading.Thread(target=self.download,args=(start,end))
            thread.start()
            thread_list.append(thread)
        for i in thread_list:
            i.join()
        print 'download %s load success'%(self.name)
        self.fd.close()
if __name__=='__main__':
    down = downloader()
    down.run()

执行python downloader效果如下


total is 21520
thread 0 start:0,end:2690
thread 1 start:2690,end:5380
thread 2 start:5380,end:8070
thread 3 start:8070,end:10760
thread 4 start:10760,end:13450
thread 5 start:13450,end:16140
thread 6 start:16140,end:18830
thread 7 start:18830,end:
0:2690 is end
2690:5380 is end
13450:16140 is end
10760:13450 is end
5380:8070 is end
8070:10760 is end
18830: is end
16140:18830 is end
download pc.jpg load success

run函数做了修改,加了多线程的东西,加了一个download函数专门用来下载数据块,这俩函数详细解释如下

def download(self,start,end):
    #拼接Range字段,accept字段支持所有编码
    headers={'Range':'Bytes=%s-%s' % (start,end),'Accept-Encoding':'*'}
    res = requests.get(self.url,headers=headers)
    print '%s:%s download success'%(start,end)
    #seek到start位置
    self.fd.seek(start)
    self.fd.write(res.content)
def run(self):
    # 保存文件打开对象
    self.fd =  open(self.name,'w')
    thread_list = []
    #一个数字,用来标记打印每个线程
    n = 0
    for ran in self.get_range():
        start,end = ran
        #打印信息
        print 'thread %d start:%s,end:%s'%(n,start,end)
        n+=1
        #创建线程 传参,处理函数为download
        thread = threading.Thread(target=self.download,args=(start,end))
        #启动
        thread.start()
        thread_list.append(thread)
    for i in thread_list:
        # 设置等待
        i.join()
    print 'download %s load success'%(self.name)
    #关闭文件
    self.fd.close()

持续可以优化的点

  • 一个文件描述符多个进程用会出问题
    • 建议用os.dup复制文件描述符和os.fdopen来打开处理文件
  • 要下载的资源地址和线程数,应该做成命令行传进来的
    • 用sys.argv获取命令行参数
    • 支持python downloader.py url num这种写法
    • 参数数量不对或者格式不对时报错
  • 各种容错处理
  • 正所谓女人的迪奥,男人的奥利奥,这篇文章,你值得拥有

大概就是这样了,我也是正在学习python,文章代表我个人看法,有错误不可避免,欢迎大家指正,共同学习,本文完整代码在github,跪求大家star

Reboot体生成器

刚才看到一个issue,感觉挺逗的,决定Reboot其实也可以搞一个类似的试试,生成文案用,PC语录1,欢迎大家补充

def pc_say(name,bad):
    return '''
        不是很推荐%s这种比较奇葩的。因为%s的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。
    ''' %(name,bad)

print pc_say('mongo','写入')
print pc_say('js','兼容')

不是很推荐mongo这种比较奇葩的。因为写入的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。
不是很推荐js这种比较奇葩的。因为兼容的能力不是很好。虽然它后来又有一些改进方案来缓解这个问题,但注意,只是缓解。

用python 10min手写一个简易的实时内存监控系统

10min手写一个简易的内存监控系统:snail: :mushroom:

欢迎大家关注我的 微博知乎

本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客:snail: :mushroom:

项目地址,可以看到具体的代码,喜欢请加个星星

腾讯视频链接

录制中间网出问题了,重启了一下,所以有两部分 可以听到哥有磁性的声音

本文的目的在于,尽可能用简单的代码,让大家了解内存监控的原理
主题思路

  • 获取内存信息
  • 存储信息
  • 展现
  • 后续扩展
    • 加主机名,monitor部署在多台机器,不直接插数据库
    • 通过http请求的方式,一台机器起flask专门存数据monitor

思路图

第一步,我们需要获取内存信息

其实所有的监控项,包括内存数据,都是从文件中读取的,大家执行以下 cat /proc/meminfo就可以看到关于内存的信息,我们关注的是前四行,总内存,空闲内存,缓冲和缓存大小

计算内存占用量公式:

(总内存-空闲内存-缓冲-缓存)/1024Mb

代码呼之欲出 monitor.py

用with打开文件,可以自动关闭,比直接open优雅那么一丢丢

def getMem():
    with open('/proc/meminfo') as f:
        total = int(f.readline().split()[1])
        free = int(f.readline().split()[1])
        buffers = int(f.readline().split()[1])
        cache = int(f.readline().split()[1])
    mem_use = total-free-buffers-cache
    print mem_use/1024
while True:
    time.sleep(1)
    getMem()

执行文件 python monitor.py,每一秒打印一条内存信息

[woniu@teach memory]$ python mointor.py 
2920
2919
2919
2919
2919

我们可以写个很搓的测试代码,占用一点内存,看看数据会不会变
执行下面代码,能看到内存使用量明显多了几M

# test.py

s = 'akdsakjhdjkashdjkhasjkdhasjkdhkjashdaskjhfoopnnm,ioqouiew'*100000

for i in s:
    for j in s:
        s.count(j)                 

获取内存数据done!

第二步存储数据库

我们选用mysql

新建表格,我们需要两个字段,内存和时间 sql呼之欲出,简单粗暴

create memory(memory int,time int)

我们的 monitor.py就不能只打印内存信息了,要存储数据库啦,引入mysql模块,代码如下

import time
import MySQLdb as mysql

db = mysql.connect(user="reboot",passwd="reboot123",db="memory",host="localhost")
db.autocommit(True)
cur = db.cursor()

def getMem():
    with open('/proc/meminfo') as f:
        total = int(f.readline().split()[1])
        free = int(f.readline().split()[1])
        buffers = int(f.readline().split()[1])
        cache = int(f.readline().split()[1])
    mem_use = total-free-buffers-cache
    t = int(time.time())
    sql = 'insert into memory (memory,time) value (%s,%s)'%(mem_use/1024,t)
    cur.execute(sql)
    print mem_use/1024
    #print 'ok'
while True:
    time.sleep(1)
    getMem()

比之前的多了拼接sql和执行的步骤,具体过程见视频,大家到数据库里执行一下下面的sql,就能看到我们辛辛苦苦获取的内存数据啦

    select * from memory

我们的数据库里数据越来越多,怎么展示呢

我们需要flask
我们看下文件结构

.
├── flask_web.py web后端代码
├── mointor.py 监控数据获取
├── static 静态文件,第三方图表库
│   ├── exporting.js
│   ├── highstock.js
│   └── jquery.js
├── templates
│   └── index.html 展示前端页面
└── test.py 占用内存的测试代码


flask_web就是我们的web服务代码,template下面的html,就是前端展示的文件,static下面是第三方库

flask_web的代码如下

  • 提供两个路由
    • 根目录渲染文件index.html
    • /data路由去数据库插数据,返回json,供画图使用
from flask import Flask,render_template,request
import MySQLdb as mysql

con = mysql.connect(user='reboot',passwd='reboot123',host='localhost',db='memory')

con.autocommit(True)
cur = con.cursor()
app = Flask(__name__)
import json

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/data')
def data():
    sql = 'select * from memory'
    cur.execute(sql)
    arr = []
    for i in cur.fetchall():
        arr.append([i[1]*1000,i[0]])
    return json.dumps(arr)

if __name__=='__main__':
    app.run(host='0.0.0.0',port=9092,debug=True)

前端index.html
highstock的demo页面,copy过来,具体过程见视频

<html>
<head>
<title>51reboot</title>
</head>

<body>
hello world

<div id="container" style="height: 400px; min-width: 310px"></div>

<script src='/static/jquery.js'></script>
<script src='/static/highstock.js'></script>
<script src='/static/exporting.js'></script>
<script>
$(function () {
    // 使用当前时区,否则东八区会差八个小时
    Highcharts.setOptions({
        global: {
            useUTC: false
        }
    });
    $.getJSON('/data', function (data) {
        // Create the chart
        $('#container').highcharts('StockChart', {
            rangeSelector : {
                selected : 1
            },
            title : {
                text : '内存数据'
            },
            series : [{
                name : '本机内存',
                data : data,
                tooltip: {
                    valueDecimals: 2
                }
            }]
        });
    });
});
</script>
</body>
</html>

具体观察数据结构的过程,见视频和demo链接,我们做的 就是把数据库里的数据,拼接成前端画图需要的数据,展现出来

这时候前端就能看到图表啦

我们并不仅限于此,如果想实时的看到内存,应该怎么搞呢

  • 查询数据时候增加一个时间戳当限制条件,再次查询时,只返回两次查询之间的增量数据
  • 前端动态添加增量结点数据到图表中
  • 代码呼之欲出

python

tmp_time = 0

@app.route('/data')
def data():
    global tmp_time
    if tmp_time>0:
        sql = 'select * from memory where time>%s' % (tmp_time/1000)
    else:
        sql = 'select * from memory'
    cur.execute(sql)
    arr = []
    for i in cur.fetchall():
        arr.append([i[1]*1000,i[0]])
    if len(arr)>0:
        tmp_time = arr[-1][0]
    return json.dumps(arr)

前端,3秒查一次增量数据

    $.getJSON('/data', function (data) {

        // Create the chart
        $('#container').highcharts('StockChart', {
        chart:{
        events:{
            load:function(){
                var series = this.series[0]
                setInterval(function(){
                $.getJSON('/data',function(res){
                    $.each(res,function(i,v){
                        series.addPoint(v)
                    })
                })
                },3000)
            }
        }
        },
            rangeSelector : {
                selected : 1
            },
            title : {
                text : 'AAPL Stock Price'
            },
            series : [{
                name : 'AAPL',
                data : data,
                tooltip: {
                    valueDecimals: 2
                }
            }]
        });
    });

done!两个文件都搞定,double kill!
效果

代码没有特别注意细节,希望大家喜欢

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支持我,谢谢

python 10min系列之写配置文件生成增删改查系统

woniu-cmdb

🐌 奇技淫巧--写配置文件生成增删改查系统🍄

项目地址
线上demo,此页面都是一个配置文件自动生成的

使用视频

运维人员都不喜欢搞CMDB,因为有很多前端的内容,但CMDB却在运维圈占有重要的地位,开发CMDB就是各种增删改查,之后我有个想法,做一个写配置文件就自动生成页面的CMDB, 请支持我的woniu-cmdb,喜欢请star

写好配置文件,自从生成页增删改查面不是梦

此项目不仅限于cmdb,各种管理系统,都可以用此项目配置,改成学生老师啥的,就变成了学校内部的mis系统,我会一直维护这个项目,大家有新需求请提issue

效果图(我们只写左边的配置,右边的是自动生成的)

简单配置,生成页面

命令只有两个

python woniu-build.py init # 初始化数据库+根据配置生成文件

python woniu-build.py 仅根绝配置生成文件 

使用指南

  1. 下载该项目到本地
  2. config.py是我们唯一要修改的文件
  3. 修改config.py里的db_config变量,配置数据库的host,用户名,密码和要操作的数据库
db_config = {
    'host':'localhost',
    'user':'root',
    'passwd':"",
    'db':'cmdb'
}
  1. 修改config.py的page_config变量,此变量是设置具体的页面变量,先做一个简单的配置

page_config = {
    # menu是一个list,包含所有的页面信息
    "menu":[{
        //页面的名字,和数据库表一致
        "name": 'user',
        // 显示的页面标题
        "title": '用户管理',

        # 页面里具体的字段,如果有两个字段,配置两个即可,包含name和title
        "data": [{
            "name": 'username',
            "title": '用户名'
        },{
            "name":'password',
            "title":'密码'
        }]
    }}]
}

  1. 执行 python woniu-build.py 处理文件,启动flask_web.py,浏览器访问http://localhost:9092/
  2. 默认有一个用户,账号和密码都是51reboot

字段详解

  1. page_config配置
    menu:下面具体介绍,页面具体的字段
    favicon:页面标签的小logo 默认用reboot的
    title:页面标签的标题,默认是woniu-cmdb
    brand_name:项目左上角显示文字,默认是woniu-cmdb

  1. menu配置详解

{
    name:名字和数据库表名一直
    titile:中文
    modal_detail:是否用模态窗展示详情(有隐藏字段没展示)
    具体字段数据
    data:[
        {
            name:
            title:
            type:类型,默认input text
            value:select直接从value里渲染,不发ajax和preload,如果没有type,就是input里的value属性

            select_type:获取数据的action_type的值,和对应的name字段一致
            toname:preload数据里,完成id到name得转换显示,select默认true
             hide:默认false,true的话隐藏此字段

            option_val list的显示字段 默认id
            option_name list的显示字段 默认name

        }
    ]
}

todolist:

  • 登录权限+页面权限
  • 更丰富的前端组件,现在只支持输入,日期和下啦框
  • 大家有需求请提issues 我会持续维护这个项目,

依赖

本项目python依赖flask和mysqldb模块,直接pip安装一下即可

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码

dev

  • Be awesome
  • Prepare dinner
    • Research recipe
    • Buy ingredients
    • Cook recipe
  • Sleep

python 10 min系列二之日志可视化的进阶

日志可视化 🍄

本文需要有一定的python和前端基础,如果没基础的,请关注我后续的基础教程系列博客
本文所有的demo,都是浏览器下展示的

同步发布在博客

项目地址,跪求右上角star ⭐ 🌟 🌠
基于python,前端基于echarts,力求用简单的代码说明原理

提纲

  • access日志按照什么维度展示数据
  • 和web结合,更好的体验,只需记住URL
  • 分页排序和搜索,表格展示体验的提升
  • 一图胜万言,汇总信息更友好的展示
  • 逼格满满,让IP信息在地图上展示
  • 后续展望--机房网络流量可视化,机器、机柜、机房3D展示

背景

老板要看日志数据汇总

  • 本文重点:如何做可视化
  • 目标:如何用友好的方式去展现沉闷繁冗的数据
  • 下个月就发年终奖!!!
  • 为了说明可视化的方式,用一个简单的log举例子

原材料


  • 一个标准的access_log日志 大概2W行
  • 老板想要这个日志的分析结果,每个url,ip,status分别访问多少次,把前几名统计出来看看
  • 分析出统计数据 展现结果

很普通的日志,大概长这样

为了方便展示,切割了一下,大概2W行

61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
61.159.140.123 - - [23/Aug/2014:00:01:42 +0800] "GET /favicon.ico HTTP/1.1" 404 \ "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36 LBBROWSER" "-"
66.249.64.5 - - [23/Aug/2014:00:02:16 +0800] "GET /data/uploads/2013/0519/09/small_51982ba18e012.jpg HTTP/1.1" 200 \ "-" "Googlebot-Image/1.0" "-"
66.249.64.10 - - [23/Aug/2014:00:02:54 +0800] "GET /data/uploads/2013/0319/08/middle_5147b116e93b4.jpg HTTP/1.1" 200 \ "-" "Googlebot-Image/1.0" "-"

目标

  • 初级展现
  • 友好交互
  • 饼图汇总
  • 更进一步
  • 后续展望

级别1


  • 数据处理,命令行展现
  • 打开文件,处理完数据后排序
  • 打印前10

talk is cheap, show me the money code!

# coding=utf-8
f = open('www_access_20140823.log')
res = {}
for l in f:
    arr = l.split(' ')
    # 获取ip url 和status
    ip = arr[0]
    url = arr[6]
    status = arr[8]
    # ip url 和status当key,每次统计+1
    res[(ip,url,status)] = res.get((ip,url,status),0)+1
# 生成一个临时的list
res_list = [(k[0],k[1],k[2],v) for k,v in res.items()]
# 按照统计数量排序,打印前10
for k in sorted(res_list,key=lambda x:x[3],reverse=True)[:10]:
    print k

处理结果

('222.86.153.12', '/images/cursor_minify.cur', '404', 60)
('222.86.153.12', '/images/cursor_zoom.cur', '404', 32)
('58.253.6.133', '/images/cursor_minify.cur', '404', 32)
('111.85.34.165', '/%3Ca%20href=', '404', 28)
('58.253.6.133', '/images/cursor_zoom.cur', '404', 27)
('218.29.111.117', '/images/cursor_zoom.cur', '404', 27)
('218.29.111.117', '/images/cursor_minify.cur', '404', 26)
('117.63.146.40', '/public/js/common.js?20110824', '200', 19)
('117.63.146.40', '/favicon.ico', '404', 18)
('117.63.146.40', '/public/js/weibo.js?20110824', '200', 16)

任务完成

  • 下一步粘到邮件里,或者生成一个csv文件发出去
  • 然而这是一个看脸的社会,运维也逃脱不了这个魔咒

级别2

浏览器端展现

生成list之后,拼接sql,存入数据库

talk is cheap, show me the money code!

import MySQLdb as mysql
con = mysql.connect(user='root',\
                    passwd='',\
                    db='log',\
                    host='localhost')
con.autocommit(True)
cur = con.cursor()
# 处理文件省略
for s in res_list:
    sql = 'insert log values ("%s","%s",%s,%s)' % s
    try:
        # 入库
        cur.execute(sql)
    except Exception, e:
        pass

前端展现

读库 展现页面

talk is cheap, show me the money code!

from flask import Flask,request,render_template
app = Flask(__name__)
import MySQLdb as mysql
con = mysql.connect(user='xx',\
                    passwd='xx',\
                    db='xx')
cur = con.cursor()
@app.route('/')
def index():
    table = '<table border="1">'
    cur.execute('select * from log order by value desc limit 20; ')
    for c in cur.fetchall():
        table += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>'%c
    table +='</table>'
    return table

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=9092)

给老板一个url即可,老板想看随时能看

表格

但是老板表示有点丑

老板

完整的思路

表格

前端展现上做一些优化

  • 分页
  • 表格排序
  • 搜索
  • 控制每页显示数量
  • 数据多了之后,前端交互和后端数据的接口配合

不动戳大

表格

我们的console页面,提供几个汇总信息,那就更好啦

比如根据http的status来个汇总

老板

难不倒我

一句sql搞定

select status,sum(value) from log group by status
+--------+------------+
| status | sum(value) |
+--------+------------+
|    200 |      15529 |
|    206 |          6 |
|    301 |          2 |
|    304 |       3549 |
|    403 |          1 |
|    404 |        847 |
+--------+------------+
6 rows in set (0.02 sec)

汇总信息可视化

根据https状态汇总

其他功能

  • 图例开关
  • 图表转换
  • 数据视图
  • 直接导出图片

可视化并不仅限于此

上面只是举得小栗子

如果你对时间更感兴趣,我们的log里也是有时间信息的,可以像下面这样

统计量,时间轴拖动,保存图片,etc

更进一步

如何让日志数据更加一目了然,让老板觉得你很有逼格呢

IP都是有地址位置的,定位每个ip的位置,画个地图出来汇总

经纬度坐标系统

  • 地球坐标(WGS84)
    • 国际标准,从 GPS 设备中取出的数据的坐标系
    • 国际地图提供商使用的坐标系
  • 火星坐标(GCJ-02)
    • **标准,从国行移动设备中定位获取的坐标数据使用这个坐标系
  • 百度坐标(BD-09)
    • 百度标准,百度 SDK,百度地图,Geocoding 使用

应用场景

  • WGS84坐标系:
    • 国际标准,谷歌国外地图、osm地图等国外的地图一般都是这个
  • 火星坐标系:
    • iOS 地图
    • Gogole地图
    • 搜搜、阿里云、高德地图
  • 百度坐标系:
    • 当然只有百度地图

地图是需要经纬度的,用第三方的ip库转换一下

http://developer.baidu.com/map/index.php?title=webapi/ip-api

talk is cheap, show me the money code!

import urllib2
import json
key = 'q5mTrTGzCSVq5QmGpI9y18Bo'
ipurl = 'http://api.map.baidu.com/location/ip?ak='+key+'&coor=bd09ll&ip='
sqlarr = []
def getGeo(ip):
    try:
        u = urllib2.urlopen(ipurl+ip)
        page = json.load(u)
        if 'content' in page:
            point = page['content'].get('point')
            print 'ip %s has geoX %s and geoY %s' % (ip,point['x'],point['y']) 
    except:
        print 'error'
getGeo('202.198.16.3')
# ip 202.198.16.3 has geoX 125.31364243 and geoY 43.89833761

就想玩网游时候,坐标可以定位一个人,经纬度可以再地图上定位一个点,画图展现

刚才那个图仅关注区域,进阶一下,还要关注访问量

可以根据value筛选

进阶:多台机器的日志

  • 获取每个机器的hostname和ip,和日志数据一起存在数据库里
  • 一个表存日志,带上一个机器的id
  • 机器的id=>ip和经纬度
  • 最终统计访问量

后续扩展

  • 日志数据
  • 前端展现场景

怎么实践

  • 这次分享的主题关注与可视化
  • 我们用了一个很小的静态日志,目的是说明可视化的思路
  • 实际工作中日志数据应该怎么处理

elk

Logstash+ElasticSearch+Kibana4

  • logstash
    • 日志进行收集、分析,并将其存储供使用
  • ElasticSearch
    • 开源分布式搜索引擎,
  • Kibana4
    • 日志分析友好的 Web 界面
  • 其他
    • Kafka scribe等

常见日志处理架构

  • ELK
  • logstash+Hadoop
  • scribe+hadoop
  • 线上数据->Flume->Kafka->Hdfs->Map/Reduce
  • 线上数据->flume->kafka->storm
  • 在上面的基础上定制化二次开发,比如MR平台上写代码,我们的代码就可以直接拿来用
  • 日志的数据处理架构详情,请见下回分解

前端展现场景

  • 展现逼格更高一些,数据一样,效果更好
  • 运维人员权限树
  • 流量图
  • 年终数据统计
  • 区域点击统计图
  • 3D机房

大家具体需要哪个,可以继续扩展,给大家展现几个假数据的demo

高逼格饼图展示状态汇总(假数据,可以替换为http_status)

人员权限树(假数据,可以作为运维人员权限展示)

流量图(假数据,可以作为机房之间,或者网卡的流量)

日志统计汇总(假数据,可以用来展示年日志数据汇总)

区域数据汇总饼图展示(假数据,可选择省份,生成饼图)

3D展示机房(网上盗图,后续会做一个类似的开源)

json生成,实时查看机器状态,点击时间

3D展示不止于此 有图有XX

谢谢!

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支持我,谢谢

python10min系列之程序员的浪漫,记录去过的足迹

Footprint--:snail::mushroom:记录去过的足迹

程序员的浪漫,我女朋友蘑菇喜欢旅游,于是我做了这个,记录2015一起去过的地方,祝她圣诞快乐

如果觉得对你有帮助,求个star 项目地址

视频教程

开发时间很短,如有bug 请轻喷,有需求提issue,我会维护这个项目
├── README.md 说明文档
├── javascript 网页版本
│   └── index.html
└── python python版本
    ├── build.py 编译生成文件
    └── config.py 你需要修改的配置文件

写配置文件,生成去过的足迹,哄妹子必备

本文集中了我的前两篇文章日志可视化配置文件生成cmdb,最终做出来的,分为网页和python两个版本,喜欢的跪求star

网页版

  1. 戳地址 基于echarts实现
  2. 左边输入标题,副标题
  3. 下面的输入框输入行程,每个输入框是一个行程,地点之间用空格间隔,可以点击添加行程按钮新增输入框
  4. 点击右侧的查看效果,右侧的图就会变成你配置的效果
  5. 新建一个html文件,点击导出代码,把弹出来的一坨代码粘进去,bingo
  6. 谢谢大家,开发时间很短,如有bug 请轻喷


不动戳大

python版本

  • 下载本项目,进入到python目录下,有两个文件,config是你需要修改的2.
  • 项目依赖于requests模块 需要pip安装
config={
    'title':'去过的地方',
    'subtitle':'北京 昆明 西北 呼和浩特',
    'foot':[
        '北京 昆明 丽江 香格里拉 丽江 昆明 北京',
        '霍营地铁站 布达拉宫',
        '北京 北戴河 北京',
        '北京 兰州 敦煌 张掖 祁连 西宁 青海湖 茶卡盐湖 西宁 银川 呼和浩特 北京'
    ]
}

  • 修改上面这个配置里的title(标题),subtitle(副标题)和foot(行程)
  • foot是一个数组,每个元素是一个行程,目的地(景点)之间用空格分开
  • 执行 python build.py ,会生成一个footprint.html,大功告成,浏览器打开看效果吧
  • 谢谢大家,开发时间很短,如有bug 请轻喷

彩色版本和定制区域

  • 时间仓促,网页版还没加上
  • config加一个color变量,就会把足迹线变成彩色,如下
config={
    'title':'去过的地方',
    'subtitle':'北京 昆明 西北 呼和浩特',
    'color':True,
    'foot':[
        '北京 昆明 丽江 香格里拉 丽江 昆明 北京',
        '霍营地铁站 布达拉宫',
        '北京 北戴河 北京',
        '北京 兰州 敦煌 张掖 祁连 西宁 青海湖 茶卡盐湖 西宁 银川 呼和浩特 北京'
    ]
}

  • 如果你只在北京内部玩,或者定制一个北京旅游计划,可以加一个region字段,如下
config={
    'title':'北京去过的地方',
    'subtitle':'走啊走',
    'color':True,
    'region':'北京',
    'foot':[
        '北京交通大学 霍营地铁站 古北水镇',
        '北京交通大学 八达岭 北京交通大学',
        '北京交通大学 妙峰山 潭柘寺'

    ]
}

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码(10元以下),凑钱买圣诞礼物 ~~~

github桌面软件使用教程

github桌面软件使用教程

首先 要先安装 桌面版官网,或者百度搜github windows下载即可

可以再github网站上直接点击,把代码添加的桌面软件中

也可以再左上角添加项目,比如actual_07_homework,如果没有,说明还没有权限,联系我开通



项目初始化之后,写代码,修改文件之后,窗口内会看到修改的内容

输入提交的信息,点击commit

commit完之后,代码改动保存在了本地,点击一下右上角的sync 即可同步到github上,如果别人对代码有修改,你点一下sync,就可以把最新的代码同步到本地

最后

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支持我,谢谢

以后都用issue来写博客啦

发现还是github的issue好,label用来分类,评论还支持markdown

博客范围:web开发 算法 运维开发

写了两篇觉得还不错,希望能坚持下去

python10min系列之面试题解析:python实现tail -f功能

python10min系列之面试题解析:python实现tail -f功能

这篇文章最初是因为reboot的群里,有人去面试,笔试题有这个题,不知道怎么做,什么思路,就发群里大家讨论

我想了一下,简单说一下我的想法吧,当然,也有很好用的pyinotify模块专门监听文件变化,不过我更想介绍的,是解决的思路,毕竟作为面试官,还是想看到一下解决问题的思路,而且我觉得这一题的难点不在于监控文件增量,而在于怎么打印最后面10行

希望大家读这篇文章前,对python基础、处理文件和常用模块有一个简单的了解,知道下面几个名词是啥

open('a.txt')
file.seek
file.tell
time.sleep()

下面思路限于我个人知识,免不了有错误和考虑不周的,希望大家有更好的方法提出来,我随时优化代码,题目的感觉没啥太多的坑,下面让天真烂漫的蜗牛教大家用python实现

怎么用python实现

其实思路也不难啦

  • 打开这个文件,指针移到最后
  • 每隔一秒就尝试readline一下,有内容就打印出来,没内容就sleep
  • 大概就是这么个意思

监听文件

思路如下:

  • 用open打开文件
  • 用seek文件指针,给大爷把跳到文件最后面
  • while True进行循环
    • 持续不停的readline,如果能读到内容,打印出来即可

代码呼之欲出

with open('test.txt') as f:
    f.seek(0,2)
    while True:
        last_pos = f.tell()
        line = f.readline()
        if line:
            print line

效果图如下

代码说明

  • seek第二个参数2,意思就是从文件结尾处开始seek,更标准的写法使用os模块下面的SEEK_END,可读性更好
  • 只写出了简单的逻辑,代码简单粗暴,如果这个题目是10分的话,最多也就拿4分吧,不能再多了

优化点

  • print有缺陷,每次都是新的一行,换成sys.stdout.write(line)更和谐
  • 文件名传参,不能写死
  • 直接打印可以当成默认行为,具体要做什么,可以写成函数处理,这样我们就可以把新行做其他处理,比如展示在浏览器里
  • 加上容错处理,比如文件不存在会报错
  • while True一直都文件,比较耗性能,每读一次,间隔一秒比较靠谱
    • 调用time.sleep(1)
  • 用类来组织代码

实例代码如下

# coding=utf-8
import sys
import time

class Tail():
    def __init__(self,file_name,callback=sys.stdout.write):
        self.file_name = file_name
        self.callback = callback
    def follow(self):

        try:
            with open(self.file_name) as f:
                f.seek(0,2)
                while True:
                    line = f.readline()
                    if line:
                        self.callback(line)
                    time.sleep(1)
        except Exception,e:
            print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
            print e

使用方法:

# 使用默认的sys.stdout.write打印到屏幕
py_tail = Tail('test.txt')
py_tail.follow()

# 自己定义处理函数

def test_tail(line):
    print 'xx'+line+'xx'

py_tail1 = Tail('test.txt', test_tail)
py_tail1.follow()

咦 等等,tail -f 默认还会打印最后10行,这个好像才是这个题目的难点所在,众所周知,python里读文件指针,只能移动到固定位置,不能判断是哪一行,囧,先实现简单的,逐渐加强吧

默认打印最后10行

现在这个代码,大概能拿6分啦,我们还有一个功能没做,那就是打印最后n行,默认是10行,现在把这个功能加上,加一个函数就行啦

文件小的时候

我们知道,readlines可以获取所有内容,并且分行,代码呼之欲出,获取list最后10行很简单有么有,切片妥妥的

# 演示代码,说明核心逻辑,完整代码在下面
last_lines = f.readlines()[-10:]
for line in last_lines:
    self.callback(line)

此时代码变成这样了

import sys
import time

class Tail():
    def __init__(self,file_name,callback=sys.stdout.write):
        self.file_name = file_name
        self.callback = callback
    def follow(self,n=10):
        try:
            with open(self.file_name) as f:
                self._file = f
                self.showLastLine(n)
                self._file.seek(0,2)
                while True:
                    line = self._file.readline()
                    if line:
                        self.callback(line)
        except Exception,e:
            print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
            print e
    def showLastLine(self, n):
        last_lines = self._file.readlines()[-n:]
        for line in last_lines:
            self.callback(line)

更进一步:大的日志怎么办

但是如果文件特别大呢,特别是日志文件,很容易几个G,我们只需要最后几行,全部读出来内存受不了,所以我们要继续优化showLastLine函数,我觉得这才是这题的难点所在

大概的思路如下

  • 我先估计日志一行大概是100个字符,注意,我只是估计一个大概,多了也没关系,我们只是需要一个初始值,后面会修正
  • 我要读十行,所以一开始就seek到离结尾1000的位置seek(-1000,2),把最后1000个字符读出来,然后有一个判断
  • 如果这1000个字符长度大于文件长度,不用管了 属于级别1的情况,直接read然后split
  • 如果1000个字符里面的换行符大于10,说明1000个字符里,大于10行,那也好办,处理思路类似级别1,直接split取后面十个
  • 问题就在于 如果小于10怎么处理
    • 比如1000个里,只有四个换行符,说明一行大概是1000/4=250个字符,我们还缺6行,所以我们再读250*5=1500,一共有2500的大概比较靠谱,然后在对2500个字符进行相同的逻辑判断,直到读出的字符里,换行符大于10,读取结束

逻辑清晰以后,代码就呼之欲出啦

# coding=utf-8
import sys
import time

class Tail():
    def __init__(self,file_name,callback=sys.stdout.write):
        self.file_name = file_name
        self.callback = callback
    def follow(self,n=10):
        try:
            with open(self.file_name) as f:
                self._file = f
                self._file.seek(0,2)
                self.file_length = self._file.tell()
                self.showLastLine(n)
                while True:
                    line = self._file.readline()
                    if line:
                        self.callback(line)
                    time.sleep(1)
        except Exception,e:
            print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
            print e
    def showLastLine(self, n):
        len_line = 100
        read_len = len_line*n
        while True:
            if read_len>self.file_length:
                self._file.seek(0)
                last_lines = self._file.read().split('\n')[-n:]
                break
            self._file.seek(-read_len, 2)
            last_words = self._file.read(read_len)
            count = last_words.count('\n')          
            if count>=n:
                last_lines = last_words.split('\n')[-n:]
                break
            else:
                if count==0:
                    len_perline = read_len
                else:
                    len_perline = read_len/count
                read_len = len_perline * n
        for line in last_lines:
            self.callback(line+'\n')
if __name__ == '__main__':
    py_tail = Tail('test.txt')
    py_tail.follow()

加上注释的版本

# coding=utf-8
import sys
import time

class Tail():
    def __init__(self,file_name,callback=sys.stdout.write):
        self.file_name = file_name
        self.callback = callback
    def follow(self,n=10):
        try:
            # 打开文件
            with open(self.file_name) as f:
                self._file = f
                self._file.seek(0,2)
                # 存储文件的字符长度
                self.file_length = self._file.tell()
                # 打印最后10行
                self.showLastLine(n)
                # 持续读文件 打印增量
                while True:
                    line = self._file.readline()
                    if line:
                        self.callback(line)
                    time.sleep(1)
        except Exception,e:
            print '打开文件失败,囧,看看文件是不是不存在,或者权限有问题'
            print e
    def showLastLine(self, n):
        # 一行大概100个吧 这个数改成1或者1000都行
        len_line = 100
        # n默认是10,也可以follow的参数传进来
        read_len = len_line*n
        # 用last_lines存储最后要处理的内容
        while True:
            # 如果要读取的1000个字符,大于之前存储的文件长度
            # 读完文件,直接break
            if read_len>self.file_length:
                self._file.seek(0)
                last_lines = self._file.read().split('\n')[-n:]
                break
            # 先读1000个 然后判断1000个字符里换行符的数量
            self._file.seek(-read_len, 2)
            last_words = self._file.read(read_len)
            # count是换行符的数量
            count = last_words.count('\n')

            if count>=n:
                # 换行符数量大于10 很好处理,直接读取
                last_lines = last_words.split('\n')[-n:]
                break
            # 换行符不够10个
            else:
                # break
                #不够十行
                # 如果一个换行符也没有,那么我们就认为一行大概是100个
                if count==0:

                    len_perline = read_len
                # 如果有4个换行符,我们认为每行大概有250个字符
                else:
                    len_perline = read_len/count
                # 要读取的长度变为2500,继续重新判断
                read_len = len_perline * n
        for line in last_lines:
            self.callback(line+'\n')
if __name__ == '__main__':
    py_tail = Tail('test.txt')
    py_tail.follow(20)

效果如下,终于可以打印最后几行了,大家可以试一下,无论日志每行只有1个,还是每行有300个字符,都是可以打印最后10行的

能做到这个地步,一般的面试官就直接被你搞定了,这个代码大概8分吧,如果还想再进一步,还有一些可以优化的地方,代码放github上了,有兴趣的可以拿去研究

待优化:留给大家作为扩展

  • 如果你tail -f的过程中,日志被备份清空或者切割了,怎么处理
    • 其实很简单,你维护一个指针位置,如果下次循环发现文件指针位置变了,从最新的指针位置开始读就行
  • 具体每种错误的类型
    • 我这里只是简单的try 可以写个函数,把文件不存在,文件没权限这些报错信息分开
  • 性能小优化,比如我第一次读了1000,只有4行,我大概估计每行250个字符,总体需要2500行,下次没必要直接读2500个,而是读1500行和之前1000行拼起来即可

最后大杀器 如果写出来这个,基本面试官会直接

import os
def tail(file_name):
    os.system('tail -f '+file_name)

tail('log.log')

打死你

以上就是我对这个题的想法,实际开发中想监控文件变化,其实还是pyinotify好用,跪求大家star

最后做个小广告,欢迎大家关注公共号,高品质运维开发,我们每周五晚上还会做线上公开课,加QQ368573673报名即可,都是关于linux,运维,python和前端的相关内容

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码

互联网人图书推荐(一)浪潮之巅+硅谷之谜,荡气回肠的IT历史

今年重读了一下浪潮之巅外加吴军博士的新书《硅谷之谜》,浪潮之巅封面上的一段话我很喜欢,大概意思如下

近一百多年来,总有些公司幸运的站在了技术革新的浪潮之上,一旦站在浪潮之上,即使不做任何事,也可以存活几十年,外人看来,公司和公司内的职员都是时间的幸运儿,特别是对于一个人来说,一生赶上这样的一次浪潮就足够了

这句话,应该是 站在风口,猪也会飞的原始版本吧
# 浪潮之巅

整本书都在通过介绍著名IT公司的兴衰来介绍it史,但是读完又感觉收获更大的,是作者对这些历史读到的见解,每个互联网人都应该了解it的兴衰史,我们只有了解了历史,才能看清未来

本书首先介绍了各大it公司的兴衰史,包括att

python 10 min系列三之小爬虫(一):简单的信息抓取

python10min系列之小爬虫

前一篇大家表示有点难,写点简单的把,比如命令行里看论坛的十大

据说右上角先给个star再看,能掌握博客代码的100% 哈哈

我是北交大的,所以就拿自己学校练手吧 知行论坛,大家学会方法后,爬什么都很easy啦,用简单的代码,说明简单爬虫的原理即可

文章代码地址

基本所有学校论坛,都有一个十大模块,我们学校也不例外,也是我比较关注的,我们就写个脚本爬一下十大列表吧

图里红色方块,就是我关心的部分,在写爬虫之前,我们先来普及一下基础知识,我们看到的网站,是红红绿绿,挺好看的,但是代码看来,其实就是一大串字符构成,比如我们新建一个文件,zhixing.html,注意,一定要用.html结尾,用文本编辑器打开,输入以下内容

name:<input type="text">
<input type="button" value="click me">

然后双击,浏览器会打开这个页面,你就会看到一个输入框,和一个按钮,见下图,这就是最简单的html

所以我们用代码去抓各种网站,代码其实看到的,就是一堆html标签,我们需要做的,就是从标签里面解析出我们想要的内容,并且输出

我们需要python的requests模块来发送请求,用pyquery来解析数据

# coding=utf-8
import requests

url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
print r.text

这几行代码抓取的内容,应该和大家在浏览器里 右键->查看源代码看到的东西是一样的

我们已经获取和网页的内容,肿么拿到十大的内容呢

复杂的html结构,各种层级嵌套,如果想自己写一个解析html的工具,估计还没学会编程就直接狗带了,我们一定要善于使用现有的工具,比如我很喜欢的pyquery,

- pyquery是python的一个模块,使用jquery的语法解析html文档

身为一个前端工程师,对pyquery简直毫无抵抗力,看代码之前,给大家再普及一下,我们在chrome里右键->审查元素(或者点F12),就可以看到浏览器的元素层级结构,具体见下图,我们通过html元素的id或者class属性找到元素即可

先看下F12页面,比如我们查看头部的banner广告

再看十大对应的标签位置

找到了两个模块的id,聚焦的id是portal_block_654,十大的是portal_block_617

再深入寻找十大标题的具体标签,见下图

我们找到了具体的标签,通俗易懂的方式就是,网页里面,id是portal_block_617和标签下面的li标签,下面的a标签就是

- 找id的语法,是#,class是小数点. 标签就是标签名,这是juqey的基本语法,这些基础内容可以直接百度

代码呼之欲出

# coding=utf-8
import requests
from pyquery import PyQuery as pq

url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
p = pq(r.text).find('#portal_block_617 li>a')
for d in p:
    print pq(d).text()

效果如图 不动戳大

我们已经成功拿到标题啦,如果想加上今日聚焦,今日聚焦和十大的标签结构有点不太一样,是table包起来的,所以只需要稍微改一下下,代码如下,主要是find的地方不太一样

# coding=utf-8
import requests
from pyquery import PyQuery as pq

url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
p = pq(r.text).find('#portal_block_654 table a')
for d in p:
    print pq(d).text()

执行效果如下

bingo,稍微扩展一下上面的代码,把每个十大的连接地址拿出来(今日聚焦的自己扩展吧)

# coding=utf-8
import requests
from pyquery import PyQuery as pq

url = 'http://zhixing.bjtu.edu.cn/portal.php'
r = requests.get(url)
p = pq(r.text).find('#portal_block_617 li>a')
for d in p:
    print pq(d).text()  
    print 'http://zhixing.bjtu.edu.cn/'+pq(d).attr('href')

效果如下

最终结果

今天舍友推荐了首神曲,超越《忐忑》
http://zhixing.bjtu.edu.cn/thread-976923-1-1.html
咱们交大部分人素质真心不敢恭维
http://zhixing.bjtu.edu.cn/thread-976951-1-1.html
大摆长裙如何愉快滴坐下
http://zhixing.bjtu.edu.cn/thread-976887-1-1.html
积分增长这么慢,何日才能升级啊。。。
http://zhixing.bjtu.edu.cn/thread-976954-1-1.html
求推介高清电影论坛
http://zhixing.bjtu.edu.cn/thread-976901-1-1.html
我双十一的包裹终于到北京辣~\(≧▽≦)/~
http://zhixing.bjtu.edu.cn/thread-976912-1-1.html
【论】别人家的学校~
http://zhixing.bjtu.edu.cn/thread-976966-1-1.html
我觉得知行应该搞一个板块叫过往的十大
http://zhixing.bjtu.edu.cn/thread-976946-1-1.html
我觉得在宿舍拖凳子声音应该小点
http://zhixing.bjtu.edu.cn/thread-976928-1-1.html
免费的论文查重网站
http://zhixing.bjtu.edu.cn/thread-976970-1-1.html

今天的第一部分先单这里,我们已经拿到了连接地址,就可以继续去抓帖子的具体地址,还可以根据用户选择,去查看不同帖子的内容,甚至还可以发帖和恢复,但是有一个问题,那就是

- 知行的帖子查看是需要登录的,我们现在直接抓,只会抓到让你登录的信息

我们需要一个东西叫做cookie,我们的登录信息都存放在cookie里面,我们抓取网页的时候,带上登录信息,就像咱们的一卡通一样,不带卡进宿舍楼,就会被拦着,带着一卡通就可以畅通无阻啦,我们就需要带着登录的cookie去抓十大的具体信息就OK拉

后续教程:

  • 模拟登录,抓取十大具体的帖子内容
  • 简单的回复帖子
  • 把十大的信息汇总,生成pdf
  • 会抓取信息,后续扩展就很多啦,比如爬知乎的文章,爬天气预报,然后汇总一下,给自己发邮件都是很easy的拉
  • 利用新浪微博的接口,做一个命令行版的微博,命令行里就可以刷微博
  • 比如知乎上的PC大大,是我很崇拜的,可以把他的专栏 面向工资编程的所有文章都爬下来,拼接一下,自动生成一个pdf,打印出来周末在家慢慢读,这是我下一步要做的教程,大家敬请期待

以上,都是在命令行里执行的 我写代码的间隙,执行一下命令,就可以看下母校的十大,关注一下学校最近的状况,不耽误时间哦

如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝[email protected]或者扫二维码

我也做了个饼图统计HTTP状态码

最近在写公司内部服务器集群的监控工具 HTTP状态码也做了个饼图
正当我觉得这玩意在逼格甚高的时候
主程说我在浪费时间
FUCK
愿天堂没有程序员

新家要买的东西

厨房

  • 置物架
  • 微波炉
  • 免打孔伸缩杆

客厅

  • 照片墙的底(羊毛毡布)
  • 打印照片
  • 餐桌
  • 餐椅
  • 洗手池
  • 毛巾
  • 浴巾
  • 墙纸
  • 鱼缸
  • 鱼食

厕所

  • 角落置物架
  • 浴室帘(伸缩杆)
  • 马桶遥控器置物架
  • 厕纸置物架

主卧

  • 梳妆台
  • 懒人沙发
  • 壁灯
  • 梳妆台灯
  • 枕头
  • 褥子
  • 靠枕
  • 床单被罩
  • 羽绒被
  • 夏凉被
  • 投影
  • 投影架

次卧

  • 电视
  • 沙发
  • 洞洞板
  • 褥子
  • 书桌椅子

阳台

  • 动物门

装饰品

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.