南岛鹋 https://www.ndmiao.cn/ zh-CN 努力想变的更好 ... Sat, 05 Dec 2020 13:45:00 +0800 Sat, 05 Dec 2020 13:45:00 +0800 如何查看Windows上联过的wifi密码 /index.php/archives/191/ /index.php/archives/191/ Sat, 05 Dec 2020 13:45:00 +0800 南岛鹋 netsh wlan show profiles

可查看连过的wifi名称

netsh wlan show profiles key="clear" name="WiFi名称"

即可查看WiFi密码

]]>
0 /index.php/archives/191/#comments /index.php/feed/archives/191/
Tableau学习记录 /index.php/archives/189/ /index.php/archives/189/ Fri, 06 Nov 2020 16:07:55 +0800 南岛鹋

Tableau是一款拖拽式,支持多种数据源的数据可视化软件

功能

图表

  1. 条形图和直方图
  2. 基本表、树状图、气泡图、词云
  3. 标靶图、甘特图、瀑布图
  4. 填充地图、多维地图、混合地图
  5. 人口金字塔、漏斗图、箱线图
  6. 范围-线图、倾斜图
  7. 网络图与弧线图
  8. 雷达图与凹凸图
  9. 回归分析与时间序列分析

数据的预处理

  1. 数据类型的转换
  2. 不要的数据进行隐藏
  3. 对数据进行拆分

数据后续处理

  1. 数据分层、数据分组、数据集
  2. 字段与表计算

优缺点

优点:

  1. 数据可视化
  2. 快速创建交互可视化(支持拖拽)
  3. 支持百万级别的数据处理
  4. 软件中支持python或者R的脚本语言
  5. 移动设备笔记本自定义仪表盘

缺点:

  1. 没有报告计划
  2. 无定义视觉导入
  3. 条件格式有数量限制
  4. 参数是静态的,数据发生变化的时候,需要更新参数
  5. 屏幕分辨率不同会影响仪表盘布局

概要

  • 主界面分为:连接、打开、教程
  • 支持直接连接excel、csv、json、主流数据库、PDF、空间文件、服务器等
  • 支持的数据类型:字符串、日期、数字和布尔数据等
  • 支持的运算符:算术运算符、逻辑运算符、比较运算符
  • 类似于Excel,有很多的函数可供使用
  • 高级操作:表计算、创建字段、创建函数、聚合计算、缺失值处理

创建图表

步骤:

首先导入数据源,新建工作表,将维度或者度量拖进列或者行。

标注:

维度的数据基本为字符串类型,度量是数字,可用来比较大小。

筛选器可将数据拖入进行筛选需要的数据,标记则是对可视化的图进行一些处理。

条形图和直方图

差别:

条形图用高表示各类别频数的大小,宽度是固定的表示类别。

直方图用面积表示各组频数的多少,高度表示每一组的频数,宽度代表组距,高宽均有意义。

条形图的创建:

数据要求,一个是类别数据,一个是度量数据。

用来比较各个类别的大小

  • 标题直接修改表名称即可。
  • 通过降序或者升序可以根据频数的大小排列图形。
  • 通过选择整个视图可将产生的图布满空间。
  • 想要显示具体的数字,可将相应的数据拖到标记的标签中。
  • 显示平均值需要在拖进来的数据右击度量选择平均值,标签那里同理操作。
  • 堆积图可将等级划分拖到颜色来实现。

直方图的创建:

用来查看数据的分布情况

  • 首先需要创建度量的数据桶

数据处理、折线图、饼图

数据前处理:

  • 右击列名点击拆分,可以根据规律自动拆分数据
  • 可以隐藏不需要的数据
  • 支持自定义拆分
  • 点击列名上方可以转换数据的类型

折线图:

  • NULL值可以删除
  • 标签显示同理
  • 右击点,可选择添加注释
  • 右击注释选择设置格式可对注释的样式进行设置
  • 右击标签的设置格式可以将标签的单位进行更改
  • 右击图选择双轴,可实现同轴显示

饼图:

  • 只能显示这里选择饼图
  • 右击标签的记录数,选择总额百分比,实现百分比图。
  • 计数的设置格式可以设置百分比的小数位
  • 在标签下的灰色区域右击选择标题,可修改注释

环形图:

  • 首先在标记里选择饼图
  • 托分类等级到颜色
  • 度量的记录数放在角度
  • 把记录数放在行,然后Ctrl按住再拖
  • 两个右击都选排序,然后双轴实现重合
  • 去除小的颜色,通过调整大小和颜色即可实现环形图

环形图2:

  • 通过智能显示创建,要同时删除两个的大小设置

基本表、树形图、气泡图、词云

基本表:

  • 将分类放在行,度量数据拖到标记的文本中

凸显表:

  • 可以直接点击智能显示
  • 或者拖拉到颜色,选为方形
  • 二阶颜色可在颜色里设置

树形图:

  • 在智能推荐里直接点击即可
  • 多维度数据可用面积大小和颜色分别表示
  • 可以添加筛选器来筛选去除很小的数据

气泡图和词云:

  • 基本操作和树形图一样
  • 动态需要把时间拖到页面
  • 可通过显示历史记录的选项来选择轨迹标签来让动图更加直观
  • 词云先托选类型,大小表示数量,气泡图显示,把形状改成文本

标靶图、甘特图、瀑布图

标靶图:

用来比较计划值和实际值

  • 右击度量可以添加参考线
  • 想要添加自定义的线,需要将相关数据添加到详细信息中。

甘特图:

表示项目的顺序和持续时间

  • 计划时间在列,类别行,右击维度的实际时间计算延迟,拖到大小

瀑布图:

  • 把度量的计算类型改为汇总
  • 将标记的图形改为甘特条形图
  • 度量的计算加到颜色,且进行汇总
  • 要显示总利润,则点击分析-合计-行总计

Tableau进阶

数据集合并:

  • 内部:筛选两个都有的留下
  • 左侧:只保留左侧有的
  • 右侧:只保留右侧有的
  • 外部:全部保留

符号地图:

  • 首先要把地理信息转化成国家地区的,直接拖入
  • 标注同理
  • 存在重复的地图国家名的时候,勾选掉地图的国家/地区名称即可。

自定义形状:

打开软件安装的位置,defaults-shapes里面加入图片

仪表板:

可以导入图像以及之前创建的图

填充地图、多维地图、混合地图

填充地图:

数值用板块颜色表示

地图层修改相应的参数

多维地图:

  • 列增添类别
  • 行添加时间,点击前面的框可以展开数据
  • 类别可以进行手动来处理

混合地图:

  • CTRL托两份行,同轴,然后添加别的参数为颜色,调整大小

多边形地图和背景图地图

多边形地图:

  • 取消聚合度量实现图形展示
  • 图形选择多边形,点ID到路径,公园名称到颜色

背景图地图:

  • 支持自定义添加地图信息
  • 在地图的管理地图中可以添加地图,混乱的化切换地图层的编码
  • 可添加背景图片,x,y要与图片的像素相符合

数据分层、数据组、数据集

数据分层:

  • 右击维度中的数据创建层级,将相关的数据按照次序拖入。

数据组:

  • 分类中具体的内容可进行分组,要显示其他里的内容,取消勾选其他即可
  • 地理区域分组,可用线框框选

数据集:

  • 静态数据集框选图形创建即可。
  • 同一个维度创建的集可以进行右击合并。

计算字段与表计算:粒度、聚合与比率

计算字段:

  • 右击即可创建字段计算
  • 字段计算中可以用语句来进行数据的筛选

粒度:

  • 同一维度下数据的粗细程度

聚合:

  • 对数据进行筛选挑选,右击数据默认属性的聚合里面可以进行修改

比率:

  • FIXED只考虑维度,INCLUDE维度的基础上考虑视图

表计算:

  • 汇总、差异、百分比差异、总额百分比、排序、百分位、移动平均值
  • 右击创建参数可以进行动态调整数据

人口金字塔、漏斗图、箱线图

人口金字塔:

  • 要将男女人口分别进行统计,图上倒序

漏斗图:

  • 复制四份数据分别以条形和线的形式表示

箱线图:

  • 将分析里的聚合度量取消,数据变成?

范围-线图、倾斜图

范围-线图:

可以展示整体数据、个体数据的最大值、最小值。通过整体特征和个体信息的对比,展示个体与整体之间的相对关系。

  • 用表计算计算整体的最大值、最小值、平均值后,到详细信息,添加相应的参考线即可。

倾斜图:

  • 添加表计算排名
  • 将排名的计算根据改成需要的数据

网络图与弧线图

网络图:

创建一个简单的网路图,需要几点的坐标,然后选择线图,将关联放到路径中即可。

弧线图:

创建一个文档,包括具体值,角度,数量。

创建字段x = [分类] cos([角度] PI()/180)

? y = [分类] sin([角度] PI()/180)

雷达图和凹凸图

雷达图:

雷达图图示

  • 选择部分字段右击转置,可以生成数据透视表,进行降维
  • 首先计算路径字段,标记序号,然后计算角度,设置X,Y

凹凸图:

  • 将显示形式改为线,表计算的维度改成子类别,双轴显示?

回归分析和时间序列分析

回归分析:

  • 显示点图以后,右击选择显示趋势即可
  • 点击线,点击编辑可切换拟合的模型

时间序列分析:

  • 点击图选择预测,更改参数即可
]]>
0 /index.php/archives/189/#comments /index.php/feed/archives/189/
面试编程题刷题~ /index.php/archives/187/ /index.php/archives/187/ Sat, 19 Sep 2020 15:32:00 +0800 南岛鹋 题目描述

S和T是两个字符串(它们只由小写字母构成),定义S与T相似当且仅当:
1、S和T长度相同。
2、对于任意两个位置i和j,如果Si和Sj相同,那么Ti和Tj相同;如果Si和Sj不同,那么Ti和Tj不同。(Si的含义为字符串S在第i个位置的字符,Ti的含义为字符串T在第i个位置的字符)
与字符串”abca”相似的串有”abca”,”cdac”,”zetz”等,现在给出一个字符串S,输出与之相似的字典序最小的串。

输入
输入只有一行,一个字符串,长度不超过100000,只由小写字母组成。

样例输入
helloworld

输出
输出一行,与之相似的字典序最小的串(只由小写字母组成的串)。

样例输出
abccdedfcg

data = input()
dict = {}
step = 97
res = ''
for i in data:
    if i not in dict.keys():
        dict[i] = chr(step)
        res += chr(step)
        step += 1
    else:
        res += dict[i]
print(res)

题目描述

大部分论坛、网站等,为了方便管理,都进行了关于敏感词的设定。

在多数网站,敏感词一般是指带有敏感政治倾向、暴力倾向、不健康色彩的词或不文明语,也有一些网站根据自身实际情况,设定一些只适用于本网站的特殊敏感词。比如,当你发贴的时候带有某些事先设定的词时,这个贴是不能发出的。或者这个词被自动替换为星号 (*),或者说是被和谐掉了。请注意敏感词只有小写字母,文本如果中的大写字母当做小写字母处理,出现敏感单词,即使作为子串出现也要被和谐,多个子串重叠他们都要被和谐。

例如当敏感词是gre,eat 是

Your English is Great.

将被和谐成

Your English is *.

请编程,输入给定的文本和关键字,将所有被和谐的部分都打上星号 (*)

输入

输入的第一行包含一个整数 n,表示敏感词的总数。

接下来的 n 行,每行包含一个长度不超过 100 的敏感词,单词不区分大小写。

接下来的一行包含一段长度不超过 10000的字符串表示待处理的文本。

样例输入

4
revolution
greatwall
democracy
science
Cross the greatwall, we can reach every corner of the world.

输出

输出一行,表示和谐过后的文本。

样例输出

Cross the *, we can reach every corner of the world.

while True:
    num = int(input())
    dis = []
    for i in range(num):
        dis.append(input())
    sentence = input()
    new_sen = []
    for word in dis:
        new_sen.append(sentence.lower().replace(word,len(word)*'*'))
    for n in new_sen:
        for t in range(len(n)):
            if n[t] == '*':
                sentence = sentence[:t] + '*' + sentence[t+1:]
    print(sentence)

题目描述

一队士兵在操场上排成一列,士兵总数为n,士兵按照队伍从前往后的顺序从1到n依次编号。每个士兵有各自的身高,第i个士兵的身高为ai。
士兵列队完毕后,将军走到队列的最前面。因为身高不一,有些士兵可能被前面身高更高的挡住了,这样将军就看不到他们。将军能看到某个士兵当且仅当他的身高严格大于他前面的所有士兵。
问将军一共能看到多少个士兵。

输入
第一行输入一个整数T(T<=100),表示测试数据的组数。每组数据第一行输入一个数n(1=<n<=10000)表示士兵的个数,第二行n个整数a1,a2,...,an(0=<ai<=1000000000),依次表示每一个士兵的身高。

样例输入
3
4
1 2 3 4
3
1 1 1
4
1 1 3 2

输出
对于每组数据,输出一行,将军能看到的士兵数。

样例输出
4
1
2

while True:
    group = int(input())
    for i in range(group):
        sum,max = 0,-1
        num = int(input())
        height = input().split()
        for m in range(num):
            if int(height[m]) > max:
                max = int(height[m])
                sum += 1
            else:
                sum,max = sum,max
        print(sum)

分数序列和(百度2017秋招真题)

有一个分数序列 2/1,3/2,5/3,8/5,13/8,21/13,.... 求这个分数序列的前n项之和。

输入
测试数据有多组,其第一行为一个正整数k(0<k<=90),表示测试数据的组数。每组测试数据为一行,为单个正整数n(0<n<=90)。

样例输入
1
2

输出
每组测试数据单独输出有一行:分数序列的和(精确到小数点后4位)。

样例输出
3.5000

while True:
    group = int(input())
    for i in range(group):
        num = int(input())
        m,n,sum = 2,1,0
        for t in range(num):
            sum += m/n
            n,m = m,n+m
        print('%.4f'%sum)

题目描述

度度熊收到了一个只有小写字母的字符串S,他对S的子串产生了兴趣,S的子串为S中任意连续的一段。他发现,一些子串只由一种字母构成,他想知道在S中一共有多少种这样的子串。

例如在串”aaabbaa”中,度度熊想找的子串有”a”,”aa”,”aaa”,”b”,”bb”五种。

(本题只考虑子串的种数,相同的子串在多个位置出现只算一次)

输入
输入只有一行,一个字符串,长度不超过100000,只由小写字母组成

样例输入
aaabbaa

输出
输出一行,符合要求的子串种数

样例输出
5

string = input()
str = ''
str1 = ''
res = set()
for i in range(len(string)):
    if string[i] == str1:
        str += string[i]
    else:
        str = string[i]
        str1 = string[i]
    res.add(str)
print(len(res))

给出两个?非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照?逆序?的方式存储的,并且它们的每个节点只能存储?一位?数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0?开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dumy = p = ListNode(None)
        s = 0
        while l1 or l2 or s:
            s += (l1.val if l1 else 0) + (l2.val if l2 else 0)
            p.next = ListNode(s%10)
            p = p.next
            s = s // 10
            l1 = l1.next if l1 else None
            l2 = l2.next if l2 else None
        return dumy.next

给定一个字符串,请你找出其中不含有重复字符的?最长子串?的长度。

示例?1:

输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是?"wke",所以其长度为 3。
? 请注意,你的答案必须是 子串 的长度,"pwke"?是一个子序列,不是子串。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        occ = set()
        n = len(s)
        rk, ans = -1, 0
        for i in range(n):
            if i != 0:
                occ.remove(s[i - 1])
            while rk+1 < n and s[rk+1] not in occ:
                occ.add(s[rk+1])
                rk += 1
            ans = max(ans,rk+1-i)
        return ans

给定两个大小为 m 和 n 的正序(从小到大)数组?nums1 和?nums2。请你找出并返回这两个正序数组的中位数。

进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

?

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums1.extend(nums2)
        nums1.sort()
        l = len(nums1)
        if l % 2 == 0:
            return (nums1[l//2-1] + nums1[l//2])/2
        else:
            return nums1[(l+1)//2-1]

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设?s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:

输入: "cbbd"
输出: "bb"

class Solution:
    def longestPalindrome(self, s: str) -> str:
        size = len(s)
        if size < 2:
            return s
        max_len = 1
        res = s[0]
        for i in range(size):
            js, js_len = self._center(s,size,i,i)
            os, os_len = self._center(s,size,i,i+1)
            max_hw = js if js_len > os_len else os
            if len(max_hw) > max_len:
                max_len = len(max_hw)
                res = max_hw
        return res
    def _center(self,s,size,left,right):
        while left >= 0 and right < size and s[left] == s[right]:
            left -= 1
            right += 1
        return s[left+1:right],right-left-1

将一个给定字符串根据给定的行数,以从上往下、从左到右进行?Z 字形排列。

比如输入字符串为 "LEETCODEISHIRING"?行数为 3 时,排列如下:

L   C   I   R
E T O E S I I G
E   D   H   N

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);
示例?1:

输入: s = "LEETCODEISHIRING", numRows = 3
输出: "LCIRETOESIIGEDHN"
示例?2:

输入: s = "LEETCODEISHIRING", numRows =?4
输出:?"LDREOEIIECIHNTSG"
解释:

L     D     R
E   O E   I I
E C   I H   N
T     S     G
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows < 2: return s
        res = ["" for _ in range(numRows)]
        i, flag = 0, -1
        for c in s:
            res[i] += c
            if i == 0 or i == numRows - 1: flag = -flag
            i += flag
        return "".join(res)
]]>
0 /index.php/archives/187/#comments /index.php/feed/archives/187/
哔哩哔哩八月生活搞笑区热度视频数据分析 /index.php/archives/186/ /index.php/archives/186/ Wed, 16 Sep 2020 14:31:00 +0800 南岛鹋

分析哔哩哔哩生活搞笑区的热度视频信息,分析月度视频的热词,三联等数据对视频播放量的影响。

数据爬取

确定目标

因为想要一个量大的数据集,因此没有考虑热榜排名,因为所有区加起来也才一千左右。全部视频信息的话技术不行,然后就盯上了分区榜。

分区榜

从这个榜单可以选择时间段,可以根据每个月的视频热度排名等信息,来分析月度热点,哪些视频更加容易火,以及各种因素对视频播放量的影响。虽然只是一个小分区月度热度排名,并不包含全部视频,但是数据量也是极大的。下图可以看到接近有23万条数据。

数据量

网站分析

这里存在一个难点,就是虽然浏览器上是可以查看网页源码,并且包含了视频的相关信息,但是用requests请求之后的网页源码却并没有相关的信息。因此前两个版本,我采用了selenium库的方法来获取信息,但是这个方法有一个缺点,速度慢(因为要跟浏览器一样加载整个页面信息)、信息少(只有标题、作者、视频简介、以及视频页和个人主页网址),很麻烦。于是这次我换成了API调用的方法。

页面分析

我们选择一个具体的数字来查找,可以发现搜索出来一个search的接口。

页面分析

点进去之后,可以发现里面的result共有20条数据,刚好对应着每页20个视频。

数据分析

可以看到里面包含了作者、标题、标签、播放等一系列数据。

接口为https://s.search.bilibili.com/cate/search?callback=main_ver=v3&search_type=video&view_type=hot_rank&order=click©_right=-1&cate_id=138&page=1&pagesize=20&jsonp=jsonp&time_from=20200801&time_to=20200831 ,view_type为排行类型,page为页面数,pagesize为页面最大的视频数,上限好像是100。最后面就是时间了。

但是我还需要三连数据以及UP主的粉丝量。同理分析

得到三连的API接口:https://api.bilibili.com/x/web-interface/archive/stat?aid=371876135

其中aid由BV转换。

粉丝数为https://api.bilibili.com/x/relation/stat?vmid=32172331

mid可以在第一个接口处获取。

IP池

这时候虽然已经可以开始爬取了,但是如果数据量稍微有一点大,访问稍微有点频繁,就会导致IP被屏蔽。

这时候我们就需要用到代理IP,免费的代理IP虽然也有,而且GITHUB上也有专门的项目来建立代理IP池。但是免费的终究很麻烦,于是我选择了日租独享的IP。http://www.xdaili.cn/

代码

# coding: utf-8
# Author:南岛鹋 
# Blog: www.ndmiao.cn
# Date :2020/8/25 10:29
# Tool :PyCharm

import requests
import csv
import json
import random
import time


class video_data:
    def __init__(self):
        self.url = 'https://s.search.bilibili.com/cate/search?main_ver=v3&search_type=video&view_type=hot_rank&order=click&copy_right=-1&cate_id=138&page={}&pagesize=20&jsonp=jsonp&time_from=20200801&time_to=20200831'
        self.page = 11507
        self.alphabet = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'

    def dec(self, x):  # BV号转换成AV号
        r = 0
        for i, v in enumerate([11, 10, 3, 8, 4, 6]):
            r += self.alphabet.find(x[v]) * 58 ** i
        return (r - 0x2_0840_07c0) ^ 0x0a93_b324

    def random_headers(self, path): # 随机读取一个头信息
        with open(path, 'r') as f:
            data = f.readlines()
            f.close()

        reg = []
        for i in data:
            k = eval(i)  # 将字符串转化为字典形式
            reg.append(k)
        header = random.choice(reg)
        return header

    def get_ip(self): # 代理IP获取
        print('切换IP中.......')
        url = '代理IP的地址'
        ip = requests.get(url).text
        if ip in ['{"ERRORCODE":"10055","RESULT":"提取太频繁,请按规定频率提取!"}', '{"ERRORCODE":"10098","RESULT":"可用机器数量不足"}']: # 出现频繁或者机器不足,睡眠14秒
            time.sleep(14)
            ip = requests.get(url).text
            print(ip)
        else:
            print(ip)
        proxies = {
            'https': 'http://' + ip,
            'http': 'http://' + ip
        } # 设置https和http可以按需选择
        return proxies

    def get_requests(self, url, proxy): # 请求的函数
        headers = self.random_headers('headers.txt')
        # 将头信息和IP写入,用try来减少意外对程序的影响
        try: 
            response = requests.get(url, timeout=3, headers=headers, proxies=proxy)
        except requests.exceptions.RequestException as e:
            print(e)
            proxy = self.get_ip()
            try:
                response = requests.get(url, timeout=3, headers=headers, proxies=proxy)
            except requests.exceptions.RequestException as e:
                print(e)
                print('原始IP')
                response = requests.get(url, timeout=3, headers=headers)
        return response, proxy

    def get_follower(self, mid, proxy): # 获取粉丝数
        url = 'https://api.bilibili.com/x/relation/stat?vmid=' + str(mid)
        r, proxy = self.get_requests(url, proxy)
        result = json.loads(r.text) # 用json来解析文本
        # 按照需求获取需要的数据,因为粉丝数是必定存在的,所以失败了需要多次尝试获取。
        try:
            follower = result['data']['follower']
        except:
            follower,proxy = self.get_follower(mid, proxy)
        return follower, proxy

    def get_view(self, BV, proxy): # 获取三连和播放
        aid = self.dec(BV)
        url = 'https://api.bilibili.com/x/web-interface/archive/stat?aid=' + str(aid)
        r, proxy = self.get_requests(url, proxy)
        result = json.loads(r.text)
        view = {}# 因为视频虽然在排行榜,但是很可能已经删除,所以没有数据为None
        try:
            view['view'] = result['data']['view']
            view['danmu'] = result['data']['danmaku']
            view['reply'] = result['data']['reply']
            view['like'] = result['data']['like']
            view['coin'] = result['data']['coin']
            view['favorite'] = result['data']['favorite']
            view['share'] = result['data']['share']
            view['rank'] = result['data']['his_rank']
        except:
            view['view'] = 'None'
            view['danmu'] = 'None'
            view['reply'] = 'None'
            view['like'] = 'None'
            view['coin'] = 'None'
            view['favorite'] = 'None'
            view['share'] = 'None'
            view['rank'] = 'None'
        return view, proxy

    def get_parse(self, result, proxy): # 整合数据
        content = []
        items = result['result']
        for item in items:
            pubdate = item['pubdate']
            title = item['title']
            author = item['author']
            bvid = item['bvid']
            mid = item['mid']
            follower, proxy = self.get_follower(mid, proxy)
            video_view, proxy = self.get_view(bvid, proxy)
            view = video_view['view']
            danmu = video_view['danmu']
            reply = video_view['reply']
            like = video_view['like']
            coin = video_view['coin']
            favorite = video_view['favorite']
            share = video_view['share']
            rank = video_view['rank']
            tag = item['tag']
            con = [pubdate, title, author, bvid, mid, follower,view,danmu,reply,like,coin,favorite,share,rank, tag]
            content.append(con)
            print(con)
        print(content)
        self.save(content)
        return proxy

    def write_header(self):
        header = ['日期', '标题', '作者', 'BV', 'mid', '粉丝', '播放', '弹幕', '评论', '点赞','硬币','收藏','转发','排名','标签']
        with open('fun_video.csv', 'a', encoding='gb18030', newline='')as f:
            write = csv.writer(f)
            write.writerow(header)

    def save(self,content):# 存入csv
        with open('fun_video.csv', 'a', encoding='gb18030', newline='')as file:
            write = csv.writer(file)
            write.writerows(content)

    def run(self):
        #self.write_header()
        proxy = self.get_ip()
        for i in range(168, self.page):
            url = self.url.format(i)
            response, proxy = self.get_requests(url, proxy)
            result = json.loads(response.text)
            proxy = self.get_parse(result, proxy)
            print('第{}页爬取完毕'.format(i))


if __name__ == '__main__':
    video = video_data()
    video.run()

数据分析

以下代码在Notebook上运行
首先我们需要导入自己需要用到的库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from wordcloud import WordCloud, STOPWORDS #导入模块worldcloud
from PIL import Image #导入模块PIL(Python Imaging Library)图像处理库
import numpy as np #导入模块numpy,多维数组
import matplotlib
import jieba

数据预处理

读取数据

data=open(r'D:\video_data.csv',encoding='utf-8')
video_data = pd.read_csv(data)

查看数据前五行

video_data.head()#查看前五行

前五行数据

浏览数据的大概信息

video_data.info()#视频数据的信息

数据信息

对数据进行预处理,将None值换成0,将数字数据的格式换成int

video_data['播放'].replace('None', 0,inplace = True)#将数据中标记为None的数据替换成0,方便数据处理
video_data['弹幕'].replace('None', 0,inplace = True)
video_data['评论'].replace('None', 0,inplace = True)
video_data['点赞'].replace('None', 0,inplace = True)
video_data['硬币'].replace('None', 0,inplace = True)
video_data['收藏'].replace('None', 0,inplace = True)
video_data['转发'].replace('None', 0,inplace = True)
video_data['排名'].replace('None', 0,inplace = True)
video_data['播放'] = video_data['播放'].astype("int64")#将数字的格式转换成int格式,用于数据处理
video_data['弹幕'] = video_data['弹幕'].astype("int")
video_data['评论'] = video_data['评论'].astype("int")
video_data['点赞'] = video_data['点赞'].astype("int")
video_data['硬币'] = video_data['硬币'].astype("int")
video_data['收藏'] = video_data['收藏'].astype("int")
video_data['转发'] = video_data['转发'].astype("int")
video_data['排名'] = video_data['排名'].astype("int")

查看预处理之后的数据格式

video_data.info()

转换后

对标题进行预处理,只保留中文字符

video_data['标题'] = video_data['标题'].str.replace(r'[^\u4e00-\u9fa5]','')#只保留中文

将标题分割成一个个短词

video_data['标题'].fillna(' ',inplace=True) #将空值替换成空格
video_data['标题'] = video_data['标题'].apply(lambda x:' '.join(jieba.cut(x)))#将句子分割成一个个词语
video_data['标题'].head()

处理后的结果

同理处理标签

#同理处理标签
video_data['标签'] = video_data['标签'].str.replace(',','')
video_data['标签'].fillna(' ',inplace=True)
video_data['标签'] = video_data['标签'].apply(lambda x:' '.join(jieba.cut(x)))
video_data['标签'].head()

标签取词结果

将时间信息转化成标准格式

video_data.日期 = pd.to_datetime(video_data.日期.str.findall(r'\d{4}.+').str.get(0)) #将时间进行解析,转化为标准格式
video_data['weekday'] = video_data.日期.dt.weekday #获取星期几
video_data['hour'] = video_data.日期.dt.hour #获取小时

设置一个四舍五入代码

#用于计算三连、弹幕、评论率
def new_round(_float, _len):
    if isinstance(_float, float):
        if str(_float)[::-1].find('.') <= _len:
            return(_float)
        if str(_float)[-1] == '5':
            return(round(float(str(_float)[:-1]+'6'), _len))
        else:
            return(round(_float, _len))
    else:
        return(round(_float, _len))

计算三连等比率

video_data['点赞率']=new_round(video_data.点赞/video_data.播放*100,0)
video_data['硬币率']=new_round(video_data.硬币/video_data.播放*100,0)
video_data['收藏率']=new_round(video_data.收藏/video_data.播放*100,0)
video_data['转发率']=new_round(video_data.转发/video_data.播放*100,0)
video_data['弹幕率']=new_round(video_data.弹幕/video_data.播放*100,0)
video_data['评论率']=new_round(video_data.评论/video_data.播放*100,1)

查看处理后的数据

video_data.head()

处理后的数据

数据分析

查看一共有几位UP主

print('共有{}位UP,分别是'.format(len(video_data['UP'].unique())))#unique是将重复的去除
video_data['UP'].unique()

UP主的数量

统计每个播放量区间的视频数量

# 计算每个播放量区间的视频数量
data_length = len(video_data)
data_length_rate0 = len(video_data[video_data['播放']<10000])
data_length_rate1 = len(video_data[(video_data['播放']>=10000) & (video_data['播放']<100000)])
data_length_rate2 = len(video_data[(video_data['播放']>=100000) & (video_data['播放']<500000)])
data_length_rate3 = len(video_data[(video_data['播放']>=500000) & (video_data['播放']<1000000)])
data_length_rate4 = len(video_data[video_data['播放']>1000000])

结果展示

video_rate = [data_length_rate0,data_length_rate1,data_length_rate2,data_length_rate3,data_length_rate4]
data_view = ['[0,9999]','[10000,99999]','[100000,499999]','[500000,999999]','[1000000,....]']
video_rate

结果查看

画图展示

# 画出饼图
plt.rcParams['font.sans-serif']=['SimHei'] # 中文不乱码
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(10,15))
plt.pie(video_rate,autopct='%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点)
plt.legend(
           data_view,
           fontsize=12,
           title="区间",
           loc="center left",
           bbox_to_anchor=(1, 0.9))
plt.title("播放量占比")
plt.show() 

扇形图展示

只展示一万播放量以上的内容

video_rate = [data_length_rate1,data_length_rate2,data_length_rate3,data_length_rate4]
data_view = ['[10000,99999]','[100000,499999]','[500000,999999]','[1000000,....]']
fig = plt.figure(figsize=(10,15))
plt.pie(video_rate,autopct='%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点)
plt.legend(
           data_view,
           fontsize=12,
           title="区间",
           loc="center left",
           bbox_to_anchor=(1, 0.9))
plt.title("播放量占比")
plt.show() 

一万以上的展示图

统计展示播放量排名前二十的UP主

#统计播放量排名前20的UP
top_20=video_data.sort_values(by=['播放'],ascending=False)[:20]
top_20['UP'].value_counts()

数量展示

前20的具体数据

# 前20的具体数据
top_20[['UP','播放','粉丝']]

前20的视频基本数据

根据UP主分组,对每个UP八月的总播放量进行排序

#根据UP主分组,对每个UP八月的总播放量进行排序
print(video_data.groupby('UP')['播放'].sum().sort_values(ascending=False)[:20])
top_1 = video_data[video_data['UP']=='大霓奈']
print(top_1['UP'].value_counts())
top_1 = video_data[video_data['UP']=='陈师姬']
print(top_1['UP'].value_counts())
top_1 = video_data[video_data['UP']=='17岁反派里的持枪Boy']
print(top_1['UP'].value_counts())

播放量和展示

对每个UP主的弹幕数综合进行排序

# 对每个UP主的弹幕数综合进行排序
video_data.groupby('UP')['弹幕'].sum().sort_values(ascending=False)[:20]

弹幕数展示

评论数展示

# 对每个UP主的评论数综合进行排序
video_data.groupby('UP')['评论'].sum().sort_values(ascending=False)[:20]

评论数展示

视频数量展示

# 对八月份每个UP主发的视频数量进行统计
video_data['UP'].value_counts()[:20]

视频数量展示

对每周不同时间段发布的视频数量进行统计

# 对每周不同时间段发布的视频数量进行统计
fig1, ax1=plt.subplots(figsize=(14,4))
df=video_data.groupby(['hour', 'weekday']).count()['mid'].unstack()
df.plot(ax=ax1, style='-.')
plt.show()

视频数量随时间分布图

# 对每周不同时间的视频播放量进行统计
fig2,ax2=plt.subplots(figsize=(14,4))
df=video_data.groupby(['hour','weekday']).sum()['播放'].unstack()
df.plot(ax=ax2,style='-.')
plt.show()

视频播放量总和随时间分布

# 对每周不同时间段发布的视频播放量大于10000的视频数量进行汇总
view_1 = video_data[video_data['播放']>10000]
fig2,ax2=plt.subplots(figsize=(14,4))
df=view_1.groupby(['hour','weekday']).sum()['mid'].unstack()
df.plot(ax=ax2,style='-.')
plt.show()

大于一万播放量的视频数量随时间分布图

view_2 = video_data[video_data['播放']>100000]
view_3 = video_data[video_data['播放']>1000000]

用词云显示热词

matplotlib.rcParams['font.sans-serif'] = ['KaiTi']#作图的中文
matplotlib.rcParams['font.serif'] = ['KaiTi']#作图的中文
infile = open("D:/stopwords.txt",encoding='utf-8')
stopwords_lst = infile.readlines()
STOPWORDS = [x.strip() for x in stopwords_lst] #去除头尾字符
stopwords = set(STOPWORDS) #设置停用词

def ciyun(texts,mid='all'): #支持指定UP主
    if mid == 'all':
        text = ' '.join(texts)
    else:
        text = ' '.join(texts[video_data['mid']==mid])

    wc = WordCloud(font_path="msyh.ttc", background_color='white', max_words=100, stopwords=stopwords, max_font_size=80, random_state=42, margin=3) #配置词云参数
    wc.generate(text) #生成词云
    plt.imshow(wc,interpolation="bilinear")#作图
    plt.axis("off") #不显示坐标轴

ciyun(video_data['标题'])

热词

大于一万视频的热词

ciyun(view_1['标题'])

热词

大于10万播放视频的热词

ciyun(view_2['标题'])

热词

大于100万播放视频的热词

ciyun(view_3['标题'])

热词

同理查看标签的热词

ciyun(video_data['标签'])

热词

ciyun(view_1['标签'])

热词

ciyun(view_2['标签'])

热词

ciyun(view_3['标签'])

热词

统计标题中包含老师的视频数和播放量综合

# 统计标题中包含老师的视频数和播放量综合
video_teacher = video_data[video_data['标题'].str.contains("老师")]
teacher = [len(video_teacher),video_teacher['播放'].sum()]
teacher

[3022, 16610756]

video_bro = video_data[video_data['标题'].str.contains("兄弟")]
brother = [len(video_bro),video_bro['播放'].sum()]
brother

[1897, 25270292]

video_girlfriend = video_data[video_data['标题'].str.contains("女朋友")]
girlfriend = [len(video_girlfriend),video_girlfriend['播放'].sum()]
girlfriend

[830, 28265224]

# 包含女朋友的标题中包含兄弟的视频信息
fun = video_girlfriend[video_girlfriend['标题'].str.contains("兄弟")].drop_duplicates()
print(len(fun))
print(fun)

女朋友的标题中包含兄弟的视频信息

video_yidan = video_data[video_data['标题'].str.contains("一旦")]
yidan = [len(video_yidan),video_yidan['播放'].sum()]
yidan

[89, 28302099]

video_wubei = video_data[video_data['标题'].str.contains("吾辈")]
wubei = [len(video_wubei),video_wubei['播放'].sum()]
wubei

[318, 35563900]

video_waizui = video_data[video_data['标题'].str.contains("歪嘴")]
waizui = [len(video_waizui),video_waizui['播放'].sum()]
waizui

[1810, 70787655]

查看带有这几个热词标题视频的个数饼状图

video_rate = [teacher[0],brother[0],girlfriend[0],yidan[0],wubei[0],waizui[0]]
data_view = ['老师','兄弟','女朋友','一旦','吾辈','歪嘴']
fig = plt.figure(figsize=(10,15))
plt.pie(video_rate,autopct='%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点)
plt.legend(
           data_view,
           fontsize=12,
           title="区间",
           loc="center left",
           bbox_to_anchor=(1, 0.9))
plt.title("数量占比")
plt.show() 

饼图

带有热刺标题的视频播放量饼图

video_rate = [teacher[1],brother[1],girlfriend[1],yidan[1],wubei[1],waizui[1]]
data_view = ['老师','兄弟','女朋友','一旦','吾辈','歪嘴']
fig = plt.figure(figsize=(10,15))
plt.pie(video_rate,autopct='%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点)
plt.legend(
           data_view,
           fontsize=12,
           title="区间",
           loc="center left",
           bbox_to_anchor=(1, 0.9))
plt.title("播放量占比")
plt.show()

饼图

同理处理标签的

video_bijian = video_data[video_data['标签'].str.contains("必剪")]
bijian = [len(video_bijian),video_bijian['播放'].sum()]
video_fun = video_data[video_data['标签'].str.contains("恶作剧")]
fun = [len(video_fun),video_fun['播放'].sum()]
video_tc = video_data[video_data['标签'].str.contains("吐槽")]
tc = [len(video_tc),video_tc['播放'].sum()]
video_beauty = video_data[video_data['标签'].str.contains("美女")]
beauty = [len(video_beauty),video_beauty['播放'].sum()]
video_wezy = video_data[video_data['标签'].str.contains("万恶之源")]
wezy = [len(video_wezy),video_wezy['播放'].sum()]
video_show = video_data[video_data['标签'].str.contains("表演")]
show = [len(video_show),video_show['播放'].sum()]
video_tuwei = video_data[video_data['标签'].str.contains("土味")]
tuwei = [len(video_tuwei),video_tuwei['播放'].sum()]
video_rate = [bijian[0],fun[0],tc[0],beauty[0],wezy[0],show[0],tuwei[0]]
data_view = ['必剪','恶作剧','吐槽','美女','万恶之源','表演','土味']
fig = plt.figure(figsize=(10,15))
plt.pie(video_rate,autopct='%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点)
plt.legend(
           data_view,
           fontsize=12,
           title="区间",
           loc="center left",
           bbox_to_anchor=(1, 0.9))
plt.title("数量占比")
plt.show() 

饼图

video_rate = [bijian[1],fun[1],tc[1],beauty[1],wezy[1],show[1],tuwei[1]]
data_view = ['必剪','恶作剧','吐槽','美女','万恶之源','表演','土味']
fig = plt.figure(figsize=(10,15))
plt.pie(video_rate,autopct='%1.2f%%') #画饼图(数据,数据对应的标签,百分数保留两位小数点)
plt.legend(
           data_view,
           fontsize=12,
           title="区间",
           loc="center left",
           bbox_to_anchor=(1, 0.9))
plt.title("数量占比")
plt.show() 

饼图

对大于1万播放量的视频三连率等进行排序

# 对大于1万播放量的视频三连率等进行排序
video_rate = video_data[video_data['播放']>10000]
like_20=video_rate.sort_values(by=['点赞率'],ascending=False)[:20]
coin_20=video_rate.sort_values(by=['硬币率'],ascending=False)[:20]
sc_20=video_rate.sort_values(by=['收藏率'],ascending=False)[:20]
share_20=video_rate.sort_values(by=['转发率'],ascending=False)[:20]
danmu_20=video_rate.sort_values(by=['弹幕率'],ascending=False)[:20]
command_20=video_rate.sort_values(by=['评论率'],ascending=False)[:20]
like_20[['标题','播放','UP','点赞','点赞率']]

点赞率前20

coin_20[['标题','播放','UP','硬币','硬币率']]

硬币率前20

sc_20[['标题','播放','UP','收藏','收藏率']]

收藏率前20

share_20[['标题','播放','UP','转发','转发率']]

分享率前20

danmu_20[['标题','播放','UP','弹幕','弹幕率']]

弹幕率前20

command_20[['标题','播放','UP','评论','评论率']]

评论率前20

结论

  • 生活搞笑区的视频中,大部分视频的播放量都集中在10000以下,占了93.86%。
  • 要想获得高播放,则有三条途径:粉丝数、视频质量、视频数量。
  • 每个月大量上传视频获取高播放完全有可能。播放总和最高的两位UP,一个投了154个视频,一个投了528个。
  • 弹幕和评论则是粉丝数多的UP占优势,粉丝黏性高。
  • 八月投放视频最多的UP是老年人诱捕大队队长,一共投放了6932个视频。
  • 视频主要集中在10:00-24:00投放,这个区间的播放总和也是最高。
  • 八月热词主要是龙王,节日相关,哔哩哔哩活动以及相关的UP主。
  • 哔哩哔哩相关活动热词视频播放量普遍较低,UP相关的和月度梗相关的播放量收益最好。
  • 三连率、弹幕率、转发率、评论率对视频播放量的影响不大。

资料

[button color="success" icon="glyphicon glyphicon-save" url="https://github.com/ndmiao/bilibili-data/" type=""]代码和数据[/button]

]]>
0 /index.php/archives/186/#comments /index.php/feed/archives/186/
哔哩哔哩分区视频详细信息爬取(三连、播放量、标签)等(第三版) /index.php/archives/146/ /index.php/archives/146/ Thu, 03 Sep 2020 15:39:00 +0800 南岛鹋

因为上手练习一个自己的数据分析项目,因此需要爬取数据。经历过两个版本的更新后,终于写出了第三版。期间也学会了selenium库的运用,API接口的调用,IP池等。

确定目标

因为想要一个量大的数据集,因此没有考虑热榜排名,因为所有区加起来也才一千左右。全部视频信息的话技术不行,然后就盯上了分区榜。

分区榜

从这个榜单可以选择时间段,可以根据每个月的视频热度排名等信息,来分析月度热点,哪些视频更加容易火,以及各种因素对视频播放量的影响。虽然只是一个小分区月度热度排名,并不包含全部视频,但是数据量也是极大的。下图可以看到接近有23万条数据。

数据量

网站分析

这里存在一个难点,就是虽然浏览器上是可以查看网页源码,并且包含了视频的相关信息,但是用requests请求之后的网页源码却并没有相关的信息。因此前两个版本,我采用了selenium库的方法来获取信息,但是这个方法有一个缺点,速度慢(因为要跟浏览器一样加载整个页面信息)、信息少(只有标题、作者、视频简介、以及视频页和个人主页网址),很麻烦。于是这次我换成了API调用的方法。

页面分析

我们选择一个具体的数字来查找,可以发现搜索出来一个search的接口。

页面分析

点进去之后,可以发现里面的result共有20条数据,刚好对应着每页20个视频。

数据分析

可以看到里面包含了作者、标题、标签、播放等一系列数据。

接口为https://s.search.bilibili.com/cate/search?callback=main_ver=v3&search_type=video&view_type=hot_rank&order=click©_right=-1&cate_id=138&page=1&pagesize=20&jsonp=jsonp&time_from=20200801&time_to=20200831 ,view_type为排行类型,page为页面数,pagesize为页面最大的视频数,上限好像是100。最后面就是时间了。

但是我还需要三连数据以及UP主的粉丝量。同理分析

得到三连的API接口:https://api.bilibili.com/x/web-interface/archive/stat?aid=371876135

其中aid由BV转换。

粉丝数为https://api.bilibili.com/x/relation/stat?vmid=32172331

mid可以在第一个接口处获取。

IP池

这时候虽然已经可以开始爬取了,但是如果数据量稍微有一点大,访问稍微有点频繁,就会导致IP被屏蔽。

这时候我们就需要用到代理IP,免费的代理IP虽然也有,而且GITHUB上也有专门的项目来建立代理IP池。但是免费的终究很麻烦,于是我选择了日租独享的IP。http://www.xdaili.cn/

代码

# coding: utf-8
# Author:南岛鹋 
# Blog: www.ndmiao.cn
# Date :2020/8/25 10:29
# Tool :PyCharm

import requests
import csv
import json
import random
import time


class video_data:
    def __init__(self):
        self.url = 'https://s.search.bilibili.com/cate/search?main_ver=v3&search_type=video&view_type=hot_rank&order=click&copy_right=-1&cate_id=138&page={}&pagesize=20&jsonp=jsonp&time_from=20200801&time_to=20200831'
        self.page = 11507
        self.alphabet = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'

    def dec(self, x):  # BV号转换成AV号
        r = 0
        for i, v in enumerate([11, 10, 3, 8, 4, 6]):
            r += self.alphabet.find(x[v]) * 58 ** i
        return (r - 0x2_0840_07c0) ^ 0x0a93_b324

    def random_headers(self, path): # 随机读取一个头信息
        with open(path, 'r') as f:
            data = f.readlines()
            f.close()

        reg = []
        for i in data:
            k = eval(i)  # 将字符串转化为字典形式
            reg.append(k)
        header = random.choice(reg)
        return header

    def get_ip(self): # 代理IP获取
        print('切换IP中.......')
        url = '代理IP的地址'
        ip = requests.get(url).text
        if ip in ['{"ERRORCODE":"10055","RESULT":"提取太频繁,请按规定频率提取!"}', '{"ERRORCODE":"10098","RESULT":"可用机器数量不足"}']: # 出现频繁或者机器不足,睡眠14秒
            time.sleep(14)
            ip = requests.get(url).text
            print(ip)
        else:
            print(ip)
        proxies = {
            'https': 'http://' + ip,
            'http': 'http://' + ip
        } # 设置https和http可以按需选择
        return proxies

    def get_requests(self, url, proxy): # 请求的函数
        headers = self.random_headers('headers.txt')
        # 将头信息和IP写入,用try来减少意外对程序的影响
        try: 
            response = requests.get(url, timeout=3, headers=headers, proxies=proxy)
        except requests.exceptions.RequestException as e:
            print(e)
            proxy = self.get_ip()
            try:
                response = requests.get(url, timeout=3, headers=headers, proxies=proxy)
            except requests.exceptions.RequestException as e:
                print(e)
                print('原始IP')
                response = requests.get(url, timeout=3, headers=headers)
        return response, proxy

    def get_follower(self, mid, proxy): # 获取粉丝数
        url = 'https://api.bilibili.com/x/relation/stat?vmid=' + str(mid)
        r, proxy = self.get_requests(url, proxy)
        result = json.loads(r.text) # 用json来解析文本
        # 按照需求获取需要的数据,因为粉丝数是必定存在的,所以失败了需要多次尝试获取。
        try:
            follower = result['data']['follower']
        except:
            follower,proxy = self.get_follower(mid, proxy)
        return follower, proxy

    def get_view(self, BV, proxy): # 获取三连和播放
        aid = self.dec(BV)
        url = 'https://api.bilibili.com/x/web-interface/archive/stat?aid=' + str(aid)
        r, proxy = self.get_requests(url, proxy)
        result = json.loads(r.text)
        view = {}# 因为视频虽然在排行榜,但是很可能已经删除,所以没有数据为None
        try:
            view['view'] = result['data']['view']
            view['danmu'] = result['data']['danmaku']
            view['reply'] = result['data']['reply']
            view['like'] = result['data']['like']
            view['coin'] = result['data']['coin']
            view['favorite'] = result['data']['favorite']
            view['share'] = result['data']['share']
            view['rank'] = result['data']['his_rank']
        except:
            view['view'] = 'None'
            view['danmu'] = 'None'
            view['reply'] = 'None'
            view['like'] = 'None'
            view['coin'] = 'None'
            view['favorite'] = 'None'
            view['share'] = 'None'
            view['rank'] = 'None'
        return view, proxy

    def get_parse(self, result, proxy): # 整合数据
        content = []
        items = result['result']
        for item in items:
            pubdate = item['pubdate']
            title = item['title']
            author = item['author']
            bvid = item['bvid']
            mid = item['mid']
            follower, proxy = self.get_follower(mid, proxy)
            video_view, proxy = self.get_view(bvid, proxy)
            view = video_view['view']
            danmu = video_view['danmu']
            reply = video_view['reply']
            like = video_view['like']
            coin = video_view['coin']
            favorite = video_view['favorite']
            share = video_view['share']
            rank = video_view['rank']
            tag = item['tag']
            con = [pubdate, title, author, bvid, mid, follower,view,danmu,reply,like,coin,favorite,share,rank, tag]
            content.append(con)
            print(con)
        print(content)
        self.save(content)
        return proxy

    def write_header(self):
        header = ['日期', '标题', '作者', 'BV', 'mid', '粉丝', '播放', '弹幕', '评论', '点赞','硬币','收藏','转发','排名','标签']
        with open('fun_video.csv', 'a', encoding='gb18030', newline='')as f:
            write = csv.writer(f)
            write.writerow(header)

    def save(self,content):# 存入csv
        with open('fun_video.csv', 'a', encoding='gb18030', newline='')as file:
            write = csv.writer(file)
            write.writerows(content)

    def run(self):
        #self.write_header()
        proxy = self.get_ip()
        for i in range(168, self.page):
            url = self.url.format(i)
            response, proxy = self.get_requests(url, proxy)
            result = json.loads(response.text)
            proxy = self.get_parse(result, proxy)
            print('第{}页爬取完毕'.format(i))


if __name__ == '__main__':
    video = video_data()
    video.run()
]]>
0 /index.php/archives/146/#comments /index.php/feed/archives/146/
哔哩哔哩视频页视频详细信息采集(三连、播放量、标签)(第二版) /index.php/archives/140/ /index.php/archives/140/ Wed, 02 Sep 2020 12:44:00 +0800 南岛鹋

在第一版的基础上进行了改进,增加了IP代理的功能。但是在selenium模块来爬取的时候,增加ip代理也不太可靠,需要优质的IP才能保证流畅的进行。

# coding: utf-8
# Author:南岛鹋 
# Blog: www.ndmiao.cn
# Date :2020/8/24 14:35
# Tool :PyCharm

from selenium import webdriver
import re
import csv
import time
import random
class Bilibili_data:
    def __init__(self):
        self.start_url="https://www.bilibili.com/"
        self.driver=webdriver.Chrome() # 调用谷歌浏览器
    def get_item_list(self):
        list = self.driver.find_elements_by_xpath("//*[@id='primaryChannelMenu']/*/*/*/span")
        item_list = []
        i = 0
        for element in list:
            item = {}
            str = re.sub("[A-Za-z0-9\!\%\[\]\+\。]", "", element.text)
            item["str"] = str
            item["url"] = element.find_element_by_xpath("./..")
            item_list.append(item)
            i = i+1
            if i == 15:
                break
        return item_list

    def get_item_detail(self,url):
        url.click()
        list = self.driver.find_elements_by_xpath("//ul[@class='clearfix']/*[position()>1]/*")
        i = 0
        item_detail = []
        for element in list:
            item = {}
            item["str"] = str(i) + ':' + element.text
            i = i + 1
            item["url"] = element
            item_detail.append(item)
        return item_detail

    def choose_time(self,url):
        url_last = "#/all/{}/0/1/{}"
        item = ['click', 'scores', 'stow', 'coin', 'dm']
        cn_item = ['播放数','评论数','收藏数','硬币数','弹幕数']
        num = 0
        for i in cn_item:
            print(str(num) + ':' + i)
            num = num+1
        item_choice = int(input('请输入你选择的排序:'))
        time_choice = input('请输入时间段(例如 2020-01-01,2020-01-07):')
        url = url + url_last.format(item[item_choice],time_choice)
        self.driver.get(url)

    def get_content_list(self):
        li_list = self.driver.find_elements_by_xpath("//ul[@class='vd-list mod-2']/li")
        content_list = []
        for li in li_list:
            video_detail = {}
            video_detail['title'] = li.find_element_by_xpath(".//div[@class='r']/a").text
            video_detail['author'] = li.find_element_by_xpath(".//div[@class='up-info']/a").text
            video_detail['href'] = li.find_element_by_xpath(".//div[@class='r']/a").get_attribute("href")
            author_href = li.find_element_by_xpath(".//div[@class='up-info']/a").get_attribute("href")
            video_detail['mid'] = re.findall(r'\d+',author_href)[0]
            content_list.append(video_detail)
        print(content_list)
        next_url = self.driver.find_elements_by_xpath("//button[@class='nav-btn iconfont icon-arrowdown3']")
        next_url = next_url[0] if len(next_url) > 0 else None
        return content_list,next_url

    def save_content_list(self,content_list):
        header = ['title','author','href','mid']
        with open('video.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writerows(content_list)  # 写入数据

    def random_sleep(self,mu=3, sigma=0.4):
        '''正态分布随机睡眠

        :param mu: 平均值
        :param sigma: 标准差,决定波动范围
        '''
        secs = random.normalvariate(mu, sigma)
        if secs <= 0:
            secs = mu  # 太小则重置为平均值
        time.sleep(secs)

    def run(self):
        header = ['title','author','href','mid']
        with open('video.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writeheader()  # 写入列名
        self.driver.get(self.start_url)
        list = self.get_item_list()
        num = 0
        for i in list:
            print(str(num) + ':' + i['str'])
            num = num+1
        choice1 = int(input("请输入你选择的分区:"))
        item_detail = self.get_item_detail(list[choice1]['url'])
        for detail in item_detail:
            print(detail['str'])
        choice2 = int(input("请输入你选择的分类:"))
        url_detail = item_detail[choice2]['url'].get_attribute("href")
        self.choose_time(url_detail)
        content_list,next_url = self.get_content_list()
        self.save_content_list(content_list)
        while next_url is not None:
            next_url.click()
            self.random_sleep()
            content_list, next_url=self.get_content_list()
            self.save_content_list(content_list)
        self.driver.quit()

if __name__=="__main__":
    data_get=Bilibili_data()
    data_get.run()
# coding: utf-8
# Author:南岛鹋 
# Blog: www.ndmiao.cn
# Date :2020/8/25 10:29
# Tool :PyCharm

import requests
import json
from bs4 import BeautifulSoup
import re
import bs4
import csv
import random
import time
class detail_data:
    def __init__(self):
        # 爬虫地址
        self.alphabet = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'

    def dec(self, x):  # BV号转换成AV号
        r = 0
        for i, v in enumerate([11, 10, 3, 8, 4, 6]):
            r += self.alphabet.find(x[v]) * 58 ** i
        return (r - 0x2_0840_07c0) ^ 0x0a93_b324

    def url_deal(self, url): 
        url = url[-12:]  #取后面的BV号
        return url

    def random_headers(self, path):# 随机读取一个信息头
        with open(path, 'r') as f:
            data = f.readlines()
            f.close()

        reg = []
        for i in data:
            k = eval(i)  # 将字符串转化为字典形式
            reg.append(k)
        header = random.choice(reg)
        return header

    def save_content_list(self,video_dict):
        header = ['title', 'author', 'href', 'bvid', 'view', 'danmu', 'reply_num', 'like_num', 'coin_num', 'favorite_num','share_num', 'follow-num', 'video_type', 'video_time', 'video_rank', 'video_tag']
        with open('video_data.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writerow(video_dict)  # 写入数据

    def get_ip(self):
        print('切换IP中.......')
        url = '填入获取IP的接口'
        ip = requests.get(url).text
        if ip in ['{"ERRORCODE":"10055","RESULT":"提取太频繁,请按规定频率提取!"}','{"ERRORCODE":"10098","RESULT":"可用机器数量不足"}']:
            time.sleep(7.5)
            ip = requests.get(url).text
            print(ip)
        else:
            print(ip)
        proxies = {
            'https': 'http://' + ip,
            'http': 'http://' + ip
        }
        return proxies

    def get_time(self, url,proxy):
        headers = self.random_headers('headers.txt')
        try:
            r = requests.get(url,timeout=3,headers=headers,proxies =proxy)
        except requests.exceptions.RequestException as e:
            print(e)
            try:
                r = requests.get(url,timeout=5,headers=headers)
                proxy = self.get_ip()
            except requests.exceptions.RequestException as e:
                print(e)
                return ['None','None','None','None'],proxy
        print(1)
        soup = BeautifulSoup(r.text, "html.parser")
        result = soup.find(class_='video-data')
        try:
            result2 = soup.find(class_='default-btn follow-btn b-gz not-follow').find('span').find('span')
            followers = result2.text
        except:
            followers = 'none'
        timedata = []
        timedata.append(followers)
        try:
            for i in result:
                if type(i) == bs4.element.Tag:
                    timedata.append(re.sub('\s', ' ', i.text))
                else:
                    timedata.append('None')
        except:
            return ['None','None','None','None'],proxy
        return timedata,proxy

    def get_view(self,BV,url_video,proxy,title,author):
        bid = BV
        aid = self.dec(bid)
        url = r'https://api.bilibili.com/x/web-interface/archive/stat?aid=' + str(aid)
        url2 = r'https://api.bilibili.com/x/web-interface/view/detail/tag?aid=' + str(aid)
        headers = self.random_headers('headers.txt')
        timedata, proxy = self.get_time(url_video, proxy)
        try:
            response = requests.get(url, timeout=3, headers=headers, proxies=proxy)
        except requests.exceptions.RequestException as e:
            print(e)
            try:
                response = requests.get(url, timeout=5, headers=headers)
                proxy=self.get_ip()
            except requests.exceptions.RequestException as e:
                print(e)
        print(2)
        headers2 = self.random_headers('headers.txt')
        try:
            response2 = requests.get(url2, timeout=3, headers=headers2, proxies=proxy)
        except requests.exceptions.RequestException as e:
            print(e)
            try:
                response2 = requests.get(url2, timeout=5, headers=headers2)
                proxy = self.get_ip()
            except requests.exceptions.RequestException as e:
                print(e)
        print(3)
        text = response.text
        text2 = response2.text
        jsonobj = json.loads(text)
        jsonobj2 = json.loads(text2)
        video_tags = ''

        # 从Json对象获取视频基本信息并转入词典中
        try:
            for tags in jsonobj2['data']:
                video_tags = video_tags + tags['tag_name'] + ' '
            video_dict = {'title': title,
                          'author': author,
                          'href': url_video,
                          'bvid': BV,
                          'view': jsonobj['data']['view'],
                          'danmu': jsonobj['data']['danmaku'],
                          'reply_num': jsonobj['data']['reply'],
                          'like_num': jsonobj['data']['like'],
                          'coin_num': jsonobj['data']['coin'],
                          'favorite_num': jsonobj['data']['favorite'],
                          'share_num': jsonobj['data']['share'],
                          'follow-num': timedata[0],
                          'video_type': timedata[1],
                          'video_time': timedata[2],
                          'video_rank': timedata[3],
                          'video_tag': video_tags
                          }
        except:
            video_dict = {'title': title,
                          'author': author,
                          'href': url_video,
                          'bvid': 'None',
                          'view': 'None',
                          'danmu': 'None',
                          'reply_num': 'None',
                          'like_num': 'None',
                          'coin_num': 'None',
                          'favorite_num': 'None',
                          'share_num': 'None',
                          'follow-num': 'None',
                          'video_type': 'None',
                          'video_time': 'None',
                          'video_rank': 'None',
                          'video_tag': 'None'
                          }
            return video_dict, proxy

        return video_dict, proxy

    def run(self):
        header = ['title', 'author', 'href', 'bvid', 'view', 'danmu', 'reply_num', 'like_num', 'coin_num',
                  'favorite_num', 'share_num', 'follow-num', 'video_type', 'video_time', 'video_rank', 'video_tag']
        proxy = self.get_ip()
        with open('video_data.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writeheader()  # 写入列名
        with open(r'video.csv', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                url = row['href']
                BV = self.url_deal(url)
                video_dict,proxy = self.get_view(BV, url,proxy,row['title'],row['author'])
                a = 1
                if video_dict['bvid'] == 'None' and a==1:
                    proxy = self.get_ip()
                    video_dict, proxy = self.get_view(BV, url, proxy, row['title'], row['author'])
                    a = a+1
                print(video_dict)
                self.save_content_list(video_dict)

if __name__ == '__main__':
    video = detail_data()
    video.run()
]]>
0 /index.php/archives/140/#comments /index.php/feed/archives/140/
哔哩哔哩视频页视频详细信息采集(三连、播放量、标签)(第一版) /index.php/archives/139/ /index.php/archives/139/ Sun, 30 Aug 2020 16:19:00 +0800 南岛鹋

第一次尝试用爬虫爬取每个分区热度排名视频信息,然后进行数据分析,但是哔哩哔哩的API接口很奇怪,我用了付费代理IP也没有一个能够访问。很烦,所以想用这篇文章来爬取的话,就需要调慢速度来爬了,几百条数据还好,上万条就太太太慢了。

from selenium import webdriver
import re
import csv
import time
class Bilibili_data:
    def __init__(self):
        self.start_url="https://www.bilibili.com/"
        self.driver=webdriver.Chrome()
    def get_item_list(self):
        list = self.driver.find_elements_by_xpath("//*[@id='primaryChannelMenu']/*/*/*/span")
        item_list = []
        i = 0
        for element in list:
            item = {}
            str = re.sub("[A-Za-z0-9\!\%\[\]\+\。]", "", element.text)
            item["str"] = str
            item["url"] = element.find_element_by_xpath("./..")
            item_list.append(item)
            i = i+1
            if i == 15:
                break
        return item_list
    def get_item_detail(self,url):
        url.click()
        list = self.driver.find_elements_by_xpath("//ul[@class='clearfix']/*[position()>1]/*")
        i = 0
        item_detail = []
        for element in list:
            item = {}
            item["str"] = str(i) + ':' + element.text
            i = i + 1
            item["url"] = element
            item_detail.append(item)
        return item_detail

    def choose_time(self,url):
        url_last = "#/all/{}/0/1/{}"
        item = ['click', 'scores', 'stow', 'coin', 'dm']
        cn_item = ['播放数','评论数','收藏数','硬币数','弹幕数']
        num = 0
        for i in cn_item:
            print(str(num) + ':' + i)
            num = num+1
        item_choice = int(input('请输入你选择的排序:'))
        time_choice = input('请输入时间段(例如 2020-01-01,2020-01-07):')
        url = url + url_last.format(item[item_choice],time_choice)
        self.driver.get(url)

    def get_content_list(self):
        li_list = self.driver.find_elements_by_xpath("//ul[@class='vd-list mod-2']/li")
        content_list = []
        for li in li_list:
            video_detail = {}
            video_detail['title'] = li.find_element_by_xpath(".//div[@class='r']/a").text
            video_detail['author'] = li.find_element_by_xpath(".//div[@class='up-info']/a").text
            video_detail['href'] = li.find_element_by_xpath(".//div[@class='r']/a").get_attribute("href")
            content_list.append(video_detail)
        print(content_list)
        next_url = self.driver.find_elements_by_xpath("//button[@class='nav-btn iconfont icon-arrowdown3']")
        next_url = next_url[0] if len(next_url) > 0 else None
        return content_list,next_url

    def save_content_list(self,content_list):
        header = ['title','author','href','bvid','view','danmu','reply_num','like_num','coin_num','favorite_num','share_num','video_type','video_time','video_rank','video_tag']
        with open('video.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writerows(content_list)  # 写入数据

    def run(self):
        header = ['title','author','href','bvid','view','danmu','reply_num','like_num','coin_num','favorite_num','share_num','video_type','video_time','video_rank','video_tag']
        with open('video.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writeheader()  # 写入列名
        self.driver.get(self.start_url)
        list = self.get_item_list()
        num = 0
        for i in list:
            print(str(num) + ':' + i['str'])
            num = num+1
        choice1 = int(input("请输入你选择的分区:"))
        item_detail = self.get_item_detail(list[choice1]['url'])
        for detail in item_detail:
            print(detail['str'])
        choice2 = int(input("请输入你选择的分类:"))
        url_detail = item_detail[choice2]['url'].get_attribute("href")
        self.choose_time(url_detail)
        content_list,next_url = self.get_content_list()
        self.save_content_list(content_list)
        while next_url is not None:
            next_url.click()
            time.sleep(3)
            content_list, next_url=self.get_content_list()
            self.save_content_list(content_list)
        self.driver.quit()


if __name__=="__main__":
    data_get=Bilibili_data()
    data_get.run()
import requests
import json
from bs4 import BeautifulSoup
import re
import bs4
import time
import csv
import random
class detail_data:

    def __init__(self):
        # 爬虫地址
        self.alphabet = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'

    def dec(self, x):  # BV号转换成AV号
        r = 0
        for i, v in enumerate([11, 10, 3, 8, 4, 6]):
            r += self.alphabet.find(x[v]) * 58 ** i
        return (r - 0x2_0840_07c0) ^ 0x0a93_b324

    def url_deal(self, url):
        url = url[-12:]
        return url

    def get_time(self, url):
        # proxy = self.random_data('ip.txt')
        headers = self.random_data('headers.txt')
        r = requests.get(url,timeout=30,headers=headers)
        r.raise_for_status()
        soup = BeautifulSoup(r.text, "html.parser")
        result = soup.find(class_='video-data')
        timedata = []
        for i in result:
            if type(i) == bs4.element.Tag:
                timedata.append(re.sub('\s', ' ', i.text))
            else:
                timedata.append('None')
        return timedata

    def random_data(self, path):
        with open(path, 'r') as f:  # 这里打开的就是你之前爬取ip保存的地址
            data = f.readlines()
            f.close()

        reg = []
        for i in data:
            k = eval(i)  # 将字符串转化为字典形式
            reg.append(k)
        ip = random.choice(reg)  # 随机从reg这个存储了多个ip的list 里返回一个ip地址
        return ip

    def save_content_list(self,video_dict):
        header = ['title', 'author', 'href', 'bvid', 'view', 'danmu', 'reply_num', 'like_num', 'coin_num', 'favorite_num','share_num', 'video_type', 'video_time', 'video_rank', 'video_tag']
        with open('video_data.csv', 'a', newline='', encoding='utf-8') as f:
            writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
            writer.writerow(video_dict)  # 写入数据


    def run(self, BV, url_video,title,author):
        bid = BV
        aid = self.dec(bid)
        url = r'https://api.bilibili.com/x/web-interface/archive/stat?aid=' + str(aid)
        # 携带cookie进行访问
        url2 = r'https://api.bilibili.com/x/web-interface/view/detail/tag?aid=' + str(aid)
        # proxy = self.random_data('ip.txt')
        # proxy2 = self.random_data('ip.txt')
        headers = self.random_data('headers.txt')
        response = requests.get(url, timeout=30, headers=headers)
        headers2 = self.random_data('headers.txt')
        response2 = requests.get(url2, timeout=30, headers=headers2)
        time.sleep(3)
        if response.status_code == 200:
            text = response.text
            text2 = response2.text
            jsonobj = json.loads(text)
            jsonobj2 = json.loads(text2)
            video_tags=''
            for tags in jsonobj2['data']:
                video_tags=video_tags+tags['tag_name']+' '
            timedata = self.get_time(url_video)
            # 从Json对象获取视频基本信息并转入词典中
            video_dict = {'title': title,
                          'author': author,
                          'href': url_video,
                          'bvid': jsonobj['data']['bvid'],
                          'view': jsonobj['data']['view'],
                          'danmu': jsonobj['data']['danmaku'],
                          'reply_num': jsonobj['data']['reply'],
                          'like_num': jsonobj['data']['like'],
                          'coin_num': jsonobj['data']['coin'],
                          'favorite_num': jsonobj['data']['favorite'],
                          'share_num': jsonobj['data']['share'],
                          'video_type': timedata[0],
                          'video_time': timedata[1],
                          'video_rank': timedata[2],
                          'video_tag': video_tags
                          }
            return video_dict



if __name__ == '__main__':
    Detail_data = detail_data()
    header = ['title', 'author', 'href', 'bvid', 'view', 'danmu', 'reply_num', 'like_num', 'coin_num', 'favorite_num','share_num', 'video_type', 'video_time', 'video_rank', 'video_tag']
    with open('video_data.csv', 'a', newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames=header)  # 提前预览列名,当下面代码写入数据时,会将其一一对应。
        writer.writeheader()  # 写入列名
    with open(r'video.csv', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            url = row['href']
            BV = Detail_data.url_deal(url)
            video_dict = Detail_data.run(BV, url,row['title'],row['author'])
            print(video_dict)
            Detail_data.save_content_list(video_dict)
]]>
0 /index.php/archives/139/#comments /index.php/feed/archives/139/
selenium库的安装以及安装出现的问题的解决方法和简单使用 /index.php/archives/138/ /index.php/archives/138/ Mon, 24 Aug 2020 17:22:00 +0800 南岛鹋

在想要爬虫实现哔哩哔哩鬼畜区热门榜的数据进行数据分析,但是自己的技术不够,对js也一窍不通。上网后发现了selenium的一个库,觉得挺强大的,但是安装过程中出现了一些小问题,于是写下文章记录一下。

安装

像其他的库一样安装

pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple

错误

错误类型以及截图

运行的过程中出现了selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH.的错误。

错误截图

解决方法

查阅资料后了解到因为这个库是模拟客户端对浏览器的操作,从而来爬取资源的。出错的原因是由于相应浏览器的驱动版本不一样。

因为我用的是谷歌浏览器,所以首先需要查看其版本号,输入chrome://version/即可查看

版本号查看

然后进入http://chromedriver.storage.googleapis.com/index.html ,找到相应的版本

版本

然后选择自己的系统下载

下载

解压后获得chromedriver.exe,我们需要复制到两个目录下

  • C:Program Files (x86)GoogleChromeApplication
  • 安装python的目录

然后就可以正常使用了。

库的简单使用

1.调用

首先需要声明并调用谷歌浏览器,其它浏览器同理。

from selenium import webdriver
browser = webdriver.Chrome()

2.访问页面

加上下方代码,就会实现自动访问百度页面,然后返回百度页面原代码的功能。

url = 'https:www.baidu.com'
browser.get(url)#打开浏览器预设网址
print(browser.page_source)#打印网页源代码
browser.close()#关闭浏览器

其他的详细操作可以推荐查看这篇教程http://www.python3.vip/tut/auto/selenium/01/

]]>
0 /index.php/archives/138/#comments /index.php/feed/archives/138/
2012美国总统竞选赞助数据分析项目学习 /index.php/archives/122/ /index.php/archives/122/ Sat, 22 Aug 2020 15:36:00 +0800 南岛鹋

项目目的:通过对2012美国总统竞选赞助数据分析,得出哪个总统更具有优势的结论
项目流程:数据载入、数据清洗、数据聚合分组以及可视化
项目地址:https://tianchi.aliyun.com/notebook-ai/detail?postId=10585

一、导入数据

1.数据载入

导入需要用到的库numpy、pandas、matplotlib.pyplot

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

pd.read_scv来导入存储在csv中的数据。

data_01 = pd.read_csv('D:\mycode\python\study\data\data_01.csv')#用pd.read_csv读取数据
data_02 = pd.read_csv('D:\mycode\python\study\data\data_02.csv')
data_03 = pd.read_csv('D:\mycode\python\study\data\data_03.csv')

2.数据合并

pd.concat对数据进行拼接。

data = pd.concat([data_01, data_02, data_03])#可以将三份数据拼在一起

head()函数对数据前五行进行查看

data.head()#查看前五行数据

前五行数据

数据各字段的含义:

  • cand_nm – 接受捐赠的候选人姓名
  • contbr_nm – 捐赠人姓名
  • contbr_st – 捐赠人所在州
  • contbr_employer – 捐赠人所在公司
  • contbr_occupation – 捐赠人职业
  • contb_receipt_amt – 捐赠数额(美元)
  • contb_receipt_dt – 收到捐款的日期

info()对数据进行数据类型的查看

data.info()#查看数据的信息,每个字段的名称、非空数量、数据的类型
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1001733 entries, 0 to 1730
Data columns (total 7 columns):
 #   Column             Non-Null Count    Dtype  
---  ------             --------------    -----  
 0   cand_nm            1001733 non-null  object 
 1   contbr_nm          1001733 non-null  object 
 2   contbr_st          1001729 non-null  object 
 3   contbr_employer    988004 non-null   object 
 4   contbr_occupation  993303 non-null   object 
 5   contb_receipt_amt  1001733 non-null  float64
 6   contb_receipt_dt   1001733 non-null  object 
dtypes: float64(1), object(6)
memory usage: 61.1+ MB

分析数据信息可知捐赠人职业和公司的两个数据有很大一部分的缺失。

describe()可以得知统计通用的概述值。

data.describe() #用统计学指标反应数据的概况,count非空数据、mean平均值、std标准差,后面为四分位数

统计概述

二、数据清洗

1.缺失值处理

fillna函数可以将数据中的NULL值转化为自己想要的值,其中inplace的值决定了直接覆盖还是创建副本。

data['contbr_employer'].fillna('NOT PROVIDED',inplace = True)#将数据集中的NULL用NOT PROVIDED代替,True代表直接修改原对象
data['contbr_occupation'].fillna('NOT PROVIDED',inplace = True)

2.数据转换

unique()函数是去重函数,其中format函数是将数值传入{}之中。

print('共有{}位候选人,分别是'.format(len(data['cand_nm'].unique())))#unique是将重复的去除
data['cand_nm'].unique()

十三位候选人

因为有些候选人是来自于同一个党派的,所以可以新增一列党派。

#通过搜索引擎等途径,获取到每个总统候选人的所属党派,建立字典parties,候选人名字作为键,所属党派作为对应的值
parties = {'Bachmann, Michelle': 'Republican',
           'Cain, Herman': 'Republican',
           'Gingrich, Newt': 'Republican',
           'Huntsman, Jon': 'Republican',
           'Johnson, Gary Earl': 'Republican',
           'McCotter, Thaddeus G': 'Republican',
           'Obama, Barack': 'Democrat',
           'Paul, Ron': 'Republican',
           'Pawlenty, Timothy': 'Republican',
           'Perry, Rick': 'Republican',
           "Roemer, Charles E. 'Buddy' III": 'Republican',
           'Romney, Mitt': 'Republican',
           'Santorum, Rick': 'Republican'}

通过候选人的姓名为key值,用map()查询字典获得其党派存入党派列。通过value_counts对候选人所属党派进行统计。

data['party'] = data['cand_nm'].map(parties)#在data中增加一列存储党派,用map查询parties
data['party'].value_counts()#对党派的数据进行统计

党派

将数据根据捐赠人的职业进行划分,统计每个职业的捐赠数额,ascending=False表示为倒序。

#根据职业对数据进行
data.groupby('contbr_occupation')['contb_receipt_amt'].sum().sort_values(ascending=False)[:20]
contbr_occupation
RETIRED                                   48176647.00
ATTORNEY                                  18470473.30
HOMEMAKER                                 17484807.65
INFORMATION REQUESTED PER BEST EFFORTS    15859514.55
INFORMATION REQUESTED                      8742357.59
PHYSICIAN                                  7224044.40
PRESIDENT                                  6347843.59
EXECUTIVE                                  5273717.90
CONSULTANT                                 4932627.98
NOT PROVIDED                               4224760.39
CEO                                        3570942.20
LAWYER                                     3537982.19
OWNER                                      3278488.16
INVESTOR                                   3204481.92
ENGINEER                                   2730527.43
PROFESSOR                                  2458033.81
C.E.O.                                     2433218.11
SELF-EMPLOYED                              2259150.94
MANAGER                                    2167571.47
REAL ESTATE                                2110499.34
Name: contb_receipt_amt, dtype: float64

因为一些职业的名称不一样,但是却是同一个职业,所以对data中的数据进行循环去重。

occupation_map = {
  'INFORMATION REQUESTED PER BEST EFFORTS':'NOT PROVIDED',
  'INFORMATION REQUESTED':'NOT PROVIDED',
  'SELF' : 'SELF-EMPLOYED',
  'SELF EMPLOYED' : 'SELF-EMPLOYED',
  'C.E.O.':'CEO',
  'LAWYER':'ATTORNEY',
}
f = lambda x: occupation_map.get(x,x)
data.contbr_occupation = data.contbr_occupation.map(f)#对职业信息进行查询,进行去重

对雇主的数据进行类似的处理。

emp_mapping = {
   'INFORMATION REQUESTED PER BEST EFFORTS' : 'NOT PROVIDED',
   'INFORMATION REQUESTED' : 'NOT PROVIDED',
   'SELF' : 'SELF-EMPLOYED',
   'SELF EMPLOYED' : 'SELF-EMPLOYED',
}
f = lambda x: emp_mapping.get(x,x)
data.contbr_employer = data.contbr_employer.map(f)

3.数据筛选

因为赞助还包括退款(负的),所以为了简化过程,只需要为正的

data = data[data['contb_receipt_amt']>0]

根据捐助人的姓名分组,进行金额的总和,倒叙排序

data.groupby('cand_nm')['contb_receipt_amt'].sum().sort_values(ascending = False)

从下方输出的结果来看,obama和romney的支持率是最高的,因此选择这两个来分析

cand_nm
Obama, Barack                     1.358776e+08
Romney, Mitt                      8.833591e+07
Paul, Ron                         2.100962e+07
Perry, Rick                       2.030675e+07
Gingrich, Newt                    1.283277e+07
Santorum, Rick                    1.104316e+07
Cain, Herman                      7.101082e+06
Pawlenty, Timothy                 6.004819e+06
Huntsman, Jon                     3.330373e+06
Bachmann, Michelle                2.711439e+06
Johnson, Gary Earl                5.669616e+05
Roemer, Charles E. 'Buddy' III    3.730099e+05
McCotter, Thaddeus G              3.903000e+04
Name: contb_receipt_amt, dtype: float64

将两个人的数据组合成一个新的数据集

data_vs = data[data['cand_nm'].isin(['Obama, Barack','Romney, Mitt'])].copy()
#将最主要的两个竞争人的数据集进行复制

4.面元化数据

对两个数据中不同的捐助金额,分别打上标签

bins = np.array([0,1,10,100,1000,10000,100000,1000000,10000000])
labels = pd.cut(data_vs['contb_receipt_amt'],bins)
labels#根据捐助的金额打上标签
411         (10, 100]
412       (100, 1000]
413       (100, 1000]
414         (10, 100]
415         (10, 100]
             ...     
201381      (10, 100]
201382    (100, 1000]
201383        (1, 10]
201384      (10, 100]
201385    (100, 1000]
Name: contb_receipt_amt, Length: 694283, dtype: category
Categories (8, interval[int64]): [(0, 1] < (1, 10] < (10, 100] < (100, 1000] < (1000, 10000] < (10000, 100000] < (100000, 1000000] < (1000000, 10000000]]

三、数据聚合及分组

1.透视表(pivot_table)分析党派和职业

通过pivot_table根据党派和职业对数据进行聚合,然后过滤掉总出资不足200万美元的数据

by_occupation = data.pivot_table('contb_receipt_amt',index='contbr_occupation',columns='party',aggfunc='sum')
over_2mm = by_occupation[by_occupation.sum(1)>2000000]
over_2mm

党派

画出直方图

over_2mm.plot(kind = 'bar')

直方图

2.根据职业与雇主信息分组运算

根据职业分组,根据捐赠的金额排序输出最多的七个职业

def get_top_amounts(group,key,n=5):
#传入groupby分组后的对象,返回按照key字段汇总的排序前n的数据
    totals = group.groupby(key)['contb_receipt_amt'].sum()
    return totals.sort_values(ascending=False)[:n]
grouped = data_vs.groupby('cand_nm')
grouped.apply(get_top_amounts,'contbr_occupation',n=7)
cand_nm        contbr_occupation
Obama, Barack  RETIRED              25305316.38
               ATTORNEY             14302461.84
               NOT PROVIDED         13725187.32
               HOMEMAKER             4248875.80
               PHYSICIAN             3735124.94
               CONSULTANT            2459912.71
               PROFESSOR             2165071.08
Romney, Mitt   NOT PROVIDED         11638509.84
               RETIRED              11508473.59
               HOMEMAKER             8147446.22
               ATTORNEY              5372424.02
               PRESIDENT             2491244.89
               CEO                   2324297.03
               EXECUTIVE             2300947.03
Name: contb_receipt_amt, dtype: float64

同理可输出公司的捐赠排名

grouped.apply(get_top_amounts,'contbr_employer',n=10)
cand_nm        contbr_employer   
Obama, Barack  RETIRED               22694558.85
               SELF-EMPLOYED         18626807.16
               NOT PROVIDED          13883494.03
               NOT EMPLOYED           8586308.70
               HOMEMAKER              2605408.54
               STUDENT                 318831.45
               VOLUNTEER               257104.00
               MICROSOFT               215585.36
               SIDLEY AUSTIN LLP       168254.00
               REFUSED                 149516.07
Romney, Mitt   NOT PROVIDED          12321731.24
               RETIRED               11506225.71
               HOMEMAKER              8147196.22
               SELF-EMPLOYED          7414115.22
               STUDENT                 496490.94
               CREDIT SUISSE           281150.00
               MORGAN STANLEY          267266.00
               GOLDMAN SACH & CO.      238250.00
               BARCLAYS CAPITAL        162750.00
               H.I.G. CAPITAL          139500.00
Name: contb_receipt_amt, dtype: float64

综上可以得出支持Obama和Romney的群体。

根据赞助人在不同捐助金额的多少进行分类比较

grouped_bins = data_vs.groupby(['cand_nm',labels])
grouped_bins.size().unstack(0)

捐赠人

每个阶层的捐赠总和

bucket_sums=grouped_bins['contb_receipt_amt'].sum().unstack(0)
bucket_sums

金额

根据金额来画直方图

bucket_sums.plot(kind='bar')

金额直方图

用百分比来显示结果,更加明显

normed_sums = bucket_sums.div(bucket_sums.sum(axis = 1),axis=0)
normed_sums

百分比

normed_sums[:-2].plot(kind='bar',stacked=True)

百分比直方图

按照赞助人姓名分组计数,计算重复赞助次数最多的前20人

data.groupby('contbr_nm')['contbr_nm'].count().sort_values(ascending=False)[:20]
contbr_nm
WILLIAMS, DEBBY          205
BERKE, DAVID MICHAEL     171
SEBAG, DAVID             161
SMITH, ERIK              145
FALLSGRAFF, TOBY         138
SKINNER, DONNA           136
CASPERSON, CAROLINA      132
HARRIS, CLAUDIA W.       132
ROSBERG, MARILYN         115
POTTS, LILLIE            114
DUDLEY, DEBBIE           111
HAUGHEY, NOEL ANTHONY    107
DFHDFH, DFHDFH            96
SHERWIN, GLEN R.          94
MITCHELL, CAITLIN         90
SMITH, CHARLES            88
KARIMIAN, AFSANEH         87
NURU, ISAAC               87
MASTERS, MARGERY          85
BIRMINGHAM, TOM           85
Name: contbr_nm, dtype: int64

四、时间处理

1.str转datetime

使用to_datetime方法解析多种不同的日期表示形式。对标准日期格式(如ISO8601)的解析非常快。我们也可以指定特定的日期解析格式,如pd.to_datetime(series,format='%Y%m%d')

data_vs['time'] = pd.to_datetime(data_vs['contb_receipt_dt'])

2.以时间作为索引

data_vs.set_index('time',inplace = True)
data_vs.head()

时间

3.重采样和频度转换

vs_time = data_vs.groupby('cand_nm').resample('M')['cand_nm'].count()
vs_time.unstack(0)

时间列表

fig, ax1=plt.subplots(figsize=(32,8))
vs_time.unstack(0).plot(kind='area',ax=ax1,alpha=0.6)
plt.show()

时间区域

用面积图把11年4月-12年4月两位总统候选人接受的赞助笔数做个对比可以看出,越临近竞选,大家赞助的热情越高涨,奥巴马在各个时段都占据绝对的优势

]]>
0 /index.php/archives/122/#comments /index.php/feed/archives/122/
谷歌、百度循环多次翻译、语音下载python脚本 /index.php/archives/118/ /index.php/archives/118/ Tue, 18 Aug 2020 23:20:00 +0800 南岛鹋

最近哔哩哔哩出现了很多,翻译类视频,播放量都还普遍不错。但是比较懒,不想动手翻译20次,于是用python参考网上代码写了一个支持多次翻译、语音下载的脚本。

功能介绍

[tabs]
[tab name="主要截图" active="true"]
[album]
[
主要文件
语言翻译设置
脚本运行界面
]
[/album]
[/tab]
[tab name="主要功能"]

  • 支持百度、谷歌翻译、支持百度语音下载
  • 支持自定义翻译流程以及次数
  • 百度翻译API参考视频内容自行申请
    [/tab]

[/tabs]

演示视频

下载地址

[button color="success" icon="glyphicon glyphicon-save" url="https://github.com/ndmiao/word-to-voice/" type=""]点我下载[/button]

]]>
0 /index.php/archives/118/#comments /index.php/feed/archives/118/