前言

2020是一个不太平的年头,篮球标杆的意外去世,新型冠状病毒的肆虐横行,给本来喜庆的新年蒙上了厚厚的阴霾,为此,国家呼吁减少出行,安心呆在家里为国家做贡献,之前想象中的躺在床上有吃的有WiFi的生活彻底实现了,但是躺的时间太久了,难免有些厌倦,朋友圈里的有些朋友丧心病狂的去晒自己吃小橘子的步骤,简直惨不忍睹,为了打发时间,想了想要不爬一下最近全国肺炎感染信息,小小分析一下,画几个图出来看看感染情况究竟如何。

采集步骤

圈里的朋友最近都会分享丁香医生的接口https://3g.dxy.cn/newh5/view/pneumonia,就从这个网址入手吧

首先浏览器打开网址,熟练的操作F12,看到以下些个请求:

请求列表

有没有异步ajax请求呢?发现没有,看来页面开发的哥们没有直接开发接口,继续看页面数据怎么渲染上去的,点开主要的页面请求,看一看response,发现以下内容:

返回页面信息

原来开发页面哥们直接用js渲染json到页面上了,那就更简单了,直接get请求发送,解析标签获取内容即可,获取json直接保存到数据库,思路有了,接下来设置一下表结构吧:

create table sars_real_num(
    parent_id varchar(20), --父级地区
    name varchar(20), --省市名字
    confirmedCount integer, --确诊人数
    deadCount integer, --死亡人数
    suspectedCount integer, --疑似人数
    curedCount integer, --治愈人数
    update_time datetime --更新时间
);

之后直接上代码:

run.py

# 导入相关包
import requests
import json
import re
import datetime
from pymysql import *
from bs4 import BeautifulSoup

# 设置url
url = 'https://3g.dxy.cn/newh5/view/pneumonia'

# 发送http请求,获取结果
result = requests.get(url, headers=headers)

# 获取更新时间
update_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

# 解析内容
result.encoding='utf-8'
content = result.text
soup = BeautifulSoup(content, 'lxml')
script_tags = soup.find_all(id='getAreaStat')
r = script_tags[0].text[27:-11]
province_list = json.loads(r)

# 设置数据库连接信息
host = '*.*.*.*'
port = 3306
database = 'sars'
user = 'sars'
password = 'sars'
charset='utf8'
connection = connect(host=host, port=port, database=database, 
               user=user, password=password, charset=charset)
cursor = connection.cursor()
insert_sql = 'insert into sars_real_num(name, parent_id, confirmedCount, deadCount, suspectedCount, curedCount, update_time) values(%s, %s, %s, %s, %s, %s, %s)'

# 解析json,保存到数据库
for province in province_list:
    params = list()
    params.append(province['provinceName'])
    params.append('NULL')
    params.append(province['confirmedCount'])
    params.append(province['deadCount'])
    params.append(province['suspectedCount'])
    params.append(province['curedCount'])
    params.append(update_time)
    cursor.execute(insert_sql, params)
    for city in province['cities']:
        params = list()
        params.append(city['cityName'])
        params.append(province['provinceName'])
        params.append(city['confirmedCount'])
        params.append(city['deadCount'])
        params.append(city['suspectedCount'])
        params.append(city['curedCount'])
        params.append(update_time)
        cursor.execute(insert_sql, params)
connection.commit()

# 关闭数据库连接
cursor.close()
connection.close()

为了使数据具有时效性,我们可以使用schedule这个模块,设置定时任务,每小时收集一次:

get_sars_real_num.py

import requests
import json
import re
import datetime
import schedule
import time
from pymysql import *
from bs4 import BeautifulSoup

def run():

    url = 'https://3g.dxy.cn/newh5/view/pneumonia'

    headers = {
        'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36',
        'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'Accept-Encoding':'gzip, deflate, br',
    }

    result = requests.get(url, headers=headers)

    update_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    result.encoding='utf-8'

    content = result.text

    soup = BeautifulSoup(content, 'lxml')

    script_tags = soup.find_all(id='getAreaStat')

    r = script_tags[0].text[27:-11]

    province_list = json.loads(r)

    host = '*.*.*.*'
    port = 3306
    database = 'sars'
    user = 'sars'
    password = 'sars'
    charset='utf8'

    connection = connect(host=host, port=port, database=database, 
                   user=user, password=password, charset=charset)
    cursor = connection.cursor()

    insert_sql = 'insert into sars_real_num(name, parent_id, confirmedCount, deadCount, suspectedCount, curedCount, update_time) values(%s, %s, %s, %s, %s, %s, %s)'

    for province in province_list:
        params = list()
        params.append(province['provinceName'])
        params.append('NULL')
        params.append(province['confirmedCount'])
        params.append(province['deadCount'])
        params.append(province['suspectedCount'])
        params.append(province['curedCount'])
        params.append(update_time)
        cursor.execute(insert_sql, params)
        for city in province['cities']:
            params = list()
            params.append(city['cityName'])
            params.append(province['provinceName'])
            params.append(city['confirmedCount'])
            params.append(city['deadCount'])
            params.append(city['suspectedCount'])
            params.append(city['curedCount'])
            params.append(update_time)
            cursor.execute(insert_sql, params)
    connection.commit()
    cursor.close()
    connection.close()


if __name__ == "__main__":
    schedule.every(1).hours.do(run)
    while True:
        schedule.run_pending()
        time.sleep(1)

在Linux服务器后台设置后台任务,让代码自己跑起来:

nohup python3 get_sars_real_num.py >> get_sars_real_num.log 2>&1 &

在后台数据库我们可以看见数据一点一点的保存了进来:

数据库信息

数据简单分析

作为典型,我们拿出湖北省的数据来进行画图分析,在这里我们用到了Python比较经典的画图库matplotlib,有兴趣的小伙伴可以去官网学习库的用法,在这里不多过赘述。

大体流程:查询数据->画图

analysis.py

import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['font.serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['figure.figsize'] = (14.0, 10.0)
from pymysql import *
host = '60.205.181.33'
port = 3306
database = 'sars'
user = 'sars'
password = 'sars'
charset='utf8'
connection = connect(host=host, port=port, database=database, 
               user=user, password=password, charset=charset)
cursor = connection.cursor()
select_sql = 'select * from sars_real_num where name = %s'
params = list()
params.append('湖北省')
count = cursor.execute(select_sql, params)
results = cursor.fetchall()
confirmedCount = list()
deadCount = list()
suspectedCount = list()
curedCount = list()
update_time = list()
for result in results:
    update_time.append(result[-1].strftime('%Y-%m-%d %H:%M:%S'))
    confirmedCount.append(result[2])
    deadCount.append(result[3])
    suspectedCount.append(result[4])
    curedCount.append(result[5])
plt.plot(update_time,confirmedCount,linewidth=3, linestyle='-.', label='确诊人数')
plt.title("湖北省肺炎确诊情况统计图",fontsize=20)
plt.tick_params(axis='both',labelsize=15)
plt.xticks(update_time, rotation=90)
y_major_locator=MultipleLocator(200)
ax=plt.gca()
ax.yaxis.set_major_locator(y_major_locator)
plt.ylim(0,5000)
plt.legend()
plt.show() #显示折线图

肺炎感染人数统计图

plt.plot(update_time,deadCount,linewidth=3, linestyle='-.', label='死亡人数')
plt.plot(update_time,suspectedCount,linewidth=3, linestyle='-.', label='疑似人数')
plt.plot(update_time,curedCount,linewidth=3, linestyle='-.', label='治愈人数')
plt.title("湖北省肺炎死亡治愈情况统计图",fontsize=20)
plt.tick_params(axis='both',labelsize=15)
plt.xticks(update_time, rotation=90)
y_major_locator=MultipleLocator(10)
ax=plt.gca()
ax.yaxis.set_major_locator(y_major_locator)
plt.ylim(0,200)
plt.legend()
plt.show()

肺炎死亡人数统计图

结论

丁香医生的接口大概在早上7点左右更新数据,且湖北省的整体感染人数、死亡人数都在不断的增加,即将要达到一个爆发期,特殊时期,谨慎出行,出门戴口罩,减少疾病的传播,保持一个健康的身体才是最重要的,祝愿祖国能早日度过这个难关,闲来无事,水文一篇,求各位大佬轻喷,溜了溜了,继续躺着去了~~~

前言

目前项目进行到中期,最近又学习了一些新的知识,例如sas拼表、导出文件、数据集筛选等,好记性不如烂笔头,记录下来有待后期回头查看,人生总是走在学习的道路上。

SAS 导出文件

SAS将数据集导出为文件有两种方法,一种使用PROC EXPORT,另外一种是使用DATA

  • proc export
PROC EXPORT DATA=DATA_SET OUTFILE="/sas/data_set.txt" DBMS=TAB REPLACE;
DELIMITER="|";
PUTNAMES=NO;
RUN;
/*
    DATA:指定要导出的数据集
    OUTFILE:指定要导出的文件
    DBMS:指定导出文件类型,文本文件为TAB CSV文件为CSV
    REPLACE:文件覆盖选项
    DELIMITER:指定文件的分隔符
    PUTNAMES:指定是否输出表头
*/
  • data
FILENAME DATA_SET "/sas/data_set.txt";
DATA _NULL_;
SET DATA_SET;
FILE DATA_SET LRECL=32767 DLM="|" ENCODING="UTF-8" TERMSTR=CRLF;
PUT
NAME AGE CLASS_NAME;
RUN;
/*
    LRECL:指定读入记录的长度,默认是256
    DLM:指定分割符
    ENCODING:指定文件编码
    TERMSTR:指定记录分隔符
*/

SAS 数据集筛选

  • WHERE子句进行筛选

SAS 数据集筛选可以在DATA步中进行操作,使用WHERE关键字对指定的列的值进行条件筛选,例子如下:

DATA STUDENT;
SET STUDENT;
WHERE AGE <= 10;
RUN;
  • KEEP关键字

KEEP关键字可以使数据集保留下你想要的字段,比如一个表里边有五个字段,你只想保留三个字段,则使用KEEP关键字进行过滤

DATA STUDENT;
    SET STUDENT;
    KEEP 字段1 字段2 字段3;
RUN;
  • DROP关键字

DROP关键字可以丢掉你不想要的字段

DATA STUDENT;
    SET STUDENT;
    DROP 字段1 字段2 字段3;
RUN;
  • 另外的写法
DATA STUDENT;
    SET STUDENT(KEEP=);
RUN;

DATA STUDENT;
    SET SUTDENT(DROP=);
RUN;

DATA STUDENT;
    SET STUDENT(WHERE=);
RUN;

SAS 循环语句格式

DATA _NULL_;
    IF CONDITION THEN ACTION;
RUN;

DATA _NULL_;
    IF CONDITION THEN DO;
        ACTION;
        ACTION;
        ACTION;
    END;
RUN;

DATA _NULL_;
    IF CONDITION THEN ACTION;
        ELSE IF CONDITION THEN ACTION;
        ELSE IF CONDITION THEN ACTION;
        ELSE IF CONDITION THEN ACTION;
    ELSE CONDITION THEN ACTION;
RUN;

SAS 拼表

SAS 拼表过程十分简单,拼表意思就是表A和表B有共同的列,将表根据相同的列拼在一起,跟SQL中的left join和right join还有inner join是一个道理,在拼表之前需要将数据集根据公共的字段排序,需要用到PROC SORT,下面介绍一下PROC SORT

  • PROC SORT
PROC SORT DATA=STUDENT NODUPKEY DUPOUT=;
BY 字段名1 字段名2 字段名3;
/*PROC SORT 默认会根据第一个字段排序,然后第二个,第三个*/
RUN;

/*
    NODUPKEY:关键字可以实现去重功能
    DUPOUT=数据集名称:可以实现将重复的数据放在指定的数据集中
    BY DESCENDING:默认以升序排序,用了DESCENDING关键字则以降序排序
*/
  • MERGE
/*left join*/
DATA STUDENT;
    MERGE A(IN=A) B(IN=B);
    BY NAME;
    IF A;
RUN;
/*right join*/
DATA STUDENT;
    MERGE A(IN=A) B(IN=B);
    BY NAME;
    IF B;
RUN;
/*inner join*/
DATA STUDENT;
    MERGE A(IN=A) B(IN=B);
    BY NAME;
    IF A AND B;
RUN;

/*
    MERGE:表A(IN=A) 表B(IN=B)
    BY 后面跟两个表共有的主键
    IF 后面跟表关联关系
*/

老司机教你用树莓派搭建Jupyter Web开发环境

前言

近期,从箱子里找出了毕业设计用的开发板神器---树莓派(Raspberry Pi),用它做的语音机器人还拿到了学校为数不多的优秀毕业设计,现在回想起来还是很激动,前几篇文章介绍过用树莓派搭建共享存储smb服务、实现内网穿透,今天将介绍用树莓派搭建好用的Python开发环境---Jupyter Notebook,废话不多说,让我们开始吧!

Jupyter Notebook简介

Jupyter Notebook(此前被称为 IPython notebook)是一个交互式笔记本,支持运行 40 多种编程语言。

Jupyter Notebook 的本质是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码,数学方程,可视化和 markdown。 用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等

安装配置

  • ssh登录到树莓派,并切换到root用户,安装Jupyter

这里说一下为什么用root,博主亲自实验过,如果用pi用户的话,Jupyter是写不到环境变量中的,可执行程序无法找到,也就启动不了Jupyter

sudo su
pip3 install jupyter
pip3 install jupyter_contrib_nbextensions
pip3 install six=1.11.0
jupyter contrib nbextension install

执行完上面的命令之后,命令行一直在输出,等到结束之后,就代表安装完毕,然后切换回pi用户,用普通用户执行下面的步骤

  • 生成配置文件

    jupyter notebook --generate-config
  • 生成密码

    jupyter notebook password

执行这条命令之后,命令行会让你输入密码,自行设置这个密码,并记住这个密码,一会Web登录要用到

  • 编辑配置文件

    1. ~/.jupyter/jupyter_notebook_config.json文件中复制出password后面的字符串

    1569597732956

    1. ~/.jupyter/jupyter_notebook_config.py文件中加入以下内容

      c.NotebookApp.ip = '*'
      c.NotebookApp.allow_remote_access = True
      c.NotebookApp.password = u'填上面复制出来的字符串'
      c.NotebookApp.port = 8888
      c.NotebookApp.open_browser = False
      c.NotebookApp.notebook_dir = '/home/pi/jupyter_notebook'

创建可持续化服务文件

切换到root用户,执行

vim /lib/systemd/system/jupyter.service

在文件中添加以下内容:
[Unit]
Description=jupyter_notebook service
After=network.target syslog.target
Wants=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/jupyter notebook --allow-root

[Install]
WantedBy=multi-user.target

启动服务

systemctl start jupyter

systemctl enable jupyter

享用

在浏览器中输入树莓派ip:8888,输入密码,就可以享用你的Jupyter了,开始愉快的编程吧~~~

1569598294810

前言

近期,从Boss那里领取了一份爬数据的任务,刚开始接到任务的时候,感觉应该很简单,页面是静态页面,不用js渲染,也不用自己构造请求参数,就是个从网页文本中用标签选择器咔咔一顿乱选,最后整理存入mongoDB即可,导出csv,完成任务。这本来是我心目中完美的一套流程,但是,随着更加深入的进入到工作节奏中之后,我才发现事情并没有那么简单~~~(大众点评的前端,你真可爱)

前期准备

古人云:工欲善其事,必先利其器。一般网络爬虫,比较经典的几个库是必须的,例如:lxmlrequestsBeautifulSoup,先甭说其他的,先装吧

pip3 install lxml
pip3 install requests
pip3 install beautifulsoup4

在准备好我们的利器之后,接下来肯定就是开始搞事情~~~

开搞第一波

熟练的在浏览器中输入http://www.dianping.com,然后随便打开一个店,比如这个

1565527644309

熟练的点击登录,拿手机扫码登录,出现上图~~~

随着下拉页面,我们终于看到了我们所需要的评论数据,比如这样:

1565527767039

来,摁一下F12,右击检查来瞧瞧这些可爱的小宝贝,到底在html里面是怎么存在的,此时发出了志在必得的嘲笑声~~~哈哈哈哈哈

然后~~~

1565527909029

这是什么??此刻黑人问号脸??在页面上不是TMD显示是字吗?这个svgmtsi是什么标签,我的网页设计白学了?我记着我当时学习挺认真的啊?大兵老师教的挺好呀????????????

img

第一次尝试,失败!Game Over!

第二次尝试

在进行第一次尝试之后,发现大众点评这个网站并不是用普通方式渲染的,是用特定的标签进行渲染上去的,这个标签到底是什么呢?让我很是费解,从来没见过,我该如何下手?既然是不认识的东西,那么看看它的属性?在CSS是怎样表示的。

1565528442040

看了一下这个标签,在CSS中只有一个background属性,给了两个坐标,难道字是用图片拼的?带着怀疑的心情,看一看这个background究竟是何方神圣,打开那个url一看,是这样的情况:

1565529438230

看到了这一个个熟悉的字,果然是通过图片渲染到页面上的,但是,另外一个难题出现了,这个字究竟是怎么对应上去的,此时还有一个信息没有用,那就是上面CSS发现的坐标值,看一看这个svg的源码吧,看看有没有什么发现,打开源码,是这样的~~~

1565529694884

看到了,x和y的值,是不是跟上面的background有一定的关系,此时发挥出小时候做找规律数学题的技能,找一找规律~~~

1565532374180

我们来看一下这个字的坐标:-434-1512,在svg源码中搜索这个字

1565533409422

字在这一行中是第32个字,同时这一样的y坐标是1535,在CSS中它的属性y坐标是-1512,如果将坐标取正值,正式坐标差23,推测一下,y坐标计算是通过background属性y取正加23得到,我们验证一下其他字,发现也是这个规律,至此,我们推测出,y坐标的对应关系;现在还剩下最后一个难题,x的坐标该怎么对应?此时发现svg上面有这么一个属性:

1565533863617

字号是14px,是不是字数*14就等于x坐标了呢?求证一下,年是第32个字,但是31 * 14 = 434,所以大胆猜测一下,x坐标是这个字在这一行的(第几位-1)* 14所得,后来发现其他字也是这样,推测正确。

第二次尝试~~~成功!

获得结论

每个标签的background属性对应着svg中的位置,首先计算过程是将x、y取正,用x / 14 所得的值+1,就是这个标签所代表字在这一行的第几位,用 y + 23 就是带这个标签代表字在哪一行

获取网页源码

接下来要做的,就是通过正常的get请求,去获取评论页的源码,在经过几次的尝试之后,发现这么一个问题,每次请求如果用一个请求头的话,最多你只能拿到30页左右的评论数据,在想继续拿到就会被封锁,即使你传入Cookie值,也无济于事

所以你需要使用一个第三方的库:fake_useragent

pip3 install fake_useragent

使用示例:

from fake_useragent import UserAgent
ua = UserAgent(verify_ssl=False)
ua.random #这里会生成一个随机的浏览器请求头

第二步,在收集网页数据的过程中,每次请求的间隔不要太短,每次请求的过程中还可能触发验证机制,你需要在浏览器端进行手动验证,方可继续使用访问,每次请求评论页的Referer是上一页的网址,意思就是告诉大众点评,你是一页一页评论连续看的,并不是从第一页一直调到其他页,下面放一下源码:

get_data.py

import requests
import time
import re
import sys
from fake_useragent import UserAgent
ua = UserAgent(verify_ssl=False)

url = 'http://www.dianping.com/shop/' + sys.argv[1] + '/review_all'

for i in range(2, int(sys.argv[2])):
    headers = {
        'User-Agent': ua.random,
        'Referer': url + '/p' + str(i-1), 
        'Cookie': sys.argv[3]
    }
    u = url + '/p' + str(i)
    result = requests.get(u, headers=headers).text
    a = re.findall('<p class="not-found-words">抱歉!页面无法访问......</p>', result, re.S)
    b = re.findall("<div class='logo' id='logo'>验证中心</div>", result, re.S)
    if a:
        print('IP被封,死心了吧')
        temp = input()
    elif b:
        print('去浏览器进行手工验证')
        temp = input()
    else:
        with open(str(i)+ '.html', 'w', encoding='utf-8') as file:
            file.write(result)
        print(u + '已经下载完毕')
        time.sleep(5)

在使用get_data.py的时候需要传入三个参数,第一个是你要爬取的店铺的id,在网址中也可以看到,例如这个:1565535039899

第二个参数,是你要爬到第几页

第三个参数,是你从浏览器中复制的Cookie

1565535133342

开始爬取

代码流程如下:

读取网页源码--->从源码中获取CSS文件URL--->从CSS文件中获取到SVG文件的URL--->获取SVG的内容--->选取评论标签--->解析标签SVG的URL--->解析标签的class--->获取坐标--->从SVG中对应字--->保存数据

在这里贴出来几个关键函数:

def get_Word_Point(className, CSSContent):
    point = re.findall(className + '{background:-(.*?).0px.*?-(.*?).0px', CSSContent, re.S)
    x = int(int(point[0][0])/14)
    y = int(point[0][1]) + 23
    return x, y

def get_Word_Content(SVGUrl, x, y, SVG_dic):
    SVGContent = SVG_dic[SVGUrl]
    result = re.findall('<text x="0" y="' + str(y) + '">(.*?)</text>', SVGContent, re.S)
    if result:
        return result[0][x]
    else:
        return get_Word_Content_B(SVGUrl, x, y, SVG_dic)

结果展示

dazhong

1565535819724

Tips

在与大众点评斗志斗勇的过程中,发现大众点评的CSS加密机制是一天两换的,上面的那个加密机制只是白天的一种,如果你访问频率过多,大众点评会自动触发另外一套加密机制,另外一套加密机制将会在下期进行述说,敬请期待。近期,我会把代码整理并开源至Github,欢迎各位的指正与指导!

EmailTyrantLucifer@linuxstudy.cn

个人博客https://www.linuxstudy.cn

前言

包括实习和入职,在SAS中国也已经待了有一段时间,经过这么久的熏陶我对SAS语言还不是很熟悉,平时的时间也没有太过深入的去进行学习SAS语言,前几天去中国银行客户现场,也总算接触到了一点SAS语言的皮毛,今天就算是一个引子吧,准备在博客开一个学习SAS语言的专栏,一是来记忆一下学到的知识点,俗话说好记性不如烂笔头,二是积累一下自己的技能,为后面的工作做铺垫,人生总是走在学习的路上。

SAS数据集、变量、常量、观测

  • 数据集:SAS可以管理的结构化数据,简单来说就是SAS软件认识的数据表,可以过程步用来数据处理,数据建模,如果说你的数据是外部文件保存,需要用SAS语言进行处理之后变成SAS数据集

    • 数据集的名称不超过32个字符
  • 变量:简单来说就是表头,数据表每一个数据项的名称就是一个变量

    • 类型:数值型和字符型(变量名后带$)
    • 特性:名称、类型、长度、输入格式、输出格式、标记
    • 最多有32个字符组成,由字母、数字、下划线组成
    • 长度默认为8
  • 常量:固定不变,跟其他编程语言类似
  • 观测:简单来说就是表的一行数据,在SAS里称之为观测

SAS导入数据的几个方法

  • 图形化界面导入(在数据不大的时候可以去用图形化界面进行导入,这里不过多赘述)
  • SAS代码导入

本次工作的目标

本次的工作目标是用SAS脚本把客户的外部数据导入到SAS软件中

SAS代码导入实例

  1. 导入内部数据,也就是sas代码中定义的数据,使用datalines关键字
data test;
    input name $ sex $ age;
    datalines;
    tom    男 23
    jim    女 24;
run;
  1. 导入txt文件数据
tom    男 23
jim    女 24

假设txt文件内容如上图所示,分隔符为空格

data test;
    infile "/home/sas/test.txt";
    input name $ sex $ age;
run;
tom/男/23
jim/女/24;

假设txt文件内容如上图所示,分隔符为/

data test;
    infile "/home/sas/test.txt" dlm='/';
    input name $ sex $ age;
run;

dlm这个参数可以指定分隔符,但前提是分隔符只有一个字符,如果分隔符是多个字符的话,则需要用dlmstr参数指定

姓名 性别 年龄
tom    男 23
jim    女 24

假设txt文件内容如上图所示,第一行有表的变量名称,我不想读进去,这时候用firstobs这个参数,指定SAS从哪一行开始读,同时obs这个参数还可以控制读到多少行

data test;
    infile "/home/sas/test.txt" firstobs=1;
    input name $ sex $ age;
run;
姓名 性别 年龄
tommmmmmmmmmmmmmmmmmmmmm 男 23
jimmmmmmmmmmmmmmmmmmmmmm 女 24

假设txt文件内容如上图所示,姓名已经超过了默认的8个长度,为了完整的导入数据,这时我们可以用length这个关键字,去进行定义这个变量的长度,或者在input语句中使用:去定义长度,所以代码可以是这样

data test;
    infile "/home/sas/test.txt" firstobs=1;
    length name $20.;
    input name $ sex $ age;
run;

或者是这样

data test;
    infile "/home/sas/test.txt" firstobs=1;
    input name:$20. sex $ age;
run;

在最后介绍一下另外的两个关键字missoverdsd

missover的作用是,如果txt文件一行数据不够时,告诉sas不要跳到下一行进行读取,简单来说就是保证读取数据不会串行

dsd的作用是,忽略用引号括起来数据中的分隔符,假设数据是用,进行分割,数据项是china,beijing,为保证该行数据能完整被读入,需要使用dsd

SAS执行Linux命令,并返回命令执行的结果

sas比较强大的地方就是可以无缝与shell进行集成衔接,这样你就可以使用shell获取到的结果进行数据处理与分析,可用到的场景非常之多,举一个例子,在sas进行io测试的时候,我们可以图形化界面显示出io测试的结果和数据,后期尝试写一个脚本出来,可以提高以后的工作效率。

 
示例代码如下:
filename cmd pipe "ls /home/sas";
data _null_;
infile cmd;
input result:$200.;
run;

未完待续

本次只是提到了txt文件,后续还有csv、excel文件,下次更新~~~

NCIAE_ Data_Structure

项目地址:https://github.com/TyrantJoy/NCIAE_Data_Structure

概述

​ 本项目是源自于北华航天工业学院大二数据结构科目的结业实训,整体来说较为简单,主要是数据结构中一些基础知识:例如线性表的删除、遍历、增加、排序;图的生成与迪杰斯特拉算法;文件的读写等。

​ 本项目在Dev C++环境下开发,clone本项目到本地之后,使用Dev C++打开Management System.dev文件,即可使用代码。

谈一谈为什么写这个东西

  • 致敬一下我的大学生活,这个实训是我当年的实训的项目,当时没有好好写,这次算是给老师交一个作业
  • 复习一下C语言和数据结构,好久不碰,害怕手生忘记

模块设计

1.初始化模块(init.cpp)

  • 初始化用户信息
  • 初始化图信息

2. 用户信息管理模块(user.cpp)

  • 增加用户
  • 删除用户
  • 查找用户
  • 修改用户

3. 排序模块(rank.cpp)

  • 冒泡排序

4. 辅助功能模块(tools.cpp)

  • 求平均步数
  • 求连续运动天数
  • 求星期
  • 数组转置
  • 打印菜单

5. 图结构模块(map.cpp)

  • 构建图
  • 迪杰斯特拉算法

数据结构设计

  • 用户信息结构体
typedef struct user
{
    char name[20];                // 用户昵称
    char phoneNumber[12];        // 用户编号
    char sex[2];                // 用户性别
    int record[7];                // 用户7天步数
    int age;                    // 用户年龄
    double averageSteps;        // 用户7天平均步数
    int motionDays;                // 用户连续运动天数
}user;
  • 图结构体
typedef struct graph
{
    char vexs[MAX][MAX];        // 地点名称
    int vexnum;                   // 节点数目
    float matrix[MAX][MAX];        // 地图边数
}Graph, *PGraph;

运行截图示例

main

user

rank

map

最近突然想起来看一看高中时期未看完的小说,心血来潮就去网上找了找资源,发现不论下载下来的txt文件还是专门用来看小说的app不是有广告,就是缺少章节,观看体验十分不爽,为了解决这个问题,自己动手,丰衣足食,于是这个采集脚本应运而生

技术要点:

  • BeautifulSoup4:解析标签
  • Requests:模拟http请求
  • Python3

脚本使用步骤:

  • 安装BeautifulSoup4
pip3 install beautifulsoup4 
  • 安装requests
pip3 install requests
  • 保存以下代码为book.py
import re                                                                             import sys
import requests
import time
from bs4 import BeautifulSoup
from requests.packages.urllib3.exceptions import InsecureRequestWarning 
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class BookSpider():
    '''爬取顶点小说网小说'''
    def __init__(self):
               
       self.headers = { 
            'Host':'www.dingdiann.com',
            'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36',
        }
       self.chapter_url_list = list()
       self.chapter_name_list = list()
       self.book_info = dict()

    def get_book_url(self, book_name, author_name):
        '''获取要爬取书籍的详情页'''
        url = 'https://www.dingdiann.com/searchbook.php'
        data = {
            'keyword':book_name
        }
        result = requests.get(url, headers=self.headers, params=data, verify=False).text
        soup = BeautifulSoup(result, 'lxml')
        book_name_list = soup.find_all(name='span', attrs={'class':'s2'})
        book_author_list = soup.find_all(name='span', attrs={'class':'s4'})
        book_name_list.pop(0)
        book_author_list.pop(0)
        for candidate_name in book_name_list:
            book_info_list = list()
            name = str(candidate_name.a.string)
            book_url = str(candidate_name.a.get('href'))
            book_info_tuple = (name, book_url)
            book_info_list.append(book_info_tuple)
            author = str(book_author_list[0].string)
            if author in self.book_info.keys():
                self.book_info[author].append(book_info_tuple)
                book_author_list.pop(0)
            else:
                self.book_info[author] = book_info_list
                book_author_list.pop(0)
        if self.book_info[author_name]:
            for info in self.book_info[author_name]:
                if info[0] == book_name:
                    url = info[1]
                    print('书籍已经找到,您要找的书籍为 ' + book_name + ':' + author_name)   
                    print('3S 后将开始下载~~~')
                    time.sleep(3)
                    return url
        else:
            print('抱歉,书籍未找到,请确认书籍作者及名称是否正确~~~')

    def get_book_info(self, url):
        '''获取书籍的章节列表和地址'''
        all_url = 'https://www.dingdiann.com' + url
        result = requests.get(all_url, headers=self.headers, verify=False).text
        soup = BeautifulSoup(result, 'lxml')
        div = soup.find_all(id='list')[0]
        chapter_list = div.dl.contents

        for text in chapter_list :
            text = str(text)
            content = re.findall('<a href="' + url + '(.*?)" style="">(.*?)</a>.*?', text)
            if content:
                chapter_url = all_url + content[0][0]
                chapter_name = content[0][1]
                self.chapter_url_list.append(chapter_url)
                self.chapter_name_list.append(chapter_name)

        for i in range(12):
            self.chapter_url_list.pop(0)
            self.chapter_name_list.pop(0)
    
    def get_chapter_content(self, name, url):
        '''获取小说每章内容'''        
        try:
            result = requests.get(url, headers=self.headers, verify=False).text
        except:
            print(name + "下载失败~~~")
            return False
        else:
            soup = BeautifulSoup(result, 'lxml')
            div = soup.find_all(id='content')[0]
            div = str(div)
            result = re.findall('<div id="content">(.*?)<script>', div, re.S)[0].strip()
            result = re.sub('<br/>', '\n', result)
            return result
    
    def save_book(self, book_name):
        '''保存小说'''
        for chapter_name in self.chapter_name_list:
            while True:
                chapter_content = self.get_chapter_content(chapter_name, self.chapter_url_list[0])
                if chapter_content:
                    with open(book_name + ".txt", 'a') as f:
                        f.write(chapter_name)
                        f.write("\n")
                        f.write(chapter_content)
                        f.write("\n")
                    self.chapter_url_list.pop(0)
                    print(chapter_name + "已经下载完成")
                    break

    def run(self, book_name, url):
        self.get_book_info(url)
        self.save_book(book_name)

def main(book_name, author_name):
    book = BookSpider()
    url = book.get_book_url(book_name, author_name)
    book.run(book_name, url)

if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2])
  • 使用说明:脚本需要输入两个参数,参数1为小说名称,参数2为作者名称,之后便会将采集到的内容保存在本地,例如:
python3 book.py 天珠变 唐家三少

享用

免责声明

  • 本脚本采集的小说数据来自顶点小说网,只提供数据采集服务,不提供任何贩卖服务
  • 数据采集自https://www.dingdiann.com/,感谢网站管理员的慷慨支持,爬了很多次也没有ban我的ip,希望大家多多支持正版