Python爬虫:统计扇贝网站打卡数据

网上的教程,基本上都很简单,直接上了一大段代码,和大家说程序OK了
但我初学爬虫,还是什么都看不懂
所以,这是一个特别啰嗦的教程

完整代码

Python爬虫:获取扇贝打卡信息
Python爬虫:将爬虫结果保存到Excel
Python爬虫:从TXT导入数据
(如果你懒得看教程,可以直接看代码)

什么是爬虫?

爬虫是自动化帮我们获取网页数据的程序
简单来说,用爬虫,去获取网页数据

那么,我们大概需要这么几个步骤

  • 明确目标:首先确定,我们要爬虫什么内容
  • 打开网页:确定网页网址,得到网页响应
  • 定位数据:找到我们需要的数据
  • 清洗数据:把我们要的那部分数据截出来
  • 保存数据:把数据保存到EXCEL或者txt

明确目标

我们以扇贝网为例,讲解爬虫
简单介绍一下,扇贝网是一个学英语的网站,里面有个小组,小组里的成员一起打卡

我爬虫的目标是:得到小组成员一周的打卡时间汇总,以及对应的学习内容

我的目标

下面开始爬虫,出发!

打开网页

首先打卡网页,各个网页的网址不一样,大家要尝试去发现规律
比如扇贝网每一位同学,会有一个ID,我的ID是16888030
那我先定义我的ID,用input把我的ID输进去

ID = input("请输入你的扇贝ID:")

web代表网页地址,str将我的ID从数字转换成字符串。下面两种表达方式完全等效。

web = "https://www.shanbay.com/api/v1/checkin/user/"+str(ID)+"/"
web = "https://www.shanbay.com/api/v1/checkin/user/16888030/"
存在web中的网址

然后开始获取网页

from urllib.request import urlopen  # 加入urllib模块
web = "https://www.shanbay.com/api/v1/checkin/user/"+str(ID)+"/"
shanbay = urlopen(web) # 打开网址

urllib 是Python 中用于获取网页数据的模块,通过import调用它。我们用urlopen打开网址,这时候print(shanbay),输出shanbay的内容

我们可以看到“Response”,代表我们成功获得了对方网址给我们的回答

那回答的内容是什么呢?通过read打开网址

shanbay_data = shanbay.read().decode()

这时候输出shanbay_data,可以看到网页内容已经被读取

shanbay_data

定位数据

网页已经读取成功,接下来我们就需要定位我们需要的数据


定位数据

定位内容:

在刚刚获取的网页内容中,看到了我们需要的内容

  • bdc代表学习单词情况
  • listen代表学习听力情况
  • sentence代表学习句子情况

定位开始和结束位置:

我们发现,这些数据最开始的部分,是从“stats”开始的,到“track_object_img”结束

  • 这里注意,选择离你要的内容近一点的定位目标
  • 定位近,为后续字符串操作提供便利
  • 定位太远了,就需要删减很多内容

那我们首先,先把从“stats”“track_object_img”的内容提出来

find_data = re.findall("\"stats\".*?track_object_img" ,shanbay_data)

这里我们调用了re 模块来使用正则表达式
正则表达式:使用单个字符串来描述、匹配一系列符合某个句法规则的字符串
官方文档:re — Regular expression operations

怎么定位到我们需要的内容?用search或者find来找

re.search() 
re.findall()  

具体内容怎么表达?用符号匹配

^      # 匹配字符串的开头
$      # 匹配字符串结尾
.      # 匹配任意字符
*      # 表示任意次(从0到无限)
+      # 表示至少一次或任意次数

[0-9]  # 从0至9共十个数字中的任意一个
[a-z]  # 从小写a到z,26个字母中的一个
[A-Z]  # 从大写A到Z,26个字母的一个

\s     # 用于匹配单个空格,包括tab键和换行符 
\S     #  用于匹配单个空格之外的所有字符 
\d     # 匹配0-9的数字 
\w     # 匹配字母、数字或下划线 

举例:我们前面说过,要把“stats”“track_object_img”的内容提出来,我们就可以这么表达

re.findall("\"stats\".*?track_object_img" ,shanbay_data)

讲解一下这句代码:
1、 用findall找出所有从“stats”“track_object_img”的内容
2、\"stats\"(开始位置).(匹配任意字符)*(任意次数)track_object_img(结束位置)

此处可能会有两个疑问:

  • \"stats\" 为什么要\",不直接"
    因为"在Python中有特殊作用("字符串""是字符串的标志),所以我们用到"的时候,用\"来表示
  • 代码中间那个要来干嘛的?
    是避免贪婪匹配。
    贪婪匹配:我从A匹配到B,默认是从第一个A匹配到最后一个B
    非贪婪匹配:第一个A匹配到第一个B

到这一步,我们就把所有我们需要的内容提取出来了,下面开始具体清洗提取我们要的数据。

清洗数据

find_data = re.findall("\"stats\".*?track_object_img" ,shanbay_data)

经过上一步,我们把所有需要的内容都保存在find_data中,find_data里面此时有20条数据,是我最近20天的打卡情况

find_data中的数据

我们仔细观察find_data数据,可以看到,打卡的内容是有规律的。

"stats": {"sentence": {"num_today": 5, "used_time": 2.0}, "bdc": {"num_today": 26, "used_time": 5.0}, "listen": {"num_today": 11, "used_time": 14.0}}, "track_object_img"

所有的内容,都会有一个num_todayused_time。就是你今天学了多少,花了多少时间。这两个数据,就是我们想要的。开心,继续往下~

我需要一条一条把信息读出来,这就需要一个for循环

for data in find_data:       
        read = re.findall("\"read\":.*?}",data)
        if read == []:
           read = "{num_today\": 0, \"used_time\": 0.0}"
        
        listen = re.findall("\"listen\":.*?}",data)
        if listen == []:
            listen = "{num_today\": 0, \"used_time\": 0.0}"
        
        sentence = re.findall("\"sentence\":.*?}",data)
        if sentence == []:
            sentence = "{num_today\": 0, \"used_time\": 0.0}"
        
        bdc = re.findall("\"bdc\":.*?}",data)
        if bdc == []:
            bdc = "{num_today\": 0, \"used_time\": 0.0}"    

for循环依次取读20条数据,提取里面的阅读read、听力listen、句子sentence、单词bdc数据

这里我们用一个if判断,如果find_data里面有这部分内容,就提取出来,如果没有(也就是我们没有学习这部分),我们就构造一段字符串{num_today\": 0, \"used_time\": 0.0},把num_todayused_time设置为0,和其他内容统一起来

大家可以看到,我用了4个re.findall,把阅读read、听力listen、句子sentence、单词bdc数据取出来
以单词bdc为例,这时候提取的数据如下

怎么具体提取出6011.0这两个数字呢?

bdc_num = re.findall(r"\d+\.?\d*",str(bdc))[0]
bdc_time = re.findall(r"\d+\.?\d*",str(bdc))[1]

还是用re正则化表达,用\d把所有数字提取出来,第一个数字赋值给bdc_num,第二个数字赋值给bdc_time


操作到这一步,所有的信息相当于都提取好了

下面我们来思考一个问题,统计时间怎么确定?
我想要统计最近一周的打卡情况

设置时间

import datetime  # 先把datetime这个模块导入进来

(1)怎么确定查卡时间?可以有两种操作方式

now = datetime.datetime.now()      # 输入查卡日期,默认是今天
now = datetime.date(2019,5,13)      # 输入查卡日期,自定义

(2) 怎么定义范围?我只想统计一周的数据

time2 = datetime.timedelta(days=8)  # 统计一个星期的数据
day_now = str(now).split(" ")[0]    # 把日期取出来,后面的几点几分就不要了
day_end = now - time2               # 计算统计结束的那天

(3) 获取网页中的打卡时间

# 获取打卡天数
    checkin_time = []
    num_checkin_days = []
    find_checkin = re.findall("\"checkin_time\".*?\"share_urls\"",shanbay_data) 
    for checkin in find_checkin:
        shanbey_time = checkin.split(",")[0]
        shanbey_days = checkin.split(",")[3]
        checkin_time.append(str(shanbey_time)[len("\"checkin_time\": \""):len("\"checkin_time\": \"")+10])
        num_checkin_days.append(str(shanbey_days)[len("\"num_checkin_days\": "):])

这里我们引入字符串的基本操作

str()         将数值转变为字符串
str[::1]      将字符串倒序
ord()         获取字符的整数表示
chr()         把编码转换为对应的字符
.join()       串联若干字符
.format()     可以将字符串中的部分字符变成变量

.upper()      将字符串中的所有英文字母变成大写
.lower()      将字符串中的所有英文字母变成小写
.swapcase()   将字符串中的所有英文字母大小写互换
.title()      所有单词首字母大写

.split()      切割字符串
.strip()      删去字符串开头和结尾的空格或字符
.lsrtip()     与.strip()功能相似,从字符串左侧开始,遇到第一个不需要移除的字符则停止

.replace()    替换字符串中的某一部分
b.find(a)     返回字符串 a 在字符串 b中第一次出现所在的索引位置

我们先来看,用find_all把打卡时间这部分的数据提取出来,同样是20条数据,包含了打卡的具体时间


打开其中一条数据,发现我们要的时间2019-05-22和打卡天数538在不同的位置

这里我们用.split(),先根据逗号,把这个字符串分开

shanbey_time = checkin.split(",")[0] 
shanbey_days = checkin.split(",")[3]


然后截取字符串,用len()统计前面那些字符的个数,这些字符就不要了
append依次把内容加上去

checkin_time.append(str(shanbey_time)[len("\"checkin_time\": \""):len("\"checkin_time\": \"")+10])
num_checkin_days.append(str(shanbey_days)[len("\"num_checkin_days\": "):])

checkin_time 打卡日期

num_checkin_days 打卡天数

这时候,我们只要再上一个if 判断一下,统计从昨天开始的打卡记录

 if checkin_time[count] >= day_now:
            count += 1
        elif checkin_time[count] > day_end:            
            # 统计总时间和各项时间
            time_total = float(read_time)+float(listen_time)+float(bdc_time)+float(sentence_time);
            time_read = time_read+float(read_time);
            time_listen = time_listen+float(listen_time);
            time_bdc = time_bdc+float(bdc_time);
            time_sentence = time_sentence+float(sentence_time);  
            
            # 统计各项数目累计
            count_read = count_read+float(read_num)
            count_listen = count_listen+float(listen_num)
            count_bdc = count_bdc+float(bdc_num)
            count_sentence = count_sentence+float(sentence_num)
            
            # 输出一周每日打卡情况
            print("{},打卡{}天:阅读{}篇,听力{}句,单词{}个,炼句{}句,学习时间{}分钟".format(checkin_time[count],num_checkin_days[count],read_num,listen_num,bdc_num,sentence_num,time_total))
            count += 1
        else:
            break

最后输出结果

print("单词:{}分钟,总计{}个".format(time_bdc,count_bdc))print("阅读:{}分钟,总计{}篇".format(time_read,count_read))
print("炼句:{}分钟,总计{}句".format(time_sentence,count_sentence))
print("听力:{}分钟,总计{}句".format(time_listen,count_listen))
print('\n') 
print("打卡时长:{}分钟".format(time_read+time_sentence+time_bdc+time_listen))
代码运行截图

保存数据

在获取自己的打卡情况之后,我觉得这种都输在屏幕上的内容,很难整理,不适合小组打卡。我需要它能自动保存到Excel
这里要注意,用到Excel相关功能的时候,要导入相关库
pip install xlwt

import xlwt  # 把Excel输出模块加进来

# 定义保存Excel的位置
workbook = xlwt.Workbook()  #定义workbook
sheet = workbook.add_sheet('本周打卡')  #添加sheet
head = ['打卡', '单词', '阅读', '炼句', '听力', '学习时间']    #表头
for h in range(len(head)):
    sheet.write(0, h, head[h])    #把表头写到Excel里面去
    
# 把内容保存到Excel
sheet.write(i, 0, checkin_time[count])  # 第i行,第1列
sheet.write(i, 1, bdc_num)  # 第i行,第2列
sheet.write(i, 2, read_num)  # 第i行,第3列
sheet.write(i, 3, sentence_num)  # 第i行,第4列
sheet.write(i, 4, listen_num)  # 第i行,第5列
sheet.write(i, 5, time_total)  # 第i行,第6列

# 保存Excel表
workbook.save('C:/Users/Administrator/Desktop/扇贝打卡.xls')
print('写入excel成功')
print("文件位置:")
print("C:/Users/Administrator/Desktop/扇贝打卡.xls")

此时程序运行效果如下:


接着,小组有这么多ID,每次改一改,我都要手动输,那太麻烦了。
我需要一个代码,把ID自动导入程序

#从txt导入数据
ID_total_input = open('C:/Users/Administrator/Desktop/user.txt')
ID_total = ID_total_input.read()
ID_total = ID_total.split("\n")  # 如果输入多个ID,用“\n”分开

自动读取ID、查卡、保存到EXCEL

代码运行截图

最后,思考一下,需要导出小组打卡的哪些数据内容,调整代码
小组打卡输出EXCEL情况如下:(昵称和ID做了打码处理)

完整代码

Python爬虫:获取扇贝打卡信息
Python爬虫:将爬虫结果保存到Excel
Python爬虫:从TXT导入数据

Python远不止爬虫

坦白说,在学习编程40天的时候,我能写出小组查卡代码,我是非常欣喜和嘚瑟的。我还去小组技术群和扇贝编程群,要求大家表扬我,哈哈。

爬虫很有用。我日常是科研狗,整理课题相关的6000多篇文献,去年我用了半个月,今年我用了2个小时写了个代码。

但是Python的内容远不止爬虫。

我们小组的组员,因为工作需要,需要将几百页PDF文档中的内容转成EXCEL表格,Python几十行代码搞定

import pdfplumber
import xlwt

# 定义保存Excel的位置
workbook = xlwt.Workbook()  #定义workbook
sheet = workbook.add_sheet('Sheet1')  #添加sheet
i = 0 # Excel起始位置

path = input("请输入PDF文件位置:")    
#path = "aaaaaa.PDF"  # 导入PDF路径
pdf = pdfplumber.open(path)
print('\n')
print('开始读取数据')
print('\n')
for page in pdf.pages:
    # 获取当前页面的全部文本信息,包括表格中的文字
    # print(page.extract_text())                     
    for table in page.extract_tables():
        # print(table)
        for row in table:            
            print(row)
            for j in range(len(row)):
                sheet.write(i, j, row[j])
            i += 1
        print('---------- 分割线 ----------')

pdf.close()

# 保存Excel表
workbook.save('C:/Users/Administrator/Desktop/PDFresult.xls')
print('\n')
print('写入excel成功')
print('保存位置:')
print('C:/Users/Administrator/Desktop/PDFresult.xls')
print('\n')
input('PDF取读完毕,按任意键退出')

另外,做PPT图表总是很丑?

Python这么多好看的图表,只要改改参数,你就能拥有。不考虑一下?
数据可视化:pygal

Python其他我未知的功能,等我学习了再来和大家分享。

最后

以上内容都只是入门代码,爬虫代码中也不涉及编写函数、账号密码登入等内容。我的编程课程还没有结束,学习永无止境。

为什么要写下这个帖子,来和大家一起分享代码?

因为我们组员Grit说过:

Learning by doing. Learning by teaching.

和大家共勉,一起学习

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容